Merge "[SettingsProvider] @Readable annotation to restrict access to hidden keys"
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 3ff2546..665deda 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -123,6 +123,20 @@
             new_since: ":android-non-updatable.api.public.latest",
         },
     },
+    dists: [
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/public/api",
+            dest: "android-non-updatable.txt",
+            tag: ".api.txt",
+        },
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/public/api",
+            dest: "android-non-updatable-removed.txt",
+            tag: ".removed-api.txt",
+        },
+    ],
 }
 
 priv_apps =
@@ -162,6 +176,20 @@
             baseline_file: "core/api/system-lint-baseline.txt",
         },
     },
+    dists: [
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/system/api",
+            dest: "android-non-updatable.txt",
+            tag: ".api.txt",
+        },
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/system/api",
+            dest: "android-non-updatable-removed.txt",
+            tag: ".removed-api.txt",
+        },
+    ],
 }
 
 droidstubs {
@@ -178,11 +206,32 @@
             baseline_file: "core/api/test-lint-baseline.txt",
         },
     },
-    dist: {
-        targets: ["sdk", "win_sdk"],
-        dir: "apistubs/android/test/api",
-        dest: "android.txt",
-    },
+    dists: [
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/test/api",
+            dest: "android.txt",
+            tag: ".api.txt",
+        },
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/test/api",
+            dest: "removed.txt",
+            tag: ".removed-api.txt",
+        },
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/test/api",
+            dest: "android-non-updatable.txt",
+            tag: ".api.txt",
+        },
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/test/api",
+            dest: "android-non-updatable-removed.txt",
+            tag: ".removed-api.txt",
+        },
+    ],
 }
 
 droidstubs {
@@ -203,6 +252,20 @@
             new_since: ":android-non-updatable.api.module-lib.latest",
         },
     },
+    dists: [
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/module-lib/api",
+            dest: "android-non-updatable.txt",
+            tag: ".api.txt",
+        },
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/module-lib/api",
+            dest: "android-non-updatable-removed.txt",
+            tag: ".removed-api.txt",
+        },
+    ],
 }
 
 /////////////////////////////////////////////////////////////////////
diff --git a/apex/OWNERS b/apex/OWNERS
index 9760013..bde2bec 100644
--- a/apex/OWNERS
+++ b/apex/OWNERS
@@ -1,7 +1,8 @@
-# Shared module build rule owners
-per-file *.bp=hansson@google.com
-per-file *.bp=jiyong@google.com
+# Mainline modularization team
 
-# This file, and all other OWNERS files
-per-file OWNERS=dariofreni@google.com
-per-file OWNERS=hansson@google.com
+andreionea@google.com
+dariofreni@google.com
+hansson@google.com
+mathewi@google.com
+pedroql@google.com
+satayev@google.com
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index ebb0070..b1394c1 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -22,6 +22,14 @@
     method @NonNull public android.app.appsearch.AppSearchManager.SearchContext.Builder setDatabaseName(@NonNull String);
   }
 
+  public interface AppSearchMigrationHelper {
+    method public void queryAndTransform(@NonNull String, @NonNull android.app.appsearch.AppSearchMigrationHelper.Transformer) throws java.lang.Exception;
+  }
+
+  public static interface AppSearchMigrationHelper.Transformer {
+    method @NonNull public android.app.appsearch.GenericDocument transform(int, int, @NonNull android.app.appsearch.GenericDocument) throws java.lang.Exception;
+  }
+
   public final class AppSearchResult<ValueType> {
     method @Nullable public String getErrorMessage();
     method public int getResultCode();
@@ -99,6 +107,11 @@
     method @NonNull public android.app.appsearch.AppSearchSchema.Int64PropertyConfig.Builder setCardinality(int);
   }
 
+  public static interface AppSearchSchema.Migrator {
+    method public default void onDowngrade(int, int, @NonNull android.app.appsearch.AppSearchMigrationHelper) throws java.lang.Exception;
+    method public default void onUpgrade(int, int, @NonNull android.app.appsearch.AppSearchMigrationHelper) throws java.lang.Exception;
+  }
+
   public abstract static class AppSearchSchema.PropertyConfig {
     method public int getCardinality();
     method public int getDataType();
@@ -135,7 +148,7 @@
     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>>);
+    method public void setSchema(@NonNull android.app.appsearch.SetSchemaRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.SetSchemaResponse>>);
   }
 
   public interface BatchResultCallback<KeyType, ValueType> {
@@ -322,6 +335,7 @@
   }
 
   public final class SetSchemaRequest {
+    method @NonNull public java.util.Map<java.lang.String,android.app.appsearch.AppSearchSchema.Migrator> getMigrators();
     method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> getSchemas();
     method @NonNull public java.util.Set<java.lang.String> getSchemasNotVisibleToSystemUi();
     method @NonNull public java.util.Map<java.lang.String,java.util.Set<android.app.appsearch.PackageIdentifier>> getSchemasVisibleToPackages();
@@ -334,10 +348,26 @@
     method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchema(@NonNull java.util.Collection<android.app.appsearch.AppSearchSchema>);
     method @NonNull public android.app.appsearch.SetSchemaRequest build();
     method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setForceOverride(boolean);
+    method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setMigrator(@NonNull String, @NonNull android.app.appsearch.AppSearchSchema.Migrator);
     method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForPackage(@NonNull String, boolean, @NonNull android.app.appsearch.PackageIdentifier);
     method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForSystemUi(@NonNull String, boolean);
   }
 
+  public class SetSchemaResponse {
+    method @NonNull public java.util.Set<java.lang.String> getDeletedTypes();
+    method @NonNull public java.util.Set<java.lang.String> getIncompatibleTypes();
+    method @NonNull public java.util.Set<java.lang.String> getMigratedTypes();
+    method @NonNull public java.util.List<android.app.appsearch.SetSchemaResponse.MigrationFailure> getMigrationFailures();
+    method public boolean isSuccess();
+  }
+
+  public static class SetSchemaResponse.MigrationFailure {
+    method @NonNull public android.app.appsearch.AppSearchResult<java.lang.Void> getAppSearchResult();
+    method @NonNull public String getNamespace();
+    method @NonNull public String getSchemaType();
+    method @NonNull public String getUri();
+  }
+
 }
 
 package android.app.appsearch.exceptions {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index c4c123c..670f8b9 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -162,7 +162,7 @@
     public void setSchema(
             @NonNull SetSchemaRequest request,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull Consumer<AppSearchResult<Void>> callback) {
+            @NonNull Consumer<AppSearchResult<SetSchemaResponse>> callback) {
         Objects.requireNonNull(request);
         Objects.requireNonNull(executor);
         Objects.requireNonNull(callback);
@@ -192,7 +192,18 @@
                     mUserId,
                     new IAppSearchResultCallback.Stub() {
                         public void onResult(AppSearchResult result) {
-                            executor.execute(() -> callback.accept(result));
+                            executor.execute(() -> {
+                                if (result.isSuccess()) {
+                                    callback.accept(
+                                            // TODO(b/151178558) implement Migration in platform.
+                                            AppSearchResult.newSuccessfulResult(
+                                                    new SetSchemaResponse.Builder().setResultCode(
+                                                            result.getResultCode())
+                                                            .build()));
+                                } else {
+                                    callback.accept(result);
+                                }
+                            });
                         }
                     });
             mIsMutated = true;
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchMigrationHelper.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchMigrationHelper.java
new file mode 100644
index 0000000..37943fc
--- /dev/null
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchMigrationHelper.java
@@ -0,0 +1,65 @@
+/*
+ * 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 android.annotation.SuppressLint;
+
+/**
+ * The helper class for {@link AppSearchSchema} migration.
+ *
+ * <p>It will query and migrate {@link GenericDocument} in given type to a new version.
+ */
+public interface AppSearchMigrationHelper {
+
+    /**
+     * Queries all documents that need to be migrated to the different version, and transform
+     * documents to that version by passing them to the provided {@link Transformer}.
+     *
+     * @param schemaType The schema that need be updated and migrated {@link GenericDocument} under
+     *     this type.
+     * @param transformer The {@link Transformer} that will upgrade or downgrade a {@link
+     *     GenericDocument} to new version.
+     * @see Transformer#transform
+     */
+    // Rethrow the Generic Exception thrown from the Transformer.
+    @SuppressLint("GenericException")
+    void queryAndTransform(@NonNull String schemaType, @NonNull Transformer transformer)
+            throws Exception;
+
+    /** The class to migrate {@link GenericDocument} between different version. */
+    interface Transformer {
+
+        /**
+         * Translates a {@link GenericDocument} from a version to a different version.
+         *
+         * <p>If the uri, schema type or namespace is changed via the transform, it will apply to
+         * the new {@link GenericDocument}.
+         *
+         * @param currentVersion The current version of the document's schema.
+         * @param finalVersion The final version that documents need to be migrated to.
+         * @param document The {@link GenericDocument} need to be translated to new version.
+         * @return A {@link GenericDocument} in new version.
+         */
+        @NonNull
+        // This method will be overridden by users, allow them to throw any customer Exceptions.
+        @SuppressLint("GenericException")
+        GenericDocument transform(
+                int currentVersion, int finalVersion, @NonNull GenericDocument document)
+                throws Exception;
+    }
+}
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 7f5d202..2e00ff2 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
@@ -164,10 +164,10 @@
          * 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.
+         * currently stored in AppSearch will result in AppSearch calling the {@link 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
@@ -182,8 +182,9 @@
          * @throws IllegalStateException if the version is negative or the builder has already been
          *     used.
          * @see AppSearchSession#setSchema
+         * @see AppSearchSchema.Migrator
+         * @see SetSchemaRequest.Builder#setMigrator
          */
-        // 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");
@@ -859,4 +860,43 @@
             }
         }
     }
+
+    /**
+     * A migrator class to translate {@link GenericDocument} from different version of {@link
+     * AppSearchSchema}
+     */
+    public interface Migrator {
+
+        /**
+         * Migrates {@link GenericDocument} to a newer version of {@link AppSearchSchema}.
+         *
+         * <p>This methods will be invoked only if the {@link SetSchemaRequest} is setting a higher
+         * version number than the current {@link AppSearchSchema} saved in AppSearch.
+         *
+         * @param currentVersion The current version of the document's schema.
+         * @param targetVersion The final version that documents need to be migrated to.
+         * @param helper The helper class could help to query all documents need to be migrated.
+         */
+        // This method will be overridden by users, allow them to throw any customer Exceptions.
+        @SuppressLint("GenericException")
+        default void onUpgrade(
+                int currentVersion, int targetVersion, @NonNull AppSearchMigrationHelper helper)
+                throws Exception {}
+
+        /**
+         * Migrates {@link GenericDocument} to an older version of {@link AppSearchSchema}.
+         *
+         * <p>The methods will be invoked only if the {@link SetSchemaRequest} is setting a higher
+         * version number than the current {@link AppSearchSchema} saved in AppSearch.
+         *
+         * @param currentVersion The current version of the document's schema.
+         * @param targetVersion The final version that documents need to be migrated to.
+         * @param helper The helper class could help to query all documents need to be migrated.
+         */
+        // This method will be overridden by users, allow them to throw any customer Exceptions.
+        @SuppressLint("GenericException")
+        default void onDowngrade(
+                int currentVersion, int targetVersion, @NonNull AppSearchMigrationHelper helper)
+                throws Exception {}
+    }
 }
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
index e9c4cb4..1486df3 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
@@ -38,16 +38,19 @@
     private final Set<AppSearchSchema> mSchemas;
     private final Set<String> mSchemasNotVisibleToSystemUi;
     private final Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages;
+    private final Map<String, AppSearchSchema.Migrator> mMigrators;
     private final boolean mForceOverride;
 
     SetSchemaRequest(
             @NonNull Set<AppSearchSchema> schemas,
             @NonNull Set<String> schemasNotVisibleToSystemUi,
             @NonNull Map<String, Set<PackageIdentifier>> schemasVisibleToPackages,
+            @NonNull Map<String, AppSearchSchema.Migrator> migrators,
             boolean forceOverride) {
         mSchemas = Preconditions.checkNotNull(schemas);
         mSchemasNotVisibleToSystemUi = Preconditions.checkNotNull(schemasNotVisibleToSystemUi);
         mSchemasVisibleToPackages = Preconditions.checkNotNull(schemasVisibleToPackages);
+        mMigrators = Preconditions.checkNotNull(migrators);
         mForceOverride = forceOverride;
     }
 
@@ -81,6 +84,12 @@
         return copy;
     }
 
+    /** Returns the map of {@link android.app.appsearch.AppSearchSchema.Migrator}. */
+    @NonNull
+    public Map<String, AppSearchSchema.Migrator> getMigrators() {
+        return Collections.unmodifiableMap(mMigrators);
+    }
+
     /**
      * Returns a mapping of schema types to the set of packages that have access to that schema
      * type. Each package is represented by a {@link PackageIdentifier}. name and byte[]
@@ -107,6 +116,7 @@
         private final Set<String> mSchemasNotVisibleToSystemUi = new ArraySet<>();
         private final Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages =
                 new ArrayMap<>();
+        private final Map<String, AppSearchSchema.Migrator> mMigrators = new ArrayMap<>();
         private boolean mForceOverride = false;
         private boolean mBuilt = false;
 
@@ -197,6 +207,23 @@
         }
 
         /**
+         * Sets the {@link android.app.appsearch.AppSearchSchema.Migrator}.
+         *
+         * @param schemaType The schema type to set migrator on.
+         * @param migrator The migrator translate a document from it's old version to a new
+         *     incompatible version.
+         */
+        @NonNull
+        @SuppressLint("MissingGetterMatchingBuilder") // Getter return plural objects.
+        public Builder setMigrator(
+                @NonNull String schemaType, @NonNull AppSearchSchema.Migrator migrator) {
+            Preconditions.checkNotNull(schemaType);
+            Preconditions.checkNotNull(migrator);
+            mMigrators.put(schemaType, migrator);
+            return this;
+        }
+
+        /**
          * Configures the {@link SetSchemaRequest} to delete any existing documents that don't
          * follow the new schema.
          *
@@ -241,6 +268,7 @@
                     mSchemas,
                     mSchemasNotVisibleToSystemUi,
                     mSchemasVisibleToPackages,
+                    mMigrators,
                     mForceOverride);
         }
     }
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
new file mode 100644
index 0000000..90a6f60
--- /dev/null
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
@@ -0,0 +1,258 @@
+/*
+ * 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 static android.app.appsearch.AppSearchResult.RESULT_OK;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArraySet;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/** The response class of {@link AppSearchSession#setSchema} */
+public class SetSchemaResponse {
+    private final List<MigrationFailure> mMigrationFailures;
+    private final Set<String> mDeletedTypes;
+    private final Set<String> mMigratedTypes;
+    private final Set<String> mIncompatibleTypes;
+    private final @AppSearchResult.ResultCode int mResultCode;
+
+    SetSchemaResponse(
+            @NonNull List<MigrationFailure> migrationFailures,
+            @NonNull Set<String> deletedTypes,
+            @NonNull Set<String> migratedTypes,
+            @NonNull Set<String> incompatibleTypes,
+            @AppSearchResult.ResultCode int resultCode) {
+        mMigrationFailures = Preconditions.checkNotNull(migrationFailures);
+        mDeletedTypes = Preconditions.checkNotNull(deletedTypes);
+        mMigratedTypes = Preconditions.checkNotNull(migratedTypes);
+        mIncompatibleTypes = Preconditions.checkNotNull(incompatibleTypes);
+        mResultCode = resultCode;
+    }
+
+    /**
+     * Returns a {@link List} of all failed {@link MigrationFailure}.
+     *
+     * <p>A {@link MigrationFailure} will be generated if the system trying to save a post-migrated
+     * {@link GenericDocument} but fail.
+     *
+     * <p>{@link MigrationFailure} contains the uri, namespace and schemaType of the post-migrated
+     * {@link GenericDocument} and the error reason. Mostly it will be mismatch the schema it
+     * migrated to.
+     */
+    @NonNull
+    public List<MigrationFailure> getMigrationFailures() {
+        return Collections.unmodifiableList(mMigrationFailures);
+    }
+
+    /**
+     * Returns a {@link Set} of schema type that were deleted by the {@link
+     * AppSearchSession#setSchema} call.
+     */
+    @NonNull
+    public Set<String> getDeletedTypes() {
+        return Collections.unmodifiableSet(mDeletedTypes);
+    }
+
+    /**
+     * Returns a {@link Set} of schema type that were migrated by the {@link
+     * AppSearchSession#setSchema} call.
+     */
+    @NonNull
+    public Set<String> getMigratedTypes() {
+        return Collections.unmodifiableSet(mMigratedTypes);
+    }
+
+    /**
+     * Returns a {@link Set} of schema type whose new definitions set in the {@link
+     * AppSearchSession#setSchema} call were incompatible with the pre-existing schema.
+     *
+     * <p>If a {@link android.app.appsearch.AppSearchSchema.Migrator} is provided for this type and
+     * the migration is success triggered. The type will also appear in {@link #getMigratedTypes()}.
+     *
+     * @see AppSearchSession#setSchema
+     * @see SetSchemaRequest.Builder#setForceOverride
+     */
+    @NonNull
+    public Set<String> getIncompatibleTypes() {
+        return Collections.unmodifiableSet(mIncompatibleTypes);
+    }
+
+    /** Returns {@code true} if all {@link AppSearchSchema}s are successful set to the system. */
+    public boolean isSuccess() {
+        return mResultCode == RESULT_OK;
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return "{\n  Does setSchema success? : "
+                + isSuccess()
+                + "\n  failures: "
+                + mMigrationFailures
+                + "\n}";
+    }
+
+    /**
+     * Builder for {@link SetSchemaResponse} objects.
+     *
+     * @hide
+     */
+    public static class Builder {
+        private final List<MigrationFailure> mMigrationFailures = new ArrayList<>();
+        private final Set<String> mDeletedTypes = new ArraySet<>();
+        private final Set<String> mMigratedTypes = new ArraySet<>();
+        private final Set<String> mIncompatibleTypes = new ArraySet<>();
+        private @AppSearchResult.ResultCode int mResultCode = RESULT_OK;
+        private boolean mBuilt = false;
+
+        /** Adds a {@link MigrationFailure}. */
+        @NonNull
+        public Builder setFailure(
+                @NonNull String schemaType,
+                @NonNull String namespace,
+                @NonNull String uri,
+                @NonNull AppSearchResult<Void> failureResult) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(schemaType);
+            Preconditions.checkNotNull(namespace);
+            Preconditions.checkNotNull(uri);
+            Preconditions.checkNotNull(failureResult);
+            Preconditions.checkState(!failureResult.isSuccess());
+            mMigrationFailures.add(new MigrationFailure(schemaType, namespace, uri, failureResult));
+            return this;
+        }
+
+        /** Adds a {@link MigrationFailure}. */
+        @NonNull
+        public Builder setFailure(
+                @NonNull String schemaType,
+                @NonNull String namespace,
+                @NonNull String uri,
+                @AppSearchResult.ResultCode int resultCode,
+                @Nullable String errorMessage) {
+            mMigrationFailures.add(
+                    new MigrationFailure(
+                            schemaType,
+                            namespace,
+                            uri,
+                            AppSearchResult.newFailedResult(resultCode, errorMessage)));
+            return this;
+        }
+
+        /** Adds deletedTypes to the list of deleted schema types. */
+        @NonNull
+        public Builder addDeletedType(@NonNull Collection<String> deletedTypes) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mDeletedTypes.addAll(Preconditions.checkNotNull(deletedTypes));
+            return this;
+        }
+
+        /** Adds incompatibleTypes to the list of incompatible schema types. */
+        @NonNull
+        public Builder addIncompatibleType(@NonNull Collection<String> incompatibleTypes) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mIncompatibleTypes.addAll(Preconditions.checkNotNull(incompatibleTypes));
+            return this;
+        }
+
+        /** Adds migratedTypes to the list of migrated schema types. */
+        @NonNull
+        public Builder addMigratedType(@NonNull String migratedType) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mMigratedTypes.add(Preconditions.checkNotNull(migratedType));
+            return this;
+        }
+
+        /** Sets the {@link AppSearchResult.ResultCode} of the response. */
+        @NonNull
+        public Builder setResultCode(@AppSearchResult.ResultCode int resultCode) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mResultCode = resultCode;
+            return this;
+        }
+
+        /** Builds a {@link SetSchemaResponse} object. */
+        @NonNull
+        public SetSchemaResponse build() {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mBuilt = true;
+            return new SetSchemaResponse(
+                    mMigrationFailures,
+                    mDeletedTypes,
+                    mMigratedTypes,
+                    mIncompatibleTypes,
+                    mResultCode);
+        }
+    }
+
+    /**
+     * The class represents a post-migrated {@link GenericDocument} that failed to be saved by
+     * {@link AppSearchSession#setSchema}.
+     */
+    public static class MigrationFailure {
+        private final String mSchemaType;
+        private final String mNamespace;
+        private final String mUri;
+        AppSearchResult<Void> mFailureResult;
+
+        MigrationFailure(
+                @NonNull String schemaType,
+                @NonNull String namespace,
+                @NonNull String uri,
+                @NonNull AppSearchResult<Void> result) {
+            mSchemaType = schemaType;
+            mNamespace = namespace;
+            mUri = uri;
+            mFailureResult = result;
+        }
+
+        /** Returns the schema type of the {@link GenericDocument} that fails to be migrated. */
+        @NonNull
+        public String getSchemaType() {
+            return mSchemaType;
+        }
+
+        /** Returns the namespace of the {@link GenericDocument} that fails to be migrated. */
+        @NonNull
+        public String getNamespace() {
+            return mNamespace;
+        }
+
+        /** Returns the uri of the {@link GenericDocument} that fails to be migrated. */
+        @NonNull
+        public String getUri() {
+            return mUri;
+        }
+
+        /**
+         * Returns the {@link AppSearchResult} that indicates why the post-migrated {@link
+         * GenericDocument} fails to be saved.
+         */
+        @NonNull
+        public AppSearchResult<Void> getAppSearchResult() {
+            return mFailureResult;
+        }
+    }
+}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResult.java
new file mode 100644
index 0000000..f04ace6
--- /dev/null
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResult.java
@@ -0,0 +1,125 @@
+/*
+ * 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 android.os.Bundle;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This class represents the results of setSchema().
+ *
+ * @hide
+ */
+public class SetSchemaResult {
+
+    public static final String DELETED_SCHEMA_TYPES_FIELD = "deletedSchemaTypes";
+    public static final String INCOMPATIBLE_SCHEMA_TYPES_FIELD = "incompatibleSchemaTypes";
+    public static final String RESULT_CODE_FIELD = "resultCode";
+    private final List<String> mDeletedSchemaTypes;
+    private final List<String> mIncompatibleSchemaTypes;
+    private final Bundle mBundle;
+
+    SetSchemaResult(@NonNull Bundle bundle) {
+        mBundle = Preconditions.checkNotNull(bundle);
+        mDeletedSchemaTypes =
+                Preconditions.checkNotNull(mBundle.getStringArrayList(DELETED_SCHEMA_TYPES_FIELD));
+        mIncompatibleSchemaTypes =
+                Preconditions.checkNotNull(
+                        mBundle.getStringArrayList(INCOMPATIBLE_SCHEMA_TYPES_FIELD));
+    }
+
+    /** Returns the {@link Bundle} of this class. */
+    @NonNull
+    public Bundle getBundle() {
+        return mBundle;
+    }
+
+    /** returns all deleted schema types in this setSchema call. */
+    @NonNull
+    public List<String> getDeletedSchemaTypes() {
+        return Collections.unmodifiableList(mDeletedSchemaTypes);
+    }
+
+    /** returns all incompatible schema types in this setSchema call. */
+    @NonNull
+    public List<String> getIncompatibleSchemaTypes() {
+        return Collections.unmodifiableList(mIncompatibleSchemaTypes);
+    }
+
+    /**
+     * returns the {@link android.app.appsearch.AppSearchResult.ResultCode} of the {@link
+     * AppSearchSession#setSchema} call.
+     */
+    public int getResultCode() {
+        return mBundle.getInt(RESULT_CODE_FIELD);
+    }
+
+    /** Builder for {@link SetSchemaResult} objects. */
+    public static final class Builder {
+        private final ArrayList<String> mDeletedSchemaTypes = new ArrayList<>();
+        private final ArrayList<String> mIncompatibleSchemaTypes = new ArrayList<>();
+        @AppSearchResult.ResultCode private int mResultCode;
+        private boolean mBuilt = false;
+
+        /** Adds a deletedSchemaTypes to the {@link SetSchemaResult}. */
+        @NonNull
+        public Builder addDeletedSchemaType(@NonNull String deletedSchemaType) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mDeletedSchemaTypes.add(Preconditions.checkNotNull(deletedSchemaType));
+            return this;
+        }
+
+        /** Adds a incompatible SchemaTypes to the {@link SetSchemaResult}. */
+        @NonNull
+        public Builder addIncompatibleSchemaType(@NonNull String incompatibleSchemaTypes) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mIncompatibleSchemaTypes.add(Preconditions.checkNotNull(incompatibleSchemaTypes));
+            return this;
+        }
+
+        /**
+         * Sets the {@link android.app.appsearch.AppSearchResult.ResultCode} of the {@link
+         * AppSearchSession#setSchema} call to the {@link SetSchemaResult}
+         */
+        @NonNull
+        public Builder setResultCode(@AppSearchResult.ResultCode int resultCode) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mResultCode = resultCode;
+            return this;
+        }
+
+        /** Builds a {@link SetSchemaResult}. */
+        @NonNull
+        public SetSchemaResult build() {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Bundle bundle = new Bundle();
+            bundle.putStringArrayList(
+                    SetSchemaResult.DELETED_SCHEMA_TYPES_FIELD, mDeletedSchemaTypes);
+            bundle.putStringArrayList(
+                    SetSchemaResult.INCOMPATIBLE_SCHEMA_TYPES_FIELD, mIncompatibleSchemaTypes);
+            bundle.putInt(RESULT_CODE_FIELD, mResultCode);
+            mBuilt = true;
+            return new SetSchemaResult(bundle);
+        }
+    }
+}
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 ec41353..ed55f00 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -54,6 +54,7 @@
 public class AppSearchManagerService extends SystemService {
     private static final String TAG = "AppSearchManagerService";
     private PackageManagerInternal mPackageManagerInternal;
+    private ImplInstanceManager mImplInstanceManager;
 
     public AppSearchManagerService(Context context) {
         super(context);
@@ -63,6 +64,7 @@
     public void onStart() {
         publishBinderService(Context.APP_SEARCH_SERVICE, new Stub());
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+        mImplInstanceManager = new ImplInstanceManager(getContext());
     }
 
     private class Stub extends IAppSearchManager.Stub {
@@ -100,7 +102,7 @@
                     }
                     schemasPackageAccessible.put(entry.getKey(), packageIdentifiers);
                 }
-                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
+                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
                 impl.setSchema(
                         packageName,
                         databaseName,
@@ -131,7 +133,7 @@
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
+                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
                 List<AppSearchSchema> schemas = impl.getSchema(packageName, databaseName);
                 List<Bundle> schemaBundles = new ArrayList<>(schemas.size());
                 for (int i = 0; i < schemas.size(); i++) {
@@ -164,7 +166,7 @@
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchBatchResult.Builder<String, Void> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
-                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
+                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
                 for (int i = 0; i < documentBundles.size(); i++) {
                     GenericDocument document = new GenericDocument(documentBundles.get(i));
                     try {
@@ -205,7 +207,7 @@
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
-                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
+                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
                 for (int i = 0; i < uris.size(); i++) {
                     String uri = uris.get(i);
                     try {
@@ -243,7 +245,7 @@
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
+                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
                 SearchResultPage searchResultPage =
                         impl.query(
                                 packageName,
@@ -276,7 +278,7 @@
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
+                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
                 SearchResultPage searchResultPage = impl.globalQuery(
                         queryExpression,
                         new SearchSpec(searchSpecBundle),
@@ -304,7 +306,7 @@
             // TODO(b/162450968) check nextPageToken is being advanced by the same uid as originally
             // opened it
             try {
-                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
+                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
                 SearchResultPage searchResultPage = impl.getNextPage(nextPageToken);
                 invokeCallbackOnResult(
                         callback,
@@ -322,7 +324,7 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
-                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
+                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
                 impl.invalidateNextPageToken(nextPageToken);
             } catch (Throwable t) {
                 Log.e(TAG, "Unable to invalidate the query page token", t);
@@ -348,7 +350,7 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
-                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
+                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
                 impl.reportUsage(
                         packageName,
                         databaseName,
@@ -383,7 +385,7 @@
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchBatchResult.Builder<String, Void> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
-                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
+                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
                 for (int i = 0; i < uris.size(); i++) {
                     String uri = uris.get(i);
                     try {
@@ -419,7 +421,7 @@
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
+                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
                 impl.removeByQuery(
                         packageName,
                         databaseName,
@@ -439,7 +441,7 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
-                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
+                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
                 impl.persistToDisk();
             } catch (Throwable t) {
                 Log.e(TAG, "Unable to persist the data to disk", t);
@@ -455,7 +457,7 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
-                ImplInstanceManager.getInstance(getContext(), callingUserId);
+                mImplInstanceManager.getInstance(callingUserId);
                 invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
             } catch (Throwable t) {
                 invokeCallbackOnError(callback, t);
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 cd90f85..fe3c2e1 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -16,14 +16,19 @@
 
 package com.android.server.appsearch;
 
+import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY;
+
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.appsearch.exceptions.AppSearchException;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.Environment;
+import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.util.SparseArray;
 
+import com.android.internal.R;
 import com.android.server.appsearch.external.localstorage.AppSearchImpl;
 
 import java.io.File;
@@ -38,7 +43,13 @@
 
     private static final SparseArray<AppSearchImpl> sInstances = new SparseArray<>();
 
-    private ImplInstanceManager() {}
+    private final Context mContext;
+    private final String mGlobalQuerierPackage;
+
+    public ImplInstanceManager(@NonNull Context context) {
+        mContext = context;
+        mGlobalQuerierPackage = getGlobalAppSearchDataQuerierPackageName(mContext);
+    }
 
     /**
      * Gets an instance of AppSearchImpl for the given user.
@@ -46,19 +57,18 @@
      * <p>If no AppSearchImpl instance exists for this user, Icing will be initialized and one will
      * be created.
      *
-     * @param context The Android context
      * @param userId The multi-user userId of the device user calling AppSearch
      * @return An initialized {@link AppSearchImpl} for this user
      */
     @NonNull
-    public static AppSearchImpl getInstance(@NonNull Context context, @UserIdInt int userId)
+    public AppSearchImpl getInstance(@UserIdInt int userId)
             throws AppSearchException {
         AppSearchImpl instance = sInstances.get(userId);
         if (instance == null) {
             synchronized (ImplInstanceManager.class) {
                 instance = sInstances.get(userId);
                 if (instance == null) {
-                    instance = createImpl(context, userId);
+                    instance = createImpl(userId);
                     sInstances.put(userId, instance);
                 }
             }
@@ -66,16 +76,41 @@
         return instance;
     }
 
-    private static AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId)
+    private AppSearchImpl createImpl(@UserIdInt int userId)
             throws AppSearchException {
-        File appSearchDir = getAppSearchDir(context, userId);
-        return AppSearchImpl.create(appSearchDir, context, /*globalQuerierPackage=*/"");
+        File appSearchDir = getAppSearchDir(mContext, userId);
+        return AppSearchImpl.create(
+                appSearchDir, mContext, userId, mGlobalQuerierPackage);
     }
 
     private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) {
         // See com.android.internal.app.ChooserActivity::getPinnedSharedPrefs
-        File userCeDir = Environment.getDataUserCePackageDirectory(
-                StorageManager.UUID_PRIVATE_INTERNAL, userId, context.getPackageName());
+        File userCeDir =
+                Environment.getDataUserCePackageDirectory(
+                        StorageManager.UUID_PRIVATE_INTERNAL, userId, context.getPackageName());
         return new File(userCeDir, APP_SEARCH_DIR);
     }
+
+    /**
+     * Returns the global querier package if it's a system package. Otherwise, empty string.
+     *
+     * @param context Context of the system service.
+     */
+    private static String getGlobalAppSearchDataQuerierPackageName(Context context) {
+        String globalAppSearchDataQuerierPackage =
+                context.getString(R.string.config_globalAppSearchDataQuerierPackage);
+        try {
+            if (context.getPackageManager()
+                            .getPackageInfoAsUser(
+                                    globalAppSearchDataQuerierPackage,
+                                    MATCH_FACTORY_ONLY,
+                                    UserHandle.USER_SYSTEM)
+                    == null) {
+                return "";
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            return "";
+        }
+        return globalAppSearchDataQuerierPackage;
+    }
 }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
similarity index 78%
rename from apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java
rename to apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
index 24d64a0..64dc972 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,9 +14,13 @@
  * limitations under the License.
  */
 
+// TODO(b/169883602): This is purposely a different package from the path so that it can access
+// AppSearchImpl's methods without having to make them public. This should be moved into a proper
+// package once AppSearchImpl-VisibilityStore's dependencies are refactored.
 package com.android.server.appsearch.external.localstorage;
 
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.app.appsearch.AppSearchResult;
 import android.app.appsearch.AppSearchSchema;
 import android.app.appsearch.GenericDocument;
@@ -25,10 +29,10 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Process;
+import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-
-import androidx.annotation.RequiresApi;
+import android.util.Log;
 
 import com.android.internal.util.Preconditions;
 
@@ -56,8 +60,16 @@
  *
  * <p>NOTE: This class holds an instance of AppSearchImpl and AppSearchImpl holds an instance of
  * this class. Take care to not cause any circular dependencies.
+ *
+ * @hide
  */
-class VisibilityStore {
+public class VisibilityStore {
+
+    private static final String TAG = "AppSearchVisibilityStore";
+
+    /** No-op user id that won't have any visibility settings. */
+    public static final int NO_OP_USER_ID = -1;
+
     /** Schema type for documents that hold AppSearch's metadata, e.g. visibility settings */
     private static final String VISIBILITY_TYPE = "VisibilityType";
 
@@ -124,8 +136,8 @@
                     .build();
 
     /**
-     * These cannot have any of the special characters used by AppSearchImpl (e.g. {@link
-     * AppSearchImpl#PACKAGE_DELIMITER} or {@link AppSearchImpl#DATABASE_DELIMITER}.
+     * These cannot have any of the special characters used by AppSearchImpl (e.g. {@code
+     * AppSearchImpl#PACKAGE_DELIMITER} or {@code AppSearchImpl#DATABASE_DELIMITER}.
      */
     static final String PACKAGE_NAME = "VS#Pkg";
 
@@ -149,11 +161,15 @@
 
     private final AppSearchImpl mAppSearchImpl;
 
+    // Context of the system service.
     private final Context mContext;
 
+    // User ID of the caller who we're checking visibility settings for.
+    private final int mUserId;
+
     // UID of the package that has platform-query privileges, i.e. can query for all
     // platform-surfaceable content.
-    private int mGlobalQuerierPackageUid;
+    private int mGlobalQuerierUid;
 
     /**
      * Maps prefixes to the set of schemas that are platform-hidden within that prefix. All schemas
@@ -180,20 +196,15 @@
      *
      * @param appSearchImpl AppSearchImpl instance
      */
-    VisibilityStore(
+    public VisibilityStore(
             @NonNull AppSearchImpl appSearchImpl,
             @NonNull Context context,
+            @UserIdInt int userId,
             @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);
-        }
+        mUserId = userId;
+        mGlobalQuerierUid = getGlobalQuerierUid(globalQuerierPackage);
     }
 
     /**
@@ -357,7 +368,9 @@
         Preconditions.checkNotNull(prefix);
         Preconditions.checkNotNull(prefixedSchema);
 
-        if (callerUid == mGlobalQuerierPackageUid
+        // We compare appIds here rather than direct uids because the package's uid may change based
+        // on the user that's running.
+        if (UserHandle.isSameApp(mGlobalQuerierUid, callerUid)
                 && isSchemaPlatformSurfaceable(prefix, prefixedSchema)) {
             return true;
         }
@@ -414,26 +427,21 @@
             return false;
         }
 
-        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) {
+            // TODO(b/169883602): Consider caching the UIDs of packages. Looking this up in the
+            // package manager could be costly. We would also need to update the cache on
+            // package-removals.
+            if (getPackageUidAsUser(packageIdentifier.getPackageName()) != callerUid) {
                 continue;
             }
 
             // Check that the package also has the matching certificate
-            if (Api28Impl.hasSigningCertificate(
-                    mContext,
-                    packageIdentifier.getPackageName(),
-                    packageIdentifier.getSha256Certificate())) {
+            if (mContext.getPackageManager()
+                    .hasSigningCertificate(
+                            packageIdentifier.getPackageName(),
+                            packageIdentifier.getSha256Certificate(),
+                            PackageManager.CERT_INPUT_SHA256)) {
                 // The caller has the right package name and right certificate!
                 return true;
             }
@@ -448,7 +456,7 @@
      *
      * <p>{@link #initialize()} must be called after this.
      */
-    void handleReset() {
+    public void handleReset() {
         mNotPlatformSurfaceableMap.clear();
         mPackageAccessibleMap.clear();
     }
@@ -464,83 +472,40 @@
     }
 
     /**
-     * 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.
+     * 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.
      */
-    @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;
+    private int getGlobalQuerierUid(@NonNull String globalQuerierPackage) {
+        try {
+            int flags =
+                    PackageManager.MATCH_DISABLED_COMPONENTS
+                            | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                            | PackageManager.MATCH_SYSTEM_ONLY;
+            // It doesn't matter that we're using the caller's userId here. We'll eventually check
+            // that the two uids in question belong to the same appId.
+            return mContext.getPackageManager()
+                    .getPackageUidAsUser(globalQuerierPackage, flags, mUserId);
+        } catch (PackageManager.NameNotFoundException e) {
+            // Global querier doesn't exist.
+            Log.i(
+                    TAG,
+                    "AppSearch global querier package not found on device:  '"
+                            + globalQuerierPackage
+                            + "'");
         }
-
-        /**
-         * 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;
-        }
+        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.
+     * Finds the UID of the {@code packageName}. Returns {@link Process#INVALID_UID} if unable to
+     * find the UID.
      */
-    @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);
+    private int getPackageUidAsUser(@NonNull String packageName) {
+        try {
+            return mContext.getPackageManager().getPackageUidAsUser(packageName, mUserId);
+        } catch (PackageManager.NameNotFoundException e) {
+            // Package doesn't exist, continue
         }
+        return Process.INVALID_UID;
     }
 }
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 1665b1c..592b8b9 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
@@ -25,6 +25,7 @@
 import android.app.appsearch.PackageIdentifier;
 import android.app.appsearch.SearchResultPage;
 import android.app.appsearch.SearchSpec;
+import android.app.appsearch.SetSchemaResult;
 import android.app.appsearch.exceptions.AppSearchException;
 import android.content.Context;
 import android.os.Bundle;
@@ -36,9 +37,11 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter;
+import com.android.server.appsearch.external.localstorage.converter.ResultCodeToProtoConverter;
 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.SetSchemaResultToProtoConverter;
 import com.android.server.appsearch.external.localstorage.converter.TypePropertyPathToProtoConverter;
 
 import com.google.android.icing.IcingSearchEngine;
@@ -160,18 +163,25 @@
      */
     @NonNull
     public static AppSearchImpl create(
-            @NonNull File icingDir, @NonNull Context context, @NonNull String globalQuerierPackage)
+            @NonNull File icingDir,
+            @NonNull Context context,
+            int userId,
+            @NonNull String globalQuerierPackage)
             throws AppSearchException {
         Preconditions.checkNotNull(icingDir);
         Preconditions.checkNotNull(context);
         Preconditions.checkNotNull(globalQuerierPackage);
-        AppSearchImpl appSearchImpl = new AppSearchImpl(icingDir, context, globalQuerierPackage);
+        AppSearchImpl appSearchImpl =
+                new AppSearchImpl(icingDir, context, userId, globalQuerierPackage);
         appSearchImpl.initializeVisibilityStore();
         return appSearchImpl;
     }
 
     private AppSearchImpl(
-            @NonNull File icingDir, @NonNull Context context, @NonNull String globalQuerierPackage)
+            @NonNull File icingDir,
+            @NonNull Context context,
+            int userId,
+            @NonNull String globalQuerierPackage)
             throws AppSearchException {
         mReadWriteLock.writeLock().lock();
 
@@ -184,7 +194,9 @@
                             .build();
             mIcingSearchEngineLocked = new IcingSearchEngine(options);
 
-            mVisibilityStoreLocked = new VisibilityStore(this, context, globalQuerierPackage);
+            mVisibilityStoreLocked =
+                    new VisibilityStore(
+                            this, context, userId, globalQuerierPackage);
 
             InitializeResultProto initializeResultProto = mIcingSearchEngineLocked.initialize();
             SchemaProto schemaProto;
@@ -250,7 +262,8 @@
      *     which do not comply with the new schema will be deleted.
      * @throws AppSearchException on IcingSearchEngine error.
      */
-    public void setSchema(
+    @NonNull
+    public SetSchemaResult setSchema(
             @NonNull String packageName,
             @NonNull String databaseName,
             @NonNull List<AppSearchSchema> schemas,
@@ -264,8 +277,9 @@
 
             SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder();
             for (int i = 0; i < schemas.size(); i++) {
+                AppSearchSchema schema = schemas.get(i);
                 SchemaTypeConfigProto schemaTypeProto =
-                        SchemaToProtoConverter.toSchemaTypeConfigProto(schemas.get(i));
+                        SchemaToProtoConverter.toSchemaTypeConfigProto(schema);
                 newSchemaBuilder.addTypes(schemaTypeProto);
             }
 
@@ -284,16 +298,10 @@
             try {
                 checkSuccess(setSchemaResultProto.getStatus());
             } catch (AppSearchException e) {
-                // Improve the error message by merging in information about incompatible types.
                 if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0
                         || setSchemaResultProto.getIncompatibleSchemaTypesCount() > 0) {
-                    String newMessage =
-                            e.getMessage()
-                                    + "\n  Deleted types: "
-                                    + setSchemaResultProto.getDeletedSchemaTypesList()
-                                    + "\n  Incompatible types: "
-                                    + setSchemaResultProto.getIncompatibleSchemaTypesList();
-                    throw new AppSearchException(e.getResultCode(), newMessage, e.getCause());
+                    return SetSchemaResultToProtoConverter.toSetSchemaResult(
+                            setSchemaResultProto, prefix);
                 } else {
                     throw e;
                 }
@@ -330,6 +338,7 @@
                 // incompatible schemas.
                 checkForOptimizeLocked(/* force= */ true);
             }
+            return SetSchemaResultToProtoConverter.toSetSchemaResult(setSchemaResultProto, prefix);
         } finally {
             mReadWriteLock.writeLock().unlock();
         }
@@ -787,6 +796,8 @@
      * <p>If the app crashes before a call to PersistToDisk(), Icing would trigger a costly recovery
      * process in next initialization. After that, Icing would still be able to recover all written
      * data.
+     *
+     * @throws AppSearchException on any error that AppSearch persist data to disk.
      */
     public void persistToDisk() throws AppSearchException {
         PersistToDiskResultProto persistToDiskResultProto =
@@ -1350,6 +1361,7 @@
     }
 
     @GuardedBy("mReadWriteLock")
+    @NonNull
     @VisibleForTesting
     VisibilityStore getVisibilityStoreLocked() {
         return mVisibilityStoreLocked;
@@ -1364,28 +1376,8 @@
      * @return AppSearchException with the parallel error code.
      */
     private static AppSearchException statusProtoToAppSearchException(StatusProto statusProto) {
-        switch (statusProto.getCode()) {
-            case INVALID_ARGUMENT:
-                return new AppSearchException(
-                        AppSearchResult.RESULT_INVALID_ARGUMENT, statusProto.getMessage());
-            case NOT_FOUND:
-                return new AppSearchException(
-                        AppSearchResult.RESULT_NOT_FOUND, statusProto.getMessage());
-            case FAILED_PRECONDITION:
-                // Fallthrough
-            case ABORTED:
-                // Fallthrough
-            case INTERNAL:
-                return new AppSearchException(
-                        AppSearchResult.RESULT_INTERNAL_ERROR, statusProto.getMessage());
-            case OUT_OF_SPACE:
-                return new AppSearchException(
-                        AppSearchResult.RESULT_OUT_OF_SPACE, statusProto.getMessage());
-            default:
-                // Some unknown/unsupported error
-                return new AppSearchException(
-                        AppSearchResult.RESULT_UNKNOWN_ERROR,
-                        "Unknown IcingSearchEngine status code: " + statusProto.getCode());
-        }
+        return new AppSearchException(
+                ResultCodeToProtoConverter.toResultCode(statusProto.getCode()),
+                statusProto.getMessage());
     }
 }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/ResultCodeToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/ResultCodeToProtoConverter.java
new file mode 100644
index 0000000..e340de0
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/ResultCodeToProtoConverter.java
@@ -0,0 +1,62 @@
+/*
+ * 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 android.app.appsearch.AppSearchResult;
+import android.util.Log;
+
+import com.google.android.icing.proto.StatusProto;
+
+/**
+ * Translates an {@link StatusProto.Code} into a {@link AppSearchResult.ResultCode}
+ *
+ * @hide
+ */
+public final class ResultCodeToProtoConverter {
+
+    private static final String TAG = "AppSearchResultCodeToPr";
+
+    private ResultCodeToProtoConverter() {}
+
+    /** Converts an {@link StatusProto.Code} into a {@link AppSearchResult.ResultCode}. */
+    public static @AppSearchResult.ResultCode int toResultCode(
+            @NonNull StatusProto.Code statusCode) {
+        switch (statusCode) {
+            case OK:
+                return AppSearchResult.RESULT_OK;
+            case OUT_OF_SPACE:
+                return AppSearchResult.RESULT_OUT_OF_SPACE;
+            case INTERNAL:
+                return AppSearchResult.RESULT_INTERNAL_ERROR;
+            case UNKNOWN:
+                return AppSearchResult.RESULT_UNKNOWN_ERROR;
+            case NOT_FOUND:
+                return AppSearchResult.RESULT_NOT_FOUND;
+            case INVALID_ARGUMENT:
+                return AppSearchResult.RESULT_INVALID_ARGUMENT;
+            default:
+                // Some unknown/unsupported error
+                Log.e(
+                        TAG,
+                        "Cannot convert IcingSearchEngine status code: "
+                                + statusCode
+                                + " to AppSearchResultCode.");
+                return AppSearchResult.RESULT_INTERNAL_ERROR;
+        }
+    }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResultToProtoConverter.java
new file mode 100644
index 0000000..e1e7d46
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResultToProtoConverter.java
@@ -0,0 +1,64 @@
+/*
+ * 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 android.app.appsearch.SetSchemaResult;
+
+import com.android.internal.util.Preconditions;
+
+import com.google.android.icing.proto.SetSchemaResultProto;
+
+/**
+ * Translates a {@link SetSchemaResultProto} into {@link SetSchemaResult}.
+ *
+ * @hide
+ */
+public class SetSchemaResultToProtoConverter {
+
+    private SetSchemaResultToProtoConverter() {}
+
+    /**
+     * Translate a {@link SetSchemaResultProto} into {@link SetSchemaResult}.
+     *
+     * @param proto The {@link SetSchemaResultProto} containing results.
+     * @param prefix The prefix need to removed from schemaTypes
+     * @return {@link SetSchemaResult} of results.
+     */
+    @NonNull
+    public static SetSchemaResult toSetSchemaResult(
+            @NonNull SetSchemaResultProto proto, @NonNull String prefix) {
+        Preconditions.checkNotNull(proto);
+        Preconditions.checkNotNull(prefix);
+        SetSchemaResult.Builder builder =
+                new SetSchemaResult.Builder()
+                        .setResultCode(
+                                ResultCodeToProtoConverter.toResultCode(
+                                        proto.getStatus().getCode()));
+
+        for (int i = 0; i < proto.getDeletedSchemaTypesCount(); i++) {
+            builder.addDeletedSchemaType(proto.getDeletedSchemaTypes(i).substring(prefix.length()));
+        }
+
+        for (int i = 0; i < proto.getIncompatibleSchemaTypesCount(); i++) {
+            builder.addIncompatibleSchemaType(
+                    proto.getIncompatibleSchemaTypes(i).substring(prefix.length()));
+        }
+
+        return builder.build();
+    }
+}
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index 5ab3450..2774181 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-Iff96eae150c7cdd281c9ecb5d93f4ef697e89f1a
+I3fd4c96bf775c2539d744c416cdbf1d3c9544f03
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 c2c1d7c..f8d0d80 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
@@ -33,6 +33,7 @@
 import android.app.appsearch.SearchResultsShim;
 import android.app.appsearch.SearchSpec;
 import android.app.appsearch.SetSchemaRequest;
+import android.app.appsearch.SetSchemaResponse;
 import android.app.appsearch.exceptions.AppSearchException;
 import android.content.Context;
 
@@ -85,8 +86,8 @@
 
     @Override
     @NonNull
-    public ListenableFuture<Void> setSchema(@NonNull SetSchemaRequest request) {
-        SettableFuture<AppSearchResult<Void>> future = SettableFuture.create();
+    public ListenableFuture<SetSchemaResponse> setSchema(@NonNull SetSchemaRequest request) {
+        SettableFuture<AppSearchResult<SetSchemaResponse>> future = SettableFuture.create();
         mAppSearchSession.setSchema(request, mExecutor, future::set);
         return Futures.transformAsync(future, this::transformResult, mExecutor);
     }
@@ -159,6 +160,16 @@
         mAppSearchSession.close();
     }
 
+    @Override
+    @NonNull
+    public ListenableFuture<Void> maybeFlush() {
+        SettableFuture<AppSearchResult<Void>> future = SettableFuture.create();
+        // The data in platform will be flushed by scheduled task. AppSearchSession won't do
+        // anything extra flush.
+        future.set(AppSearchResult.newSuccessfulResult(null));
+        return Futures.transformAsync(future, this::transformResult, mExecutor);
+    }
+
     private <T> ListenableFuture<T> transformResult(
             @NonNull AppSearchResult<T> result) throws AppSearchException {
         if (!result.isSuccess()) {
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 ff91f59..e8ea6ef 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
@@ -69,12 +69,19 @@
      * {@link AppSearchResult#RESULT_INVALID_SCHEMA} and a message describing the incompatibility.
      * In this case the previously set schema will remain active.
      *
-     * <p>If you need to make non-backwards-compatible changes as described above, you can set the
-     * {@link SetSchemaRequest.Builder#setForceOverride} method to {@code true}. In this case,
-     * instead of completing its future with an {@link
-     * android.app.appsearch.exceptions.AppSearchException} with the {@link
-     * AppSearchResult#RESULT_INVALID_SCHEMA} error code, all documents which are not compatible
-     * with the new schema will be deleted and the incompatible schema will be applied.
+     * <p>If you need to make non-backwards-compatible changes as described above, you can either:
+     *
+     * <ul>
+     *   <li>Set the {@link SetSchemaRequest.Builder#setForceOverride} method to {@code true}. In
+     *       this case, instead of completing its future with an {@link
+     *       android.app.appsearch.exceptions.AppSearchException} with the {@link
+     *       AppSearchResult#RESULT_INVALID_SCHEMA} error code, all documents which are not
+     *       compatible with the new schema will be deleted and the incompatible schema will be
+     *       applied.
+     *   <li>Add a {@link android.app.appsearch.AppSearchSchema.Migrator} for each incompatible type
+     *       and make no deletion. The migrator will migrate documents from it's old schema version
+     *       to the new version. See the migration section below.
+     * </ul>
      *
      * <p>It is a no-op to set the same schema as has been previously set; this is handled
      * efficiently.
@@ -85,13 +92,34 @@
      * Visibility settings for a schema type do not apply or persist across {@link
      * SetSchemaRequest}s.
      *
+     * <p>Migration: make non-backwards-compatible changes will delete all stored documents in old
+     * schema. You can save your documents by setting {@link
+     * android.app.appsearch.AppSearchSchema.Migrator} via the {@link
+     * SetSchemaRequest.Builder#setMigrator} for each type you want to save.
+     *
+     * <p>{@link android.app.appsearch.AppSearchSchema.Migrator#onDowngrade} or {@link
+     * android.app.appsearch.AppSearchSchema.Migrator#onUpgrade} will be triggered if the version
+     * number of the schema stored in AppSearch is different with the version in the request.
+     *
+     * <p>If any error or Exception occurred in the {@link
+     * android.app.appsearch.AppSearchSchema.Migrator#onDowngrade}, {@link
+     * android.app.appsearch.AppSearchSchema.Migrator#onUpgrade} or {@link
+     * android.app.appsearch.AppSearchMigrationHelper.Transformer#transform}, the migration will be
+     * terminated, the setSchema request will be rejected unless the schema changes are
+     * backwards-compatible, and stored documents won't have any observable changes.
+     *
      * @param request The schema update request.
-     * @return The pending result of performing this operation.
+     * @return The pending {@link SetSchemaResponse} of performing this operation. Success if the
+     *     the schema has been set and any migrations has been done. Otherwise, the failure {@link
+     *     android.app.appsearch.SetSchemaResponse.MigrationFailure} indicates which document is
+     *     fail to be migrated.
+     * @see android.app.appsearch.AppSearchSchema.Migrator
+     * @see android.app.appsearch.AppSearchMigrationHelper.Transformer
      */
     // TODO(b/169883602): Change @code references to @link when setPlatformSurfaceable APIs are
     //  exposed.
     @NonNull
-    ListenableFuture<Void> setSchema(@NonNull SetSchemaRequest request);
+    ListenableFuture<SetSchemaResponse> setSchema(@NonNull SetSchemaRequest request);
 
     /**
      * Retrieves the schema most recently successfully provided to {@link #setSchema}.
@@ -227,6 +255,17 @@
             @NonNull String queryExpression, @NonNull SearchSpec searchSpec);
 
     /**
+     * Flush all schema and document updates, additions, and deletes to disk if possible.
+     *
+     * @return The pending result of performing this operation. {@link
+     *     android.app.appsearch.exceptions.AppSearchException} with {@link
+     *     AppSearchResult#RESULT_INTERNAL_ERROR} will be set to the future if we hit error when
+     *     save to disk.
+     */
+    @NonNull
+    ListenableFuture<Void> maybeFlush();
+
+    /**
      * Closes the {@link AppSearchSessionShim} to persist all schema and document updates,
      * additions, and deletes to disk.
      */
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 13858a3..20fb909 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
@@ -25,6 +25,7 @@
 import android.app.appsearch.GetByUriRequest;
 import android.app.appsearch.SearchResult;
 import android.app.appsearch.SearchResultsShim;
+import android.app.appsearch.SetSchemaResponse;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -42,6 +43,15 @@
         return result;
     }
 
+    // TODO(b/151178558) check setSchemaResponse.xxxtypes for the test need to verify.
+    public static void checkIsSetSchemaResponseSuccess(Future<SetSchemaResponse> future)
+            throws Exception {
+        SetSchemaResponse setSchemaResponse = future.get();
+        assertWithMessage("SetSchemaResponse not successful.")
+                .that(setSchemaResponse.isSuccess())
+                .isTrue();
+    }
+
     public static List<GenericDocument> doGet(
             AppSearchSessionShim session, String namespace, String... uris) throws Exception {
         AppSearchBatchResult<String, GenericDocument> result =
diff --git a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
index 484fec3..56aa590 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
@@ -40,6 +40,13 @@
             "options": [
                 {"include-filter": "com.android.server.job"}
             ]
+        },
+        {
+            "name": "CtsHostsideNetworkTests",
+            "options": [
+                {"include-filter": "com.android.cts.net.HostsideRestrictBackgroundNetworkTests#testMeteredNetworkAccess_expeditedJob"},
+                {"include-filter": "com.android.cts.net.HostsideRestrictBackgroundNetworkTests#testNonMeteredNetworkAccess_expeditedJob"}
+            ]
         }
     ]
 }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
index 04d6947..58396eb 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -203,7 +203,7 @@
             isActive = (activeState == KNOWN_ACTIVE);
         }
         if (isActive && jobStatus.getStandbyBucket() == NEVER_INDEX) {
-            Slog.wtf(TAG, "App " + packageName + " became active but still in NEVER bucket");
+            jobStatus.maybeLogBucketMismatch();
         }
         boolean didChange = jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun);
         didChange |= jobStatus.setUidActive(isActive);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 51525e0..09dc7d2 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -206,6 +206,11 @@
     private int standbyBucket;
 
     /**
+     * Whether we've logged an error due to standby bucket mismatch with active uid state.
+     */
+    private boolean mLoggedBucketMismatch;
+
+    /**
      * Debugging: timestamp if we ever defer this job based on standby bucketing, this
      * is when we did so.
      */
@@ -805,6 +810,18 @@
         }
 
         standbyBucket = newBucket;
+        mLoggedBucketMismatch = false;
+    }
+
+    /**
+     * Log a bucket mismatch if this is the first time for this job.
+     */
+    public void maybeLogBucketMismatch() {
+        if (!mLoggedBucketMismatch) {
+            Slog.wtf(TAG,
+                    "App " + getSourcePackageName() + " became active but still in NEVER bucket");
+            mLoggedBucketMismatch = true;
+        }
     }
 
     // Called only by the standby monitoring code
diff --git a/apex/media/framework/api/current.txt b/apex/media/framework/api/current.txt
index 103bb47..2543a9c 100644
--- a/apex/media/framework/api/current.txt
+++ b/apex/media/framework/api/current.txt
@@ -22,7 +22,6 @@
     method @NonNull public android.media.ApplicationMediaCapabilities.Builder addUnsupportedHdrType(@NonNull String);
     method @NonNull public android.media.ApplicationMediaCapabilities.Builder addUnsupportedVideoMimeType(@NonNull String);
     method @NonNull public android.media.ApplicationMediaCapabilities build();
-    method @NonNull public android.media.ApplicationMediaCapabilities.Builder setSlowMotionSupported(boolean);
   }
 
   public static class ApplicationMediaCapabilities.FormatNotFoundException extends android.util.AndroidException {
diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
index 24c9e78..e1a8596 100644
--- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
+++ b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
@@ -56,16 +56,10 @@
  * <h4>Capability of handling HDR(high dynamic range) video</h4>
  * There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform,
  * application will only need to specify individual types they supported.
- *
- * <h4>Capability of handling Slow Motion video</h4>
- * There is no standard format for slow motion yet. If an application indicates support for slow
- * motion, it is application's responsibility to parse the slow motion videos using their own parser
- * or using support library.
  */
 // TODO(huang): Correct openTypedAssetFileDescriptor with the new API after it is added.
 // TODO(hkuang): Add a link to seamless transcoding detail when it is published
 // TODO(hkuang): Add code sample on how to build a capability object with MediaCodecList
-// TODO(hkuang): Add the support library page on parsing slow motion video.
 public final class ApplicationMediaCapabilities implements Parcelable {
     private static final String TAG = "ApplicationMediaCapabilities";
 
@@ -260,6 +254,7 @@
 
     /*
      * Whether handling of slow-motion video is supported
+     * @hide
      */
     public boolean isSlowMotionSupported() {
         return mIsSlowMotionSupported;
@@ -578,6 +573,7 @@
          * If an application indicates support for slow-motion, it is application's responsibility
          * to parse the slow-motion videos using their own parser or using support library.
          * @see android.media.MediaFormat#KEY_SLOW_MOTION_MARKERS
+         * @hide
          */
         @NonNull
         public Builder setSlowMotionSupported(boolean slowMotionSupported) {
diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java
index 55c4629..0852fdf 100644
--- a/apex/media/framework/java/android/media/MediaTranscodeManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java
@@ -1023,10 +1023,13 @@
                             "Source Width and height must be larger than 0");
                 }
 
-                float frameRate = mSrcVideoFormatHint.getFloat(MediaFormat.KEY_FRAME_RATE);
-                if (frameRate <= 0) {
-                    throw new IllegalArgumentException(
-                            "frameRate must be larger than 0");
+                float frameRate = 30.0f; // default to 30fps.
+                if (mSrcVideoFormatHint.containsKey(MediaFormat.KEY_FRAME_RATE)) {
+                    frameRate = mSrcVideoFormatHint.getFloat(MediaFormat.KEY_FRAME_RATE);
+                    if (frameRate <= 0) {
+                        throw new IllegalArgumentException(
+                                "frameRate must be larger than 0");
+                    }
                 }
 
                 int bitrate = getAVCBitrate(width, height, frameRate);
diff --git a/api/Android.bp b/api/Android.bp
index 69f03b8..1a58a16f 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -52,10 +52,7 @@
             dest: "current.txt",
         },
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk", "win_sdk"],
             dir: "apistubs/android/public/api",
             dest: "android.txt",
         },
@@ -112,6 +109,11 @@
             dir: "api",
             dest: "removed.txt",
         },
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/public/api",
+            dest: "removed.txt",
+        },
     ],
 }
 
@@ -139,10 +141,7 @@
             dest: "system-current.txt",
         },
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk", "win_sdk"],
             dir: "apistubs/android/system/api",
             dest: "android.txt",
         },
@@ -173,6 +172,11 @@
             dir: "api",
             dest: "system-removed.txt",
         },
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/system/api",
+            dest: "removed.txt",
+        },
     ],
     visibility: ["//visibility:public"],
 }
@@ -201,10 +205,7 @@
             dest: "module-lib-current.txt",
         },
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk", "win_sdk"],
             dir: "apistubs/android/module-lib/api",
             dest: "android.txt",
         },
@@ -234,6 +235,11 @@
             dir: "api",
             dest: "module-lib-removed.txt",
         },
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/module-lib/api",
+            dest: "removed.txt",
+        },
     ],
 }
 
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index ebe6199..4e46aa3 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -174,10 +174,6 @@
                 instrument.noWindowAnimation = true;
             } else if (opt.equals("--no-hidden-api-checks")) {
                 instrument.disableHiddenApiChecks = true;
-            } else if (opt.equals("--no-test-api-checks")) {
-                // TODO(satayev): remove this option, only kept for backwards compatibility with
-                // cached tradefed instance
-                instrument.disableTestApiChecks = false;
             } else if (opt.equals("--no-test-api-access")) {
                 instrument.disableTestApiChecks = false;
             } else if (opt.equals("--no-isolated-storage")) {
@@ -200,7 +196,6 @@
         }
 
         instrument.componentNameArg = nextArgRequired();
-
         instrument.run();
     }
 }
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index 9088db8..260c8a4 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -20,6 +20,7 @@
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.os.storage.DiskInfo;
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
@@ -30,6 +31,8 @@
 
 public final class Sm {
     private static final String TAG = "Sm";
+    private static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
+            "persist.sys.vold_app_data_isolation_enabled";
 
     IStorageManager mSm;
 
@@ -254,6 +257,10 @@
     }
 
     public void runDisableAppDataIsolation() throws RemoteException {
+        if (!SystemProperties.getBoolean(
+                ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false)) {
+            throw new IllegalStateException("Storage app data isolation is not enabled.");
+        }
         final String pkgName = nextArg();
         final int pid = Integer.parseInt(nextArg());
         final int userId = Integer.parseInt(nextArg());
diff --git a/core/api/current.txt b/core/api/current.txt
index b8327a9..892ddf4 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -10311,6 +10311,7 @@
     method public void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
     method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public abstract void sendOrderedBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
     method @Deprecated @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) public abstract void sendStickyBroadcast(@RequiresPermission android.content.Intent);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) public void sendStickyBroadcast(@NonNull @RequiresPermission android.content.Intent, @Nullable android.os.Bundle);
     method @Deprecated @RequiresPermission(allOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.BROADCAST_STICKY}) public abstract void sendStickyBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle);
     method @Deprecated @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) public abstract void sendStickyOrderedBroadcast(@RequiresPermission android.content.Intent, android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
     method @Deprecated @RequiresPermission(allOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.BROADCAST_STICKY}) public abstract void sendStickyOrderedBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
@@ -12383,6 +12384,7 @@
     field public static final String FEATURE_SCREEN_PORTRAIT = "android.hardware.screen.portrait";
     field public static final String FEATURE_SECURELY_REMOVES_USERS = "android.software.securely_removes_users";
     field public static final String FEATURE_SECURE_LOCK_SCREEN = "android.software.secure_lock_screen";
+    field public static final String FEATURE_SECURITY_MODEL_COMPATIBLE = "android.hardware.security.model.compatible";
     field public static final String FEATURE_SENSOR_ACCELEROMETER = "android.hardware.sensor.accelerometer";
     field public static final String FEATURE_SENSOR_AMBIENT_TEMPERATURE = "android.hardware.sensor.ambient_temperature";
     field public static final String FEATURE_SENSOR_BAROMETER = "android.hardware.sensor.barometer";
@@ -36459,6 +36461,9 @@
     method @NonNull public String getVersion();
     method public boolean isConnected();
     method public void shutdown();
+    field public static final String ACTION_SECURE_ELEMENT_STATE_CHANGED = "android.se.omapi.action.SECURE_ELEMENT_STATE_CHANGED";
+    field public static final String EXTRA_READER_NAME = "android.se.omapi.extra.READER_NAME";
+    field public static final String EXTRA_READER_STATE = "android.se.omapi.extra.READER_STATE";
   }
 
   public static interface SEService.OnConnectedListener {
@@ -38202,6 +38207,7 @@
     ctor public SpellCheckerService.Session();
     method public android.os.Bundle getBundle();
     method public String getLocale();
+    method public int getSupportedAttributes();
     method public void onCancel();
     method public void onClose();
     method public abstract void onCreate();
@@ -41474,6 +41480,7 @@
 
   public final class SignalStrengthUpdateRequest implements android.os.Parcelable {
     method public int describeContents();
+    method @NonNull public android.os.IBinder getLiveToken();
     method @NonNull public java.util.Collection<android.telephony.SignalThresholdInfo> getSignalThresholdInfos();
     method public boolean isReportingRequestedWhileIdle();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -41776,7 +41783,9 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void removeSubscriptionsFromGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunistic(boolean, int);
     method public void setSubscriptionOverrideCongested(int, boolean, long);
+    method public void setSubscriptionOverrideCongested(int, boolean, @NonNull int[], long);
     method public void setSubscriptionOverrideUnmetered(int, boolean, long);
+    method public void setSubscriptionOverrideUnmetered(int, boolean, @NonNull int[], long);
     method public void setSubscriptionPlans(int, @NonNull java.util.List<android.telephony.SubscriptionPlan>);
     method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, @NonNull android.app.PendingIntent);
     field public static final String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED";
@@ -41852,6 +41861,7 @@
 
   public class TelephonyManager {
     method public boolean canChangeDtmfToneLength();
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void clearSignalStrengthUpdateRequest(@NonNull android.telephony.SignalStrengthUpdateRequest);
     method @Nullable public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
     method public android.telephony.TelephonyManager createForSubscriptionId(int);
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean doesSwitchMultiSimConfigTriggerReboot();
@@ -41931,7 +41941,7 @@
     method @Deprecated public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String);
     method public boolean isConcurrentVoiceAndDataSupported();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isDataConnectionAllowed();
-    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean isDataEnabled();
+    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabled();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabledForReason(int);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled();
     method public boolean isEmergencyNumber(@NonNull String);
@@ -41965,6 +41975,7 @@
     method public boolean setOperatorBrandOverride(String);
     method public boolean setPreferredNetworkTypeToGlobal();
     method public void setPreferredOpportunisticDataSubscription(int, boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSignalStrengthUpdateRequest(@NonNull android.telephony.SignalStrengthUpdateRequest);
     method public void setVisualVoicemailSmsFilterSettings(android.telephony.VisualVoicemailSmsFilterSettings);
     method public boolean setVoiceMailNumber(String, String);
     method @Deprecated public void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
@@ -42630,6 +42641,7 @@
     field public static final int CODE_MEDIA_NO_DATA = 402; // 0x192
     field public static final int CODE_MEDIA_UNSPECIFIED = 404; // 0x194
     field public static final int CODE_MULTIENDPOINT_NOT_SUPPORTED = 902; // 0x386
+    field public static final int CODE_NETWORK_CONGESTION = 1624; // 0x658
     field public static final int CODE_NETWORK_DETACH = 1513; // 0x5e9
     field public static final int CODE_NETWORK_REJECT = 1504; // 0x5e0
     field public static final int CODE_NETWORK_RESP_TIMEOUT = 1503; // 0x5df
@@ -51794,7 +51806,8 @@
     method @Nullable public android.view.textservice.SpellCheckerSubtype getCurrentSpellCheckerSubtype(boolean);
     method @Nullable public java.util.List<android.view.textservice.SpellCheckerInfo> getEnabledSpellCheckersList();
     method public boolean isSpellCheckerEnabled();
-    method public android.view.textservice.SpellCheckerSession newSpellCheckerSession(android.os.Bundle, java.util.Locale, android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener, boolean);
+    method @Nullable public android.view.textservice.SpellCheckerSession newSpellCheckerSession(@Nullable android.os.Bundle, @Nullable java.util.Locale, @NonNull android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener, boolean);
+    method @Nullable public android.view.textservice.SpellCheckerSession newSpellCheckerSession(@Nullable android.os.Bundle, @Nullable java.util.Locale, @NonNull android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener, boolean, int);
   }
 
 }
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index bf92b34..061dc87 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -136,6 +136,14 @@
 
 package android.net {
 
+  public class ConnectivityManager {
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
+  }
+
+  public final class NetworkCapabilities implements android.os.Parcelable {
+    field public static final int TRANSPORT_TEST = 7; // 0x7
+  }
+
   public final class TcpRepairWindow {
     ctor public TcpRepairWindow(int, int, int, int, int, int);
     field public final int maxWindow;
@@ -146,6 +154,22 @@
     field public final int sndWnd;
   }
 
+  public final class TestNetworkInterface implements android.os.Parcelable {
+    ctor public TestNetworkInterface(@NonNull android.os.ParcelFileDescriptor, @NonNull String);
+    method public int describeContents();
+    method @NonNull public android.os.ParcelFileDescriptor getFileDescriptor();
+    method @NonNull public String getInterfaceName();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.TestNetworkInterface> CREATOR;
+  }
+
+  public class TestNetworkManager {
+    method @NonNull public android.net.TestNetworkInterface createTapInterface();
+    method @NonNull public android.net.TestNetworkInterface createTunInterface(@NonNull java.util.Collection<android.net.LinkAddress>);
+    method public void setupTestNetwork(@NonNull String, @NonNull android.os.IBinder);
+    method public void teardownTestNetwork(@NonNull android.net.Network);
+  }
+
 }
 
 package android.os {
@@ -154,6 +178,10 @@
     method public final void markVintfStability();
   }
 
+  public static class Build.VERSION {
+    field public static final int FIRST_SDK_INT;
+  }
+
   public interface Parcelable {
     method public default int getStability();
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 5f61472..21da2fb 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -45,6 +45,7 @@
     field public static final String BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE = "android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE";
     field public static final String BIND_PRINT_RECOMMENDATION_SERVICE = "android.permission.BIND_PRINT_RECOMMENDATION_SERVICE";
     field public static final String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
+    field public static final String BIND_RESUME_ON_REBOOT_SERVICE = "android.permission.BIND_RESUME_ON_REBOOT_SERVICE";
     field public static final String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
     field public static final String BIND_SETTINGS_SUGGESTIONS_SERVICE = "android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE";
     field public static final String BIND_SOUND_TRIGGER_DETECTION_SERVICE = "android.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE";
@@ -1657,8 +1658,14 @@
 package android.bluetooth {
 
   public final class BluetoothA2dp implements android.bluetooth.BluetoothProfile {
+    method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public android.bluetooth.BufferConstraints getBufferConstraints();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getDynamicBufferSupport();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setBufferMillis(int, int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+    field public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD = 1; // 0x1
+    field public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING = 2; // 0x2
+    field public static final int DYNAMIC_BUFFER_SUPPORT_NONE = 0; // 0x0
     field public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0; // 0x0
     field public static final int OPTIONAL_CODECS_PREF_DISABLED = 0; // 0x0
     field public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; // 0x1
@@ -1840,6 +1847,25 @@
     field public static final int UUID_BYTES_32_BIT = 4; // 0x4
   }
 
+  public final class BufferConstraint implements android.os.Parcelable {
+    ctor public BufferConstraint(int, int, int);
+    method public int describeContents();
+    method public int getDefaultMillis();
+    method public int getMaxMillis();
+    method public int getMinMillis();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BufferConstraint> CREATOR;
+  }
+
+  public final class BufferConstraints implements android.os.Parcelable {
+    ctor public BufferConstraints(@NonNull java.util.List<android.bluetooth.BufferConstraint>);
+    method public int describeContents();
+    method @Nullable public android.bluetooth.BufferConstraint getCodec(int);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int BUFFER_CODEC_MAX_NUM = 32; // 0x20
+    field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BufferConstraints> CREATOR;
+  }
+
 }
 
 package android.bluetooth.le {
@@ -6821,6 +6847,7 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
+    method public void registerQosCallback(@NonNull android.net.QosSocketInfo, @NonNull android.net.QosCallback, @NonNull java.util.concurrent.Executor);
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
     method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
@@ -6830,6 +6857,7 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider);
+    method public void unregisterQosCallback(@NonNull android.net.QosCallback);
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
     field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
     field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
@@ -7023,6 +7051,8 @@
     method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
     method public void onAutomaticReconnectDisabled();
     method public void onNetworkUnwanted();
+    method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter);
+    method public void onQosCallbackUnregistered(int);
     method public void onRemoveKeepalivePacketFilter(int);
     method public void onSaveAcceptUnvalidated(boolean);
     method public void onSignalStrengthThresholdsUpdated(@NonNull int[]);
@@ -7033,6 +7063,9 @@
     method public final void sendLinkProperties(@NonNull android.net.LinkProperties);
     method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
     method public final void sendNetworkScore(@IntRange(from=0, to=99) int);
+    method public final void sendQosCallbackError(int, int);
+    method public final void sendQosSessionAvailable(int, int, @NonNull android.telephony.data.EpsBearerQosSessionAttributes);
+    method public final void sendQosSessionLost(int, int);
     method public final void sendSocketKeepaliveEvent(int, int);
     method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
     method public void unregister();
@@ -7119,6 +7152,9 @@
     method public abstract void onRequestScores(android.net.NetworkKey[]);
   }
 
+  public class NetworkReleasedException extends java.lang.Exception {
+  }
+
   public class NetworkRequest implements android.os.Parcelable {
     method @Nullable public String getRequestorPackageName();
     method public int getRequestorUid();
@@ -7191,6 +7227,46 @@
     ctor public NetworkStats.Entry(@Nullable String, int, int, int, int, int, int, long, long, long, long, long);
   }
 
+  public abstract class QosCallback {
+    ctor public QosCallback();
+    method public void onError(@NonNull android.net.QosCallbackException);
+    method public void onQosSessionAvailable(@NonNull android.net.QosSession, @NonNull android.net.QosSessionAttributes);
+    method public void onQosSessionLost(@NonNull android.net.QosSession);
+  }
+
+  public static class QosCallback.QosCallbackRegistrationException extends java.lang.RuntimeException {
+  }
+
+  public final class QosCallbackException extends java.lang.Exception {
+  }
+
+  public abstract class QosFilter {
+    method @NonNull public abstract android.net.Network getNetwork();
+  }
+
+  public final class QosSession implements android.os.Parcelable {
+    ctor public QosSession(int, int);
+    method public int describeContents();
+    method public int getSessionId();
+    method public int getSessionType();
+    method public long getUniqueId();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSession> CREATOR;
+    field public static final int TYPE_EPS_BEARER = 1; // 0x1
+  }
+
+  public interface QosSessionAttributes {
+  }
+
+  public final class QosSocketInfo implements android.os.Parcelable {
+    ctor public QosSocketInfo(@NonNull android.net.Network, @NonNull java.net.Socket) throws java.io.IOException;
+    method public int describeContents();
+    method @NonNull public java.net.InetSocketAddress getLocalSocketAddress();
+    method @NonNull public android.net.Network getNetwork();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSocketInfo> CREATOR;
+  }
+
   public final class RouteInfo implements android.os.Parcelable {
     ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int);
     ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int, int);
@@ -7236,6 +7312,12 @@
     field public static final int SUCCESS = 0; // 0x0
   }
 
+  public class SocketLocalAddressChangedException extends java.lang.Exception {
+  }
+
+  public class SocketNotBoundException extends java.lang.Exception {
+  }
+
   public final class StaticIpConfiguration implements android.os.Parcelable {
     ctor public StaticIpConfiguration();
     ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration);
@@ -9347,8 +9429,8 @@
   public abstract class ImpressionAttestationService extends android.app.Service {
     ctor public ImpressionAttestationService();
     method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
-    method @Nullable public abstract android.service.attestation.ImpressionToken onGenerateImpressionToken(@NonNull String, @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String);
-    method public abstract boolean onVerifyImpressionToken(@NonNull String, @NonNull android.service.attestation.ImpressionToken);
+    method @Nullable public abstract android.service.attestation.ImpressionToken onGenerateImpressionToken(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String);
+    method public abstract boolean onVerifyImpressionToken(@NonNull byte[], @NonNull android.service.attestation.ImpressionToken);
   }
 
   public final class ImpressionToken implements android.os.Parcelable {
@@ -9886,6 +9968,18 @@
 
 }
 
+package android.service.resumeonreboot {
+
+  public abstract class ResumeOnRebootService extends android.app.Service {
+    ctor public ResumeOnRebootService();
+    method @Nullable public android.os.IBinder onBind(@Nullable android.content.Intent);
+    method @NonNull public abstract byte[] onUnwrap(@NonNull byte[]) throws java.io.IOException;
+    method @NonNull public abstract byte[] onWrap(@NonNull byte[], long) throws java.io.IOException;
+    field public static final String SERVICE_INTERFACE = "android.service.resumeonreboot.ResumeOnRebootService";
+  }
+
+}
+
 package android.service.search {
 
   public abstract class SearchUiService extends android.app.Service {
@@ -11832,6 +11926,19 @@
     field public static final int RESULT_SUCCESS = 0; // 0x0
   }
 
+  public final class EpsBearerQosSessionAttributes implements android.os.Parcelable android.net.QosSessionAttributes {
+    method @NonNull public static android.telephony.data.EpsBearerQosSessionAttributes create(@NonNull android.os.Parcel);
+    method public int describeContents();
+    method public long getGuaranteedDownlinkBitRate();
+    method public long getGuaranteedUplinkBitRate();
+    method public long getMaxDownlinkBitRate();
+    method public long getMaxUplinkBitRate();
+    method public int getQci();
+    method @NonNull public java.util.List<java.net.InetSocketAddress> getRemoteAddresses();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.EpsBearerQosSessionAttributes> CREATOR;
+  }
+
   public abstract class QualifiedNetworksService extends android.app.Service {
     ctor public QualifiedNetworksService();
     method @NonNull public abstract android.telephony.data.QualifiedNetworksService.NetworkAvailabilityProvider onCreateNetworkAvailabilityProvider(int);
@@ -13384,15 +13491,16 @@
 
   public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable {
     method public final long getUserActivityTimeout();
+    method public boolean isSystemApplicationOverlay();
+    method @RequiresPermission(android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY) public void setSystemApplicationOverlay(boolean);
     method public final void setUserActivityTimeout(long);
     field @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR) public static final int FLAG_BLUR_BEHIND = 4; // 0x4
     field @RequiresPermission(android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS) public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000
     field @RequiresPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW) public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 16; // 0x10
-    field @RequiresPermission(android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY) public static final int SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY = 8; // 0x8
     field @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR) public int backgroundBlurRadius;
   }
 
-  @IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS, android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags {
+  @IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags {
   }
 
 }
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 870ab83..a11ee06 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -377,6 +377,8 @@
 package android.app.admin {
 
   public class DevicePolicyManager {
+    method public int checkProvisioningPreCondition(@Nullable String, @NonNull String);
+    method @Nullable public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
     method public void forceUpdateUserSetupComplete();
     method public long getLastBugReportRequestTime();
     method public long getLastNetworkLogRetrievalTime();
@@ -385,8 +387,27 @@
     method public boolean isCurrentInputMethodSetByOwner();
     method public boolean isFactoryResetProtectionPolicySupported();
     method @NonNull public static String operationToString(int);
+    method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
     method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, boolean);
     field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
+    field public static final String ACTION_MANAGED_PROFILE_CREATED = "android.app.action.MANAGED_PROFILE_CREATED";
+    field public static final String ACTION_PROVISIONED_MANAGED_DEVICE = "android.app.action.PROVISIONED_MANAGED_DEVICE";
+    field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
+    field public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb
+    field public static final int CODE_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd
+    field public static final int CODE_HAS_DEVICE_OWNER = 1; // 0x1
+    field public static final int CODE_HAS_PAIRED = 8; // 0x8
+    field public static final int CODE_MANAGED_USERS_NOT_SUPPORTED = 9; // 0x9
+    field public static final int CODE_NONSYSTEM_USER_EXISTS = 5; // 0x5
+    field public static final int CODE_NOT_SYSTEM_USER = 7; // 0x7
+    field public static final int CODE_NOT_SYSTEM_USER_SPLIT = 12; // 0xc
+    field public static final int CODE_OK = 0; // 0x0
+    field public static final int CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS = 15; // 0xf
+    field public static final int CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14; // 0xe
+    field public static final int CODE_SYSTEM_USER = 10; // 0xa
+    field public static final int CODE_USER_HAS_PROFILE_OWNER = 2; // 0x2
+    field public static final int CODE_USER_NOT_RUNNING = 3; // 0x3
+    field public static final int CODE_USER_SETUP_COMPLETED = 4; // 0x4
     field public static final int OPERATION_CLEAR_APPLICATION_USER_DATA = 23; // 0x17
     field public static final int OPERATION_CREATE_AND_MANAGE_USER = 5; // 0x5
     field public static final int OPERATION_INSTALL_CA_CERT = 24; // 0x18
@@ -427,6 +448,62 @@
     field public static final int OPERATION_SWITCH_USER = 2; // 0x2
     field public static final int OPERATION_UNINSTALL_CA_CERT = 40; // 0x28
     field public static final int OPERATION_WIPE_DATA = 8; // 0x8
+    field public static final int PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED = 3; // 0x3
+    field public static final int PROVISIONING_RESULT_PRE_CONDITION_FAILED = 1; // 0x1
+    field public static final int PROVISIONING_RESULT_PROFILE_CREATION_FAILED = 2; // 0x2
+    field public static final int PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED = 6; // 0x6
+    field public static final int PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED = 4; // 0x4
+    field public static final int PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED = 7; // 0x7
+    field public static final int PROVISIONING_RESULT_STARTING_PROFILE_FAILED = 5; // 0x5
+  }
+
+  public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.content.ComponentName getDeviceAdminComponentName();
+    method public long getLocalTime();
+    method @Nullable public java.util.Locale getLocale();
+    method @NonNull public String getOwnerName();
+    method @Nullable public String getTimeZone();
+    method public boolean isLeaveAllSystemAppsEnabled();
+    method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.FullyManagedDeviceProvisioningParams> CREATOR;
+  }
+
+  public static final class FullyManagedDeviceProvisioningParams.Builder {
+    ctor public FullyManagedDeviceProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
+    method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams build();
+    method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
+    method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocalTime(long);
+    method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocale(@Nullable java.util.Locale);
+    method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setTimeZone(@Nullable String);
+  }
+
+  public final class ManagedProfileProvisioningParams implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public android.accounts.Account getAccountToMigrate();
+    method @NonNull public String getOwnerName();
+    method @NonNull public android.content.ComponentName getProfileAdminComponentName();
+    method @Nullable public String getProfileName();
+    method public boolean isKeepAccountMigrated();
+    method public boolean isLeaveAllSystemAppsEnabled();
+    method public boolean isOrganizationOwnedProvisioning();
+    method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.ManagedProfileProvisioningParams> CREATOR;
+  }
+
+  public static final class ManagedProfileProvisioningParams.Builder {
+    ctor public ManagedProfileProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
+    method @NonNull public android.app.admin.ManagedProfileProvisioningParams build();
+    method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setAccountToMigrate(@Nullable android.accounts.Account);
+    method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setKeepAccountMigrated(boolean);
+    method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
+    method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setOrganizationOwnedProvisioning(boolean);
+    method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setProfileName(@Nullable String);
+  }
+
+  public class ProvisioningException extends android.util.AndroidException {
+    ctor public ProvisioningException(@NonNull Exception, int);
+    method public int getProvisioningResult();
   }
 
   public static final class SecurityLog.SecurityEvent implements android.os.Parcelable {
@@ -1095,6 +1172,11 @@
     method public void forceResourceLost();
   }
 
+  public final class MediaCodec implements android.media.metrics.PlaybackComponent {
+    method public String getPlaybackId();
+    method public void setPlaybackId(@NonNull String);
+  }
+
   public static final class MediaCodecInfo.VideoCapabilities.PerformancePoint {
     ctor public MediaCodecInfo.VideoCapabilities.PerformancePoint(int, int, int, int, @NonNull android.util.Size);
     ctor public MediaCodecInfo.VideoCapabilities.PerformancePoint(@NonNull android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint, @NonNull android.util.Size);
@@ -1103,6 +1185,10 @@
     method public int getMaxMacroBlocks();
   }
 
+  public final class MediaDrm implements java.lang.AutoCloseable {
+    method @Nullable public android.media.metrics.PlaybackComponent getPlaybackComponent(@NonNull byte[]);
+  }
+
   public final class MediaRoute2Info implements android.os.Parcelable {
     method @NonNull public String getOriginalId();
   }
@@ -1165,6 +1251,15 @@
 
 }
 
+package android.media.metrics {
+
+  public interface PlaybackComponent {
+    method @NonNull public String getPlaybackId();
+    method public void setPlaybackId(@NonNull String);
+  }
+
+}
+
 package android.media.tv {
 
   public final class TvInputManager {
@@ -1187,10 +1282,6 @@
 
 package android.net {
 
-  public class ConnectivityManager {
-    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 {
     method public void setIncludeTestInterfaces(boolean);
   }
@@ -1199,31 +1290,10 @@
     field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
   }
 
-  public final class NetworkCapabilities implements android.os.Parcelable {
-    method public int[] getCapabilities();
-    field public static final int TRANSPORT_TEST = 7; // 0x7
-  }
-
   public class NetworkStack {
     method public static void setServiceForTest(@Nullable android.os.IBinder);
   }
 
-  public final class TestNetworkInterface implements android.os.Parcelable {
-    ctor public TestNetworkInterface(android.os.ParcelFileDescriptor, String);
-    method public int describeContents();
-    method public android.os.ParcelFileDescriptor getFileDescriptor();
-    method public String getInterfaceName();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.net.TestNetworkInterface> CREATOR;
-  }
-
-  public class TestNetworkManager {
-    method public android.net.TestNetworkInterface createTapInterface();
-    method public android.net.TestNetworkInterface createTunInterface(@NonNull android.net.LinkAddress[]);
-    method public void setupTestNetwork(@NonNull String, @NonNull android.os.IBinder);
-    method public void teardownTestNetwork(@NonNull android.net.Network);
-  }
-
   public class TrafficStats {
     method public static long getLoopbackRxBytes();
     method public static long getLoopbackRxPackets();
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index d0d5df9..baf21ed 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -199,6 +199,14 @@
             "android.activity.launchTaskDisplayAreaToken";
 
     /**
+     * The root task token the activity should be launched into.
+     * @see #setLaunchRootTask(WindowContainerToken)
+     * @hide
+     */
+    public static final String KEY_LAUNCH_ROOT_TASK_TOKEN =
+            "android.activity.launchRootTaskToken";
+
+    /**
      * The windowing mode the activity should be launched into.
      * @hide
      */
@@ -306,7 +314,7 @@
      * @see #setLaunchCookie
      * @hide
      */
-    private static final String KEY_LAUNCH_COOKIE = "android.activity.launchCookie";
+    public static final String KEY_LAUNCH_COOKIE = "android.activity.launchCookie";
 
     /** @hide */
     public static final int ANIM_UNDEFINED = -1;
@@ -362,6 +370,7 @@
     private int mLaunchDisplayId = INVALID_DISPLAY;
     private int mCallerDisplayId = INVALID_DISPLAY;
     private WindowContainerToken mLaunchTaskDisplayArea;
+    private WindowContainerToken mLaunchRootTask;
     @WindowConfiguration.WindowingMode
     private int mLaunchWindowingMode = WINDOWING_MODE_UNDEFINED;
     @WindowConfiguration.ActivityType
@@ -1050,6 +1059,7 @@
         mLaunchDisplayId = opts.getInt(KEY_LAUNCH_DISPLAY_ID, INVALID_DISPLAY);
         mCallerDisplayId = opts.getInt(KEY_CALLER_DISPLAY_ID, INVALID_DISPLAY);
         mLaunchTaskDisplayArea = opts.getParcelable(KEY_LAUNCH_TASK_DISPLAY_AREA_TOKEN);
+        mLaunchRootTask = opts.getParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN);
         mLaunchWindowingMode = opts.getInt(KEY_LAUNCH_WINDOWING_MODE, WINDOWING_MODE_UNDEFINED);
         mLaunchActivityType = opts.getInt(KEY_LAUNCH_ACTIVITY_TYPE, ACTIVITY_TYPE_UNDEFINED);
         mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1);
@@ -1342,6 +1352,17 @@
     }
 
     /** @hide */
+    public WindowContainerToken getLaunchRootTask() {
+        return mLaunchRootTask;
+    }
+
+    /** @hide */
+    public ActivityOptions setLaunchRootTask(WindowContainerToken windowContainerToken) {
+        mLaunchRootTask = windowContainerToken;
+        return this;
+    }
+
+    /** @hide */
     public int getLaunchWindowingMode() {
         return mLaunchWindowingMode;
     }
@@ -1692,6 +1713,9 @@
         if (mLaunchTaskDisplayArea != null) {
             b.putParcelable(KEY_LAUNCH_TASK_DISPLAY_AREA_TOKEN, mLaunchTaskDisplayArea);
         }
+        if (mLaunchRootTask != null) {
+            b.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, mLaunchRootTask);
+        }
         if (mLaunchWindowingMode != WINDOWING_MODE_UNDEFINED) {
             b.putInt(KEY_LAUNCH_WINDOWING_MODE, mLaunchWindowingMode);
         }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 161b731..b85b186 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -7689,8 +7689,8 @@
         if (code != OP_RECORD_AUDIO) {
             return false;
         }
-        final String voiceRecognitionComponent = Settings.Secure.getString(
-                context.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE);
+        final String voiceRecognitionComponent = Settings.Secure.getStringForUser(
+                context.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE, userId);
 
         final String voiceRecognitionServicePackageName =
                 getComponentPackageNameFromString(voiceRecognitionComponent);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 5222699..4ddeb8f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1469,6 +1469,45 @@
         }
     }
 
+    /**
+     * <p>Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the
+     * Intent you are sending stays around after the broadcast is complete,
+     * so that others can quickly retrieve that data through the return
+     * value of {@link #registerReceiver(BroadcastReceiver, IntentFilter)}.  In
+     * all other ways, this behaves the same as
+     * {@link #sendBroadcast(Intent)}.
+     *
+     * @deprecated Sticky broadcasts should not be used.  They provide no security (anyone
+     * can access them), no protection (anyone can modify them), and many other problems.
+     * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+     * has changed, with another mechanism for apps to retrieve the current value whenever
+     * desired.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     * Intent will receive the broadcast, and the Intent will be held to
+     * be re-broadcast to future receivers.
+     * @param options (optional) Additional sending options, generated from a
+     * {@link android.app.BroadcastOptions}.
+     *
+     * @see #sendBroadcast(Intent)
+     * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
+     */
+    @Override
+    @Deprecated
+    public void sendStickyBroadcast(@NonNull Intent intent, @Nullable Bundle options) {
+        warnIfCallingFromSystemProcess();
+        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+        try {
+            intent.prepareToLeaveProcess(this);
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+                    null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options,
+                    false, true, getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     @Override
     @Deprecated
     public void sendStickyOrderedBroadcast(Intent intent,
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index e9d63d2..8d1076e 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -139,6 +139,20 @@
     }
 
     /**
+     * Returns if it is being called in an instrumentation environment.
+     *
+     * @hide
+     */
+    public boolean isInstrumenting() {
+        // Check if we have an instrumentation context, as init should only get called by
+        // the system in startup processes that are being instrumented.
+        if (mInstrContext == null) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
      * Called when the instrumentation is starting, before any application code
      * has been loaded.  Usually this will be implemented to simply call
      * {@link #start} to begin the instrumentation thread, which will then
diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java
index 71b28fb..856c13c 100644
--- a/core/java/android/app/IntentService.java
+++ b/core/java/android/app/IntentService.java
@@ -27,8 +27,9 @@
 import android.os.Message;
 
 /**
- * IntentService is a base class for {@link Service}s that handle asynchronous
- * requests (expressed as {@link Intent}s) on demand.  Clients send requests
+ * IntentService is an extension of the {@link Service} component class that
+ * handles asynchronous requests (expressed as {@link Intent}s) on demand.
+ * Clients send requests
  * through {@link android.content.Context#startService(Intent)} calls; the
  * service is started as needed, handles each Intent in turn using a worker
  * thread, and stops itself when it runs out of work.
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 269ccb1..6d75d0f 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -40,6 +40,7 @@
 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;
@@ -107,6 +108,7 @@
  * 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;
@@ -350,9 +352,20 @@
                 "Cannot set both FLAG_IMMUTABLE and FLAG_MUTABLE for PendingIntent");
         }
 
+        // TODO(b/178092897) Remove the below instrumentation check and enforce
+        // the explicit mutability requirement for apps under instrumentation.
+        ActivityThread thread = ActivityThread.currentActivityThread();
+        Instrumentation mInstrumentation = thread.getInstrumentation();
+
         if (Compatibility.isChangeEnabled(PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED)
                 && !flagImmutableSet && !flagMutableSet) {
-            throw new IllegalArgumentException(msg);
+
+            //TODO(b/178065720) Remove check for chrome and enforce this requirement
+            if (packageName.equals("com.android.chrome") || mInstrumentation.isInstrumenting()) {
+                Log.e(TAG, msg);
+            } else {
+                throw new IllegalArgumentException(msg);
+            }
         }
     }
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7e8fb91..530cfd5 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -88,6 +88,7 @@
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.net.NetworkUtilsInternal;
@@ -148,6 +149,7 @@
  */
 @SystemService(Context.DEVICE_POLICY_SERVICE)
 @RequiresFeature(PackageManager.FEATURE_DEVICE_ADMIN)
+@SuppressLint("UseIcu")
 public class DevicePolicyManager {
 
     private static String TAG = "DevicePolicyManager";
@@ -1992,6 +1994,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int CODE_OK = 0;
 
     /**
@@ -2003,6 +2006,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int CODE_HAS_DEVICE_OWNER = 1;
 
     /**
@@ -2014,6 +2018,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int CODE_USER_HAS_PROFILE_OWNER = 2;
 
     /**
@@ -2024,6 +2029,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int CODE_USER_NOT_RUNNING = 3;
 
     /**
@@ -2035,6 +2041,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int CODE_USER_SETUP_COMPLETED = 4;
 
     /**
@@ -2042,6 +2049,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int CODE_NONSYSTEM_USER_EXISTS = 5;
 
     /**
@@ -2049,6 +2057,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int CODE_ACCOUNTS_NOT_EMPTY = 6;
 
     /**
@@ -2059,6 +2068,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int CODE_NOT_SYSTEM_USER = 7;
 
     /**
@@ -2070,6 +2080,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int CODE_HAS_PAIRED = 8;
 
     /**
@@ -2081,6 +2092,7 @@
      * @see {@link PackageManager#FEATURE_MANAGED_USERS}
      * @hide
      */
+    @TestApi
     public static final int CODE_MANAGED_USERS_NOT_SUPPORTED = 9;
 
     /**
@@ -2092,6 +2104,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int CODE_SYSTEM_USER = 10;
 
     /**
@@ -2102,6 +2115,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11;
 
     /**
@@ -2114,6 +2128,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int CODE_NOT_SYSTEM_USER_SPLIT = 12;
 
     /**
@@ -2121,11 +2136,12 @@
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
      * {@link #ACTION_PROVISION_MANAGED_PROFILE}, {@link #ACTION_PROVISION_MANAGED_USER} and
-     * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} on devices which do no support device
+     * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} on devices which do not support device
      * admins.
      *
      * @hide
      */
+    @TestApi
     public static final int CODE_DEVICE_ADMIN_NOT_SUPPORTED = 13;
 
     /**
@@ -2137,9 +2153,21 @@
      *
      * @hide
      */
+    @TestApi
     public static final int CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14;
 
     /**
+     * Result code for {@link #checkProvisioningPreCondition}.
+     *
+     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
+     * {@link #ACTION_PROVISION_MANAGED_PROFILE} on devices which do not support provisioning.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS = 15;
+
+    /**
      * Result codes for {@link #checkProvisioningPreCondition} indicating all the provisioning pre
      * conditions.
      *
@@ -2151,11 +2179,94 @@
             CODE_USER_SETUP_COMPLETED, CODE_NOT_SYSTEM_USER, CODE_HAS_PAIRED,
             CODE_MANAGED_USERS_NOT_SUPPORTED, CODE_SYSTEM_USER, CODE_CANNOT_ADD_MANAGED_PROFILE,
             CODE_NOT_SYSTEM_USER_SPLIT, CODE_DEVICE_ADMIN_NOT_SUPPORTED,
-            CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER
+            CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER,
+            CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS
     })
     public @interface ProvisioningPreCondition {}
 
     /**
+     * Service-specific error code for {@link #provisionFullyManagedDevice} and
+     * {@link #createAndProvisionManagedProfile}:
+     * Indicates the call to {@link #checkProvisioningPreCondition} returned an error code.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int PROVISIONING_RESULT_PRE_CONDITION_FAILED = 1;
+
+    /**
+     * Service-specific error code for {@link #createAndProvisionManagedProfile}:
+     * Indicates the call to {@link UserManager#createProfileForUserEvenWhenDisallowed}
+     * returned {@code null}.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int PROVISIONING_RESULT_PROFILE_CREATION_FAILED = 2;
+
+    /**
+     * Service-specific error code for {@link #createAndProvisionManagedProfile}:
+     * Indicates the call to {@link PackageManager#installExistingPackageAsUser} has failed.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED = 3;
+
+    /**
+     * Service-specific error code for {@link #createAndProvisionManagedProfile}:
+     * Indicates the call to {@link #setProfileOwner} returned {@code false}.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED = 4;
+
+    /**
+     * Service-specific error code for {@link #createAndProvisionManagedProfile}:
+     * Indicates that starting the newly created profile has failed.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int PROVISIONING_RESULT_STARTING_PROFILE_FAILED = 5;
+
+    /**
+     * Service-specific error code for {@link #provisionFullyManagedDevice}:
+     * Indicates that removing the non required apps have failed.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED = 6;
+
+    /**
+     * Service-specific error code for {@link #provisionFullyManagedDevice}:
+     * Indicates the call to {@link #setDeviceOwner} returned {@code false}.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED = 7;
+
+    /**
+     * Service-specific error codes for {@link #createAndProvisionManagedProfile} and
+     * {@link #provisionFullyManagedDevice} indicating all the errors during provisioning.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "PROVISIONING_RESULT_" }, value = {
+            PROVISIONING_RESULT_PRE_CONDITION_FAILED, PROVISIONING_RESULT_PROFILE_CREATION_FAILED,
+            PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED,
+            PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED,
+            PROVISIONING_RESULT_STARTING_PROFILE_FAILED,
+            PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED,
+            PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED
+    })
+    public @interface ProvisioningResult {}
+
+    /**
      * Disable all configurable SystemUI features during LockTask mode. This includes,
      * <ul>
      *     <li>system info area in the status bar (connectivity icons, clock, etc.)
@@ -5215,30 +5326,10 @@
                     if (!proxySpec.type().equals(Proxy.Type.HTTP)) {
                         throw new IllegalArgumentException();
                     }
-                    InetSocketAddress sa = (InetSocketAddress)proxySpec.address();
-                    String hostName = sa.getHostName();
-                    int port = sa.getPort();
-                    StringBuilder hostBuilder = new StringBuilder();
-                    hostSpec = hostBuilder.append(hostName)
-                        .append(":").append(Integer.toString(port)).toString();
-                    if (exclusionList == null) {
-                        exclSpec = "";
-                    } else {
-                        StringBuilder listBuilder = new StringBuilder();
-                        boolean firstDomain = true;
-                        for (String exclDomain : exclusionList) {
-                            if (!firstDomain) {
-                                listBuilder = listBuilder.append(",");
-                            } else {
-                                firstDomain = false;
-                            }
-                            listBuilder = listBuilder.append(exclDomain.trim());
-                        }
-                        exclSpec = listBuilder.toString();
-                    }
-                    if (android.net.Proxy.validate(hostName, Integer.toString(port), exclSpec)
-                            != android.net.Proxy.PROXY_VALID)
-                        throw new IllegalArgumentException();
+                    final Pair<String, String> proxyParams =
+                            getProxyParameters(proxySpec, exclusionList);
+                    hostSpec = proxyParams.first;
+                    exclSpec = proxyParams.second;
                 }
                 return mService.setGlobalProxy(admin, hostSpec, exclSpec);
             } catch (RemoteException e) {
@@ -5249,6 +5340,41 @@
     }
 
     /**
+     * Build HTTP proxy parameters for {@link IDevicePolicyManager#setGlobalProxy}.
+     * @throws IllegalArgumentException Invalid proxySpec
+     * @hide
+     */
+    @VisibleForTesting
+    public Pair<String, String> getProxyParameters(Proxy proxySpec, List<String> exclusionList) {
+        InetSocketAddress sa = (InetSocketAddress) proxySpec.address();
+        String hostName = sa.getHostName();
+        int port = sa.getPort();
+        StringBuilder hostBuilder = new StringBuilder();
+        final String hostSpec = hostBuilder.append(hostName)
+                .append(":").append(Integer.toString(port)).toString();
+        final String exclSpec;
+        if (exclusionList == null) {
+            exclSpec = "";
+        } else {
+            StringBuilder listBuilder = new StringBuilder();
+            boolean firstDomain = true;
+            for (String exclDomain : exclusionList) {
+                if (!firstDomain) {
+                    listBuilder = listBuilder.append(",");
+                } else {
+                    firstDomain = false;
+                }
+                listBuilder = listBuilder.append(exclDomain.trim());
+            }
+            exclSpec = listBuilder.toString();
+        }
+        if (android.net.Proxy.validate(hostName, Integer.toString(port), exclSpec)
+                != android.net.Proxy.PROXY_VALID) throw new IllegalArgumentException();
+
+        return new Pair<>(hostSpec, exclSpec);
+    }
+
+    /**
      * Set a network-independent global HTTP proxy. This is not normally what you want for typical
      * HTTP proxies - they are generally network dependent. However if you're doing something
      * unusual like general internal filtering this may be useful. On a private network where the
@@ -5383,6 +5509,26 @@
             "android.app.action.ACTION_SHOW_NEW_USER_DISCLAIMER";
 
     /**
+     * Broadcast action: notify managed provisioning that the device has been provisioned.
+     *
+     * @hide
+     */
+    @TestApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PROVISIONED_MANAGED_DEVICE =
+            "android.app.action.PROVISIONED_MANAGED_DEVICE";
+
+    /**
+     * Broadcast action: notify managed provisioning that a new managed profile is created.
+     *
+     * @hide
+     */
+    @TestApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MANAGED_PROFILE_CREATED =
+            "android.app.action.MANAGED_PROFILE_CREATED";
+
+    /**
      * Widgets are enabled in keyguard
      */
     public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0;
@@ -10598,8 +10744,9 @@
      * @return A {@link ProvisioningPreCondition} value indicating whether provisioning is allowed.
      * @hide
      */
+    @TestApi
     public @ProvisioningPreCondition int checkProvisioningPreCondition(
-            String action, @NonNull String packageName) {
+            @Nullable String action, @NonNull String packageName) {
         try {
             return mService.checkProvisioningPreCondition(action, packageName);
         } catch (RemoteException re) {
@@ -13101,4 +13248,70 @@
             throw re.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Creates and provisions a managed profile and sets the
+     * {@link ManagedProfileProvisioningParams#getProfileAdminComponentName()} as the profile
+     * owner.
+     *
+     * <p>The method {@link #checkProvisioningPreCondition} must be returning {@link #CODE_OK}
+     * before calling this method.
+     *
+     * @param provisioningParams Params required to provision a managed profile,
+     * see {@link ManagedProfileProvisioningParams}.
+     * @return The {@link UserHandle} of the created profile or {@code null} if the service is
+     * not available.
+     * @throws SecurityException if the caller does not hold
+     * {@link android.Manifest.permission#MANAGE_PROFILE_AND_DEVICE_OWNERS}.
+     * @throws ProvisioningException if an error occurred during provisioning.
+     * @hide
+     */
+    @Nullable
+    @TestApi
+    public UserHandle createAndProvisionManagedProfile(
+            @NonNull ManagedProfileProvisioningParams provisioningParams)
+            throws ProvisioningException {
+        if (mService == null) {
+            return null;
+        }
+        try {
+            return mService.createAndProvisionManagedProfile(provisioningParams);
+        } catch (ServiceSpecificException e) {
+            throw new ProvisioningException(e, e.errorCode);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Provisions a managed device and sets the {@code deviceAdminComponentName} as the device
+     * owner.
+     *
+     * <p>The method {@link #checkProvisioningPreCondition} must be returning {@link #CODE_OK}
+     * before calling this method.
+     *
+     * @param provisioningParams Params required to provision a fully managed device,
+     * see {@link FullyManagedDeviceProvisioningParams}.
+     *
+     * @throws SecurityException if the caller does not hold
+     * {@link android.Manifest.permission#MANAGE_PROFILE_AND_DEVICE_OWNERS}.
+     * @throws ProvisioningException if an error occurred during provisioning.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+    public void provisionFullyManagedDevice(
+            @NonNull FullyManagedDeviceProvisioningParams provisioningParams)
+            throws ProvisioningException {
+        if (mService != null) {
+            try {
+                mService.provisionFullyManagedDevice(provisioningParams);
+            } catch (ServiceSpecificException e) {
+                throw new ProvisioningException(e, e.errorCode);
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+    }
 }
diff --git a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.aidl b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.aidl
new file mode 100644
index 0000000..8a9cbba
--- /dev/null
+++ b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+parcelable FullyManagedDeviceProvisioningParams;
diff --git a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
new file mode 100644
index 0000000..83af019
--- /dev/null
+++ b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Locale;
+
+/**
+ * Params required to provision a fully managed device, see
+ * {@link DevicePolicyManager#provisionFullyManagedDevice}.
+ * @hide
+ */
+@TestApi
+public final class FullyManagedDeviceProvisioningParams implements Parcelable {
+    @NonNull private final ComponentName mDeviceAdminComponentName;
+    @NonNull private final String mOwnerName;
+    private final boolean mLeaveAllSystemAppsEnabled;
+    @Nullable private final String mTimeZone;
+    private final long mLocalTime;
+    @SuppressLint("UseIcu")
+    @Nullable private final Locale mLocale;
+
+    private FullyManagedDeviceProvisioningParams(
+            @NonNull ComponentName deviceAdminComponentName,
+            @NonNull String ownerName,
+            boolean leaveAllSystemAppsEnabled,
+            @Nullable String timeZone,
+            long localTime,
+            @Nullable @SuppressLint("UseIcu") Locale locale) {
+        this.mDeviceAdminComponentName = requireNonNull(deviceAdminComponentName);
+        this.mOwnerName = requireNonNull(ownerName);
+        this.mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled;
+        this.mTimeZone = timeZone;
+        this.mLocalTime = localTime;
+        this.mLocale = locale;
+    }
+
+    private FullyManagedDeviceProvisioningParams(
+            @NonNull ComponentName deviceAdminComponentName,
+            @NonNull String ownerName,
+            boolean leaveAllSystemAppsEnabled,
+            @Nullable String timeZone,
+            long localTime,
+            @Nullable String localeStr) {
+        this(deviceAdminComponentName,
+                ownerName,
+                leaveAllSystemAppsEnabled,
+                timeZone,
+                localTime,
+                getLocale(localeStr));
+    }
+
+    @Nullable
+    private static Locale getLocale(String localeStr) {
+        return localeStr == null ? null : Locale.forLanguageTag(localeStr);
+    }
+
+    @NonNull
+    public ComponentName getDeviceAdminComponentName() {
+        return mDeviceAdminComponentName;
+    }
+
+    @NonNull
+    public String getOwnerName() {
+        return mOwnerName;
+    }
+
+    public boolean isLeaveAllSystemAppsEnabled() {
+        return mLeaveAllSystemAppsEnabled;
+    }
+
+    @Nullable
+    public String getTimeZone() {
+        return mTimeZone;
+    }
+
+    public long getLocalTime() {
+        return mLocalTime;
+    }
+
+    @Nullable
+    public @SuppressLint("UseIcu") Locale getLocale() {
+        return mLocale;
+    }
+
+    /**
+     * Builder class for {@link FullyManagedDeviceProvisioningParams} objects.
+     */
+    public static final class Builder {
+        @NonNull private final ComponentName mDeviceAdminComponentName;
+        @NonNull private final String mOwnerName;
+        private boolean mLeaveAllSystemAppsEnabled;
+        @Nullable private String mTimeZone;
+        private long mLocalTime;
+        @SuppressLint("UseIcu")
+        @Nullable private Locale mLocale;
+
+        /**
+         * Initialize a new {@link Builder} to construct a
+         * {@link FullyManagedDeviceProvisioningParams}.
+         * <p>
+         * See {@link DevicePolicyManager#provisionFullyManagedDevice}
+         *
+         * @param deviceAdminComponentName The admin {@link ComponentName} to be set as the device
+         * owner.
+         * @param ownerName The name of the device owner.
+         *
+         * @throws NullPointerException if {@code deviceAdminComponentName} or
+         * {@code ownerName} are null.
+         */
+        public Builder(
+                @NonNull ComponentName deviceAdminComponentName, @NonNull String ownerName) {
+            this.mDeviceAdminComponentName = requireNonNull(deviceAdminComponentName);
+            this.mOwnerName = requireNonNull(ownerName);
+        }
+
+        /**
+         * Sets whether non-required system apps should be installed on
+         * the created profile when
+         * {@link DevicePolicyManager#provisionFullyManagedDevice}
+         * is called. Defaults to {@code false} if not set.
+         */
+        @NonNull
+        public Builder setLeaveAllSystemAppsEnabled(boolean leaveAllSystemAppsEnabled) {
+            this.mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled;
+            return this;
+        }
+
+        /**
+         * Sets {@code timeZone} on the device. If not set or set to {@code null},
+         * {@link DevicePolicyManager#provisionFullyManagedDevice} will not set a timezone
+         */
+        @NonNull
+        public Builder setTimeZone(@Nullable String timeZone) {
+            this.mTimeZone = timeZone;
+            return this;
+        }
+
+        /**
+         * Sets {@code localTime} on the device, If not set or set to
+         * {@code 0}, {@link DevicePolicyManager#provisionFullyManagedDevice} will not set a
+         * local time.
+         */
+        @NonNull
+        public Builder setLocalTime(long localTime) {
+            this.mLocalTime = localTime;
+            return this;
+        }
+
+        /**
+         * Sets {@link Locale} on the device, If not set or set to {@code null},
+         * {@link DevicePolicyManager#provisionFullyManagedDevice} will not set a locale.
+         */
+        @NonNull
+        public Builder setLocale(@SuppressLint("UseIcu") @Nullable Locale locale) {
+            this.mLocale = locale;
+            return this;
+        }
+
+        /**
+         * Combines all of the attributes that have been set on this {@code Builder}
+         *
+         * @return a new {@link FullyManagedDeviceProvisioningParams} object.
+         */
+        @NonNull
+        public FullyManagedDeviceProvisioningParams build() {
+            return new FullyManagedDeviceProvisioningParams(
+                    mDeviceAdminComponentName,
+                    mOwnerName,
+                    mLeaveAllSystemAppsEnabled,
+                    mTimeZone,
+                    mLocalTime,
+                    mLocale);
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "FullyManagedDeviceProvisioningParams{"
+                + "mDeviceAdminComponentName=" + mDeviceAdminComponentName
+                + ", mOwnerName=" + mOwnerName
+                + ", mLeaveAllSystemAppsEnabled=" + mLeaveAllSystemAppsEnabled
+                + ", mTimeZone=" + (mTimeZone == null ? "null" : mTimeZone)
+                + ", mLocalTime=" + mLocalTime
+                + ", mLocale=" + (mLocale == null ? "null" : mLocale)
+                + '}';
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, @Nullable int flags) {
+        dest.writeTypedObject(mDeviceAdminComponentName, flags);
+        dest.writeString(mOwnerName);
+        dest.writeBoolean(mLeaveAllSystemAppsEnabled);
+        dest.writeString(mTimeZone);
+        dest.writeLong(mLocalTime);
+        dest.writeString(mLocale == null ? null : mLocale.toLanguageTag());
+    }
+
+    @NonNull
+    public static final Creator<FullyManagedDeviceProvisioningParams> CREATOR =
+            new Creator<FullyManagedDeviceProvisioningParams>() {
+                @Override
+                public FullyManagedDeviceProvisioningParams createFromParcel(Parcel in) {
+                    ComponentName componentName = in.readTypedObject(ComponentName.CREATOR);
+                    String ownerName = in.readString();
+                    boolean leaveAllSystemAppsEnabled = in.readBoolean();
+                    String timeZone = in.readString();
+                    long localtime = in.readLong();
+                    String locale = in.readString();
+
+                    return new FullyManagedDeviceProvisioningParams(
+                            componentName,
+                            ownerName,
+                            leaveAllSystemAppsEnabled,
+                            timeZone,
+                            localtime,
+                            locale);
+                }
+
+                @Override
+                public FullyManagedDeviceProvisioningParams[] newArray(int size) {
+                    return new FullyManagedDeviceProvisioningParams[size];
+                }
+            };
+}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 8f84bfe..3765a67 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -25,6 +25,8 @@
 import android.app.admin.SystemUpdatePolicy;
 import android.app.admin.PasswordMetrics;
 import android.app.admin.FactoryResetProtectionPolicy;
+import android.app.admin.ManagedProfileProvisioningParams;
+import android.app.admin.FullyManagedDeviceProvisioningParams;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -493,4 +495,7 @@
 
     String getEnrollmentSpecificId(String callerPackage);
     void setOrganizationIdForUser(in String callerPackage, in String enterpriseId, int userId);
+
+    UserHandle createAndProvisionManagedProfile(in ManagedProfileProvisioningParams provisioningParams);
+    void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams);
 }
diff --git a/core/java/android/app/admin/ManagedProfileProvisioningParams.aidl b/core/java/android/app/admin/ManagedProfileProvisioningParams.aidl
new file mode 100644
index 0000000..a6fae91
--- /dev/null
+++ b/core/java/android/app/admin/ManagedProfileProvisioningParams.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+parcelable ManagedProfileProvisioningParams;
diff --git a/core/java/android/app/admin/ManagedProfileProvisioningParams.java b/core/java/android/app/admin/ManagedProfileProvisioningParams.java
new file mode 100644
index 0000000..5fe63d1
--- /dev/null
+++ b/core/java/android/app/admin/ManagedProfileProvisioningParams.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import static java.util.Objects.requireNonNull;
+
+import android.accounts.Account;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Params required to provision a managed profile, see
+ * {@link DevicePolicyManager#createAndProvisionManagedProfile}.
+ *
+ * @hide
+ */
+@TestApi
+public final class ManagedProfileProvisioningParams implements Parcelable {
+    @NonNull private final ComponentName mProfileAdminComponentName;
+    @NonNull private final String mOwnerName;
+    @Nullable private final String mProfileName;
+    @Nullable private final Account mAccountToMigrate;
+    private final boolean mLeaveAllSystemAppsEnabled;
+    private final boolean mOrganizationOwnedProvisioning;
+    private final boolean mKeepAccountMigrated;
+
+
+    private ManagedProfileProvisioningParams(
+            @NonNull ComponentName profileAdminComponentName,
+            @NonNull String ownerName,
+            @Nullable String profileName,
+            @Nullable Account accountToMigrate,
+            boolean leaveAllSystemAppsEnabled,
+            boolean organizationOwnedProvisioning,
+            boolean keepAccountMigrated) {
+        this.mProfileAdminComponentName = requireNonNull(profileAdminComponentName);
+        this.mOwnerName = requireNonNull(ownerName);
+        this.mProfileName = profileName;
+        this.mAccountToMigrate = accountToMigrate;
+        this.mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled;
+        this.mOrganizationOwnedProvisioning = organizationOwnedProvisioning;
+        this.mKeepAccountMigrated = keepAccountMigrated;
+    }
+
+    @NonNull
+    public ComponentName getProfileAdminComponentName() {
+        return mProfileAdminComponentName;
+    }
+
+    @NonNull
+    public String getOwnerName() {
+        return mOwnerName;
+    }
+
+    @Nullable
+    public String getProfileName() {
+        return mProfileName;
+    }
+
+    @Nullable
+    public Account getAccountToMigrate() {
+        return mAccountToMigrate;
+    }
+
+    public boolean isLeaveAllSystemAppsEnabled() {
+        return mLeaveAllSystemAppsEnabled;
+    }
+
+    public boolean isOrganizationOwnedProvisioning() {
+        return mOrganizationOwnedProvisioning;
+    }
+
+    public boolean isKeepAccountMigrated() {
+        return mKeepAccountMigrated;
+    }
+
+    /**
+     * Builder class for {@link ManagedProfileProvisioningParams} objects.
+     */
+    public static final class Builder {
+        @NonNull private final ComponentName mProfileAdminComponentName;
+        @NonNull private final String mOwnerName;
+        @Nullable private String mProfileName;
+        @Nullable private Account mAccountToMigrate;
+        private boolean mLeaveAllSystemAppsEnabled;
+        private boolean mOrganizationOwnedProvisioning;
+        private boolean mKeepAccountMigrated;
+
+        /**
+         * Initialize a new {@link Builder) to construct a {@link ManagedProfileProvisioningParams}.
+         * <p>
+         * See {@link DevicePolicyManager#createAndProvisionManagedProfile}
+         *
+         * @param profileAdminComponentName The admin {@link ComponentName} to be set as the profile
+         * owner.
+         * @param ownerName The name of the profile owner.
+         *
+         * @throws NullPointerException if {@code profileAdminComponentName} or
+         * {@code ownerName} are null.
+         */
+        public Builder(
+                @NonNull ComponentName profileAdminComponentName, @NonNull String ownerName) {
+            requireNonNull(profileAdminComponentName);
+            requireNonNull(ownerName);
+            this.mProfileAdminComponentName = profileAdminComponentName;
+            this.mOwnerName = ownerName;
+        }
+
+        /**
+         * Sets the profile name of the created profile when
+         * {@link DevicePolicyManager#createAndProvisionManagedProfile} is called. Defaults to
+         * {@code null} if not set.
+         */
+        @NonNull
+        public Builder setProfileName(@Nullable String profileName) {
+            this.mProfileName = profileName;
+            return this;
+        }
+
+        /**
+         * Sets the {@link Account} to migrate from the parent profile to the created profile when
+         * {@link DevicePolicyManager#createAndProvisionManagedProfile} is called. If not set, or
+         * set to {@code null}, no accounts will be migrated.
+         */
+        @NonNull
+        public Builder setAccountToMigrate(@Nullable Account accountToMigrate) {
+            this.mAccountToMigrate = accountToMigrate;
+            return this;
+        }
+
+        /**
+         * Sets whether non-required system apps should be installed on
+         * the created profile when {@link DevicePolicyManager#createAndProvisionManagedProfile}
+         * is called. Defaults to {@code false} if not set.
+         */
+        @NonNull
+        public Builder setLeaveAllSystemAppsEnabled(boolean leaveAllSystemAppsEnabled) {
+            this.mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled;
+            return this;
+        }
+
+        /**
+         * Sets if this device is owned by an organization. Defaults to {@code false}
+         * if not set.
+         */
+        @NonNull
+        public Builder setOrganizationOwnedProvisioning(boolean organizationOwnedProvisioning) {
+            this.mOrganizationOwnedProvisioning = organizationOwnedProvisioning;
+            return this;
+        }
+
+        /**
+         * Sets whether to keep the account on the parent profile during account migration.
+         * Defaults to {@code false}.
+         */
+        @NonNull
+        public Builder setKeepAccountMigrated(boolean keepAccountMigrated) {
+            this.mKeepAccountMigrated = keepAccountMigrated;
+            return this;
+        }
+
+        /**
+         * Combines all of the attributes that have been set on this {@code Builder}.
+         *
+         * @return a new {@link ManagedProfileProvisioningParams} object.
+         */
+        @NonNull
+        public ManagedProfileProvisioningParams build() {
+            return new ManagedProfileProvisioningParams(
+                    mProfileAdminComponentName,
+                    mOwnerName,
+                    mProfileName,
+                    mAccountToMigrate,
+                    mLeaveAllSystemAppsEnabled,
+                    mOrganizationOwnedProvisioning,
+                    mKeepAccountMigrated);
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "ManagedProfileProvisioningParams{"
+                + "mProfileAdminComponentName=" + mProfileAdminComponentName
+                + ", mOwnerName=" + mOwnerName
+                + ", mProfileName=" + (mProfileName == null ? "null" : mProfileName)
+                + ", mAccountToMigrate=" + (mAccountToMigrate == null ? "null" : mAccountToMigrate)
+                + ", mLeaveAllSystemAppsEnabled=" + mLeaveAllSystemAppsEnabled
+                + ", mOrganizationOwnedProvisioning=" + mOrganizationOwnedProvisioning
+                + ", mKeepAccountMigrated=" + mKeepAccountMigrated
+                + '}';
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, @Nullable int flags) {
+        dest.writeTypedObject(mProfileAdminComponentName, flags);
+        dest.writeString(mOwnerName);
+        dest.writeString(mProfileName);
+        dest.writeTypedObject(mAccountToMigrate, flags);
+        dest.writeBoolean(mLeaveAllSystemAppsEnabled);
+        dest.writeBoolean(mOrganizationOwnedProvisioning);
+        dest.writeBoolean(mKeepAccountMigrated);
+    }
+
+    public static final @NonNull Creator<ManagedProfileProvisioningParams> CREATOR =
+            new Creator<ManagedProfileProvisioningParams>() {
+                @Override
+                public ManagedProfileProvisioningParams createFromParcel(Parcel in) {
+                    ComponentName componentName = in.readTypedObject(ComponentName.CREATOR);
+                    String ownerName = in.readString();
+                    String profileName = in.readString();
+                    Account account = in.readTypedObject(Account.CREATOR);
+                    boolean leaveAllSystemAppsEnabled = in.readBoolean();
+                    boolean organizationOwnedProvisioning = in.readBoolean();
+                    boolean keepAccountMigrated = in.readBoolean();
+
+                    return new ManagedProfileProvisioningParams(
+                            componentName,
+                            ownerName,
+                            profileName,
+                            account,
+                            leaveAllSystemAppsEnabled,
+                            organizationOwnedProvisioning,
+                            keepAccountMigrated);
+                }
+
+                @Override
+                public ManagedProfileProvisioningParams[] newArray(int size) {
+                    return new ManagedProfileProvisioningParams[size];
+                }
+            };
+}
diff --git a/core/java/android/app/admin/ProvisioningException.java b/core/java/android/app/admin/ProvisioningException.java
new file mode 100644
index 0000000..639859b
--- /dev/null
+++ b/core/java/android/app/admin/ProvisioningException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.util.AndroidException;
+
+/**
+ * Thrown to indicate a failure during {@link DevicePolicyManager#provisionFullyManagedDevice} and
+ * {@link DevicePolicyManager#createAndProvisionManagedProfile}.
+ *
+ * @hide
+ *
+ */
+@TestApi
+public class ProvisioningException extends AndroidException {
+    private final @DevicePolicyManager.ProvisioningResult int mProvisioningResult;
+
+    public ProvisioningException(@NonNull Exception cause,
+            @DevicePolicyManager.ProvisioningResult int provisioningResult) {
+        super(cause);
+        mProvisioningResult = provisioningResult;
+    }
+
+    public @DevicePolicyManager.ProvisioningResult int getProvisioningResult() {
+        return mProvisioningResult;
+    }
+}
diff --git a/core/java/android/appwidget/OWNERS b/core/java/android/appwidget/OWNERS
new file mode 100644
index 0000000..439df4b
--- /dev/null
+++ b/core/java/android/appwidget/OWNERS
@@ -0,0 +1,3 @@
+pinyaoting@google.com
+suprabh@google.com
+sunnygoyal@google.com
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 15daf1c..cd91aa9 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -225,6 +225,39 @@
     @SystemApi
     public static final int OPTIONAL_CODECS_PREF_ENABLED = 1;
 
+    /** @hide */
+    @IntDef(prefix = "DYNAMIC_BUFFER_SUPPORT_", value = {
+            DYNAMIC_BUFFER_SUPPORT_NONE,
+            DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD,
+            DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    /**
+     * Indicates the supported type of Dynamic Audio Buffer is not supported.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int DYNAMIC_BUFFER_SUPPORT_NONE = 0;
+
+    /**
+     * Indicates the supported type of Dynamic Audio Buffer is A2DP offload.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD = 1;
+
+    /**
+     * Indicates the supported type of Dynamic Audio Buffer is A2DP software encoding.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING = 2;
+
     private BluetoothAdapter mAdapter;
     private final BluetoothProfileConnector<IBluetoothA2dp> mProfileConnector =
             new BluetoothProfileConnector(this, BluetoothProfile.A2DP, "BluetoothA2dp",
@@ -845,6 +878,87 @@
     }
 
     /**
+     * Get the supported type of the Dynamic Audio Buffer.
+     * <p>Possible return values are
+     * {@link #DYNAMIC_BUFFER_SUPPORT_NONE},
+     * {@link #DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD},
+     * {@link #DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING}.
+     *
+     * @return supported type of Dynamic Audio Buffer feature
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public @Type int getDynamicBufferSupport() {
+        if (VDBG) log("getDynamicBufferSupport()");
+        try {
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled()) {
+                return service.getDynamicBufferSupport();
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return DYNAMIC_BUFFER_SUPPORT_NONE;
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to get getDynamicBufferSupport, error: ", e);
+            return DYNAMIC_BUFFER_SUPPORT_NONE;
+        }
+    }
+
+    /**
+     * Return the record of {@link BufferConstraints} object that
+     * has the default/maximum/minimum audio buffer. This can be used to inform what the controller
+     * has support for the audio buffer.
+     *
+     * @return a record with {@link BufferConstraints} or null if report is unavailable
+     * or unsupported
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public @Nullable BufferConstraints getBufferConstraints() {
+        if (VDBG) log("getBufferConstraints()");
+        try {
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled()) {
+                return service.getBufferConstraints();
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return null;
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+            return null;
+        }
+    }
+
+    /**
+     * Set Dynamic Audio Buffer Size.
+     *
+     * @param codec audio codec
+     * @param value buffer millis
+     * @return true to indicate success, or false on immediate error
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public boolean setBufferMillis(@BluetoothCodecConfig.SourceCodecType int codec, int value) {
+        if (VDBG) log("setBufferMillis(" + codec + ", " + value + ")");
+        try {
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled()) {
+                return service.setBufferMillis(codec, value);
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+            return false;
+        }
+    }
+
+    /**
      * Helper for converting a state to a string.
      *
      * For debug use only - strings are not internationalized.
diff --git a/core/java/android/bluetooth/BufferConstraint.java b/core/java/android/bluetooth/BufferConstraint.java
new file mode 100644
index 0000000..cbffc78
--- /dev/null
+++ b/core/java/android/bluetooth/BufferConstraint.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Stores a codec's constraints on buffering length in milliseconds.
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class BufferConstraint implements Parcelable {
+
+    private static final String TAG = "BufferConstraint";
+    private int mDefaultMillis;
+    private int mMaxMillis;
+    private int mMinMillis;
+
+    public BufferConstraint(int defaultMillis, int maxMillis,
+            int minMillis) {
+        mDefaultMillis = defaultMillis;
+        mMaxMillis = maxMillis;
+        mMinMillis = minMillis;
+    }
+
+    BufferConstraint(Parcel in) {
+        mDefaultMillis = in.readInt();
+        mMaxMillis = in.readInt();
+        mMinMillis = in.readInt();
+    }
+
+    public static final @NonNull Parcelable.Creator<BufferConstraint> CREATOR =
+            new Parcelable.Creator<BufferConstraint>() {
+                public BufferConstraint createFromParcel(Parcel in) {
+                    return new BufferConstraint(in);
+                }
+
+                public BufferConstraint[] newArray(int size) {
+                    return new BufferConstraint[size];
+                }
+            };
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mDefaultMillis);
+        out.writeInt(mMaxMillis);
+        out.writeInt(mMinMillis);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Get the default buffer millis
+     *
+     * @return default buffer millis
+     * @hide
+     */
+    @SystemApi
+    public int getDefaultMillis() {
+        return mDefaultMillis;
+    }
+
+    /**
+     * Get the maximum buffer millis
+     *
+     * @return maximum buffer millis
+     * @hide
+     */
+    @SystemApi
+    public int getMaxMillis() {
+        return mMaxMillis;
+    }
+
+    /**
+     * Get the minimum buffer millis
+     *
+     * @return minimum buffer millis
+     * @hide
+     */
+    @SystemApi
+    public int getMinMillis() {
+        return mMinMillis;
+    }
+}
diff --git a/core/java/android/bluetooth/BufferConstraints.java b/core/java/android/bluetooth/BufferConstraints.java
new file mode 100644
index 0000000..7e5ec1e
--- /dev/null
+++ b/core/java/android/bluetooth/BufferConstraints.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * A parcelable collection of buffer constraints by codec type.
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class BufferConstraints implements Parcelable {
+    public static final int BUFFER_CODEC_MAX_NUM = 32;
+
+    private static final String TAG = "BufferConstraints";
+
+    private Map<Integer, BufferConstraint> mBufferConstraints;
+    private List<BufferConstraint> mBufferConstraintList;
+
+    public BufferConstraints(@NonNull List<BufferConstraint>
+            bufferConstraintList) {
+
+        mBufferConstraintList = new ArrayList<BufferConstraint>(bufferConstraintList);
+        mBufferConstraints = new HashMap<Integer, BufferConstraint>();
+        for (int i = 0; i < BUFFER_CODEC_MAX_NUM; i++) {
+            mBufferConstraints.put(i, bufferConstraintList.get(i));
+        }
+    }
+
+    BufferConstraints(Parcel in) {
+        mBufferConstraintList = new ArrayList<BufferConstraint>();
+        mBufferConstraints = new HashMap<Integer, BufferConstraint>();
+        in.readList(mBufferConstraintList, BufferConstraint.class.getClassLoader());
+        for (int i = 0; i < mBufferConstraintList.size(); i++) {
+            mBufferConstraints.put(i, mBufferConstraintList.get(i));
+        }
+    }
+
+    public static final @NonNull Parcelable.Creator<BufferConstraints> CREATOR =
+            new Parcelable.Creator<BufferConstraints>() {
+                public BufferConstraints createFromParcel(Parcel in) {
+                    return new BufferConstraints(in);
+                }
+
+                public BufferConstraints[] newArray(int size) {
+                    return new BufferConstraints[size];
+                }
+            };
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeList(mBufferConstraintList);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Get the buffer constraints by codec type.
+     *
+     * @param codec Audio codec
+     * @return buffer constraints by codec type.
+     * @hide
+     */
+    @SystemApi
+    public @Nullable BufferConstraint getCodec(@BluetoothCodecConfig.SourceCodecType int codec) {
+        return mBufferConstraints.get(codec);
+    }
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2190140..e4a81cf 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2625,6 +2625,36 @@
     public abstract void sendStickyBroadcast(@RequiresPermission Intent intent);
 
     /**
+     * <p>Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the
+     * Intent you are sending stays around after the broadcast is complete,
+     * so that others can quickly retrieve that data through the return
+     * value of {@link #registerReceiver(BroadcastReceiver, IntentFilter)}.  In
+     * all other ways, this behaves the same as
+     * {@link #sendBroadcast(Intent)}.
+     *
+     * @deprecated Sticky broadcasts should not be used.  They provide no security (anyone
+     * can access them), no protection (anyone can modify them), and many other problems.
+     * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+     * has changed, with another mechanism for apps to retrieve the current value whenever
+     * desired.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     * Intent will receive the broadcast, and the Intent will be held to
+     * be re-broadcast to future receivers.
+     * @param options (optional) Additional sending options, generated from a
+     * {@link android.app.BroadcastOptions}.
+     *
+     * @see #sendBroadcast(Intent)
+     * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY)
+    public void sendStickyBroadcast(@RequiresPermission @NonNull Intent intent,
+            @Nullable Bundle options) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
      * <p>Version of {@link #sendStickyBroadcast} that allows you to
      * receive data back from the broadcast.  This is accomplished by
      * supplying your own BroadcastReceiver when calling, which will be
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 078fa0d..c1c213e 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -624,6 +624,35 @@
         mBase.sendStickyBroadcast(intent);
     }
 
+    /**
+     * <p>Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the
+     * Intent you are sending stays around after the broadcast is complete,
+     * so that others can quickly retrieve that data through the return
+     * value of {@link #registerReceiver(BroadcastReceiver, IntentFilter)}.  In
+     * all other ways, this behaves the same as
+     * {@link #sendBroadcast(Intent)}.
+     *
+     * @deprecated Sticky broadcasts should not be used.  They provide no security (anyone
+     * can access them), no protection (anyone can modify them), and many other problems.
+     * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+     * has changed, with another mechanism for apps to retrieve the current value whenever
+     * desired.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     * Intent will receive the broadcast, and the Intent will be held to
+     * be re-broadcast to future receivers.
+     * @param options (optional) Additional sending options, generated from a
+     * {@link android.app.BroadcastOptions}.
+     *
+     * @see #sendBroadcast(Intent)
+     * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
+     */
+    @Override
+    @Deprecated
+    public void sendStickyBroadcast(@NonNull Intent intent, @Nullable Bundle options) {
+        mBase.sendStickyBroadcast(intent, options);
+    }
+
     @Override
     @Deprecated
     public void sendStickyOrderedBroadcast(Intent intent,
diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl
index 44b5c44..0b950b4 100644
--- a/core/java/android/content/om/IOverlayManager.aidl
+++ b/core/java/android/content/om/IOverlayManager.aidl
@@ -17,6 +17,7 @@
 package android.content.om;
 
 import android.content.om.OverlayInfo;
+import android.content.om.OverlayManagerTransaction;
 
 /**
  * Api for getting information about overlay packages.
@@ -163,4 +164,18 @@
      * @param packageName The name of the overlay package whose idmap should be deleted.
      */
     void invalidateCachesForOverlay(in String packageName, in int userIs);
+
+    /**
+     * Perform a series of requests related to overlay packages. This is an
+     * atomic operation: either all requests were performed successfully and
+     * the changes were propagated to the rest of the system, or at least one
+     * request could not be performed successfully and nothing is changed and
+     * nothing is propagated to the rest of the system.
+     *
+     * @see OverlayManagerTransaction
+     *
+     * @param transaction the series of overlay related requests to perform
+     * @throws SecurityException if the transaction failed
+     */
+    void commit(in OverlayManagerTransaction transaction);
 }
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index 217f637c..7c14c28 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -254,6 +254,29 @@
     }
 
     /**
+     * Perform a series of requests related to overlay packages. This is an
+     * atomic operation: either all requests were performed successfully and
+     * the changes were propagated to the rest of the system, or at least one
+     * request could not be performed successfully and nothing is changed and
+     * nothing is propagated to the rest of the system.
+     *
+     * @see OverlayManagerTransaction
+     *
+     * @param transaction the series of overlay related requests to perform
+     * @throws Exception if not all the requests could be successfully and
+     *         atomically executed
+     *
+     * @hide
+     */
+    public void commit(@NonNull final OverlayManagerTransaction transaction) {
+        try {
+            mService.commit(transaction);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Starting on R, actor enforcement and app visibility changes introduce additional failure
      * cases, but the SecurityException thrown with these checks is unexpected for existing
      * consumers of the API.
diff --git a/core/java/android/content/om/OverlayManagerTransaction.aidl b/core/java/android/content/om/OverlayManagerTransaction.aidl
new file mode 100644
index 0000000..6715c82
--- /dev/null
+++ b/core/java/android/content/om/OverlayManagerTransaction.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.om;
+
+parcelable OverlayManagerTransaction;
diff --git a/core/java/android/content/om/OverlayManagerTransaction.java b/core/java/android/content/om/OverlayManagerTransaction.java
new file mode 100644
index 0000000..1fa8973
--- /dev/null
+++ b/core/java/android/content/om/OverlayManagerTransaction.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.om;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Container for a batch of requests to the OverlayManagerService.
+ *
+ * Transactions are created using a builder interface. Example usage:
+ *
+ * final OverlayManager om = ctx.getSystemService(OverlayManager.class);
+ * final OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
+ *     .setEnabled(...)
+ *     .setEnabled(...)
+ *     .build();
+ * om.commit(t);
+ *
+ * @hide
+ */
+public class OverlayManagerTransaction
+        implements Iterable<OverlayManagerTransaction.Request>, Parcelable {
+    // TODO: remove @hide from this class when OverlayManager is added to the
+    // SDK, but keep OverlayManagerTransaction.Request @hidden
+    private final List<Request> mRequests;
+
+    OverlayManagerTransaction(@NonNull final List<Request> requests) {
+        checkNotNull(requests);
+        if (requests.contains(null)) {
+            throw new IllegalArgumentException("null request");
+        }
+        mRequests = requests;
+    }
+
+    private OverlayManagerTransaction(@NonNull final Parcel source) {
+        final int size = source.readInt();
+        mRequests = new ArrayList<Request>(size);
+        for (int i = 0; i < size; i++) {
+            final int request = source.readInt();
+            final String packageName = source.readString();
+            final int userId = source.readInt();
+            mRequests.add(new Request(request, packageName, userId));
+        }
+    }
+
+    @Override
+    public Iterator<Request> iterator() {
+        return mRequests.iterator();
+    }
+
+    @Override
+    public String toString() {
+        return String.format("OverlayManagerTransaction { mRequests = %s }", mRequests);
+    }
+
+    /**
+     * A single unit of the transaction, such as a request to enable an
+     * overlay, or to disable an overlay.
+     *
+     * @hide
+     */
+    public static class Request {
+        @IntDef(prefix = "TYPE_", value = {
+                TYPE_SET_ENABLED,
+                TYPE_SET_DISABLED,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @interface RequestType {}
+
+        public static final int TYPE_SET_ENABLED = 0;
+        public static final int TYPE_SET_DISABLED = 1;
+
+        @RequestType public final int type;
+        public final String packageName;
+        public final int userId;
+
+        public Request(@RequestType final int type, @NonNull final String packageName,
+                final int userId) {
+            this.type = type;
+            this.packageName = packageName;
+            this.userId = userId;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("Request{type=0x%02x (%s), packageName=%s, userId=%d}",
+                    type, typeToString(), packageName, userId);
+        }
+
+        /**
+         * Translate the request type into a human readable string. Only
+         * intended for debugging.
+         *
+         * @hide
+         */
+        public String typeToString() {
+            switch (type) {
+                case TYPE_SET_ENABLED: return "TYPE_SET_ENABLED";
+                case TYPE_SET_DISABLED: return "TYPE_SET_DISABLED";
+                default: return String.format("TYPE_UNKNOWN (0x%02x)", type);
+            }
+        }
+    }
+
+    /**
+     * Builder class for OverlayManagerTransaction objects.
+     *
+     * @hide
+     */
+    public static class Builder {
+        private final List<Request> mRequests = new ArrayList<>();
+
+        /**
+         * Request that an overlay package be enabled and change its loading
+         * order to the last package to be loaded, or disabled
+         *
+         * If the caller has the correct permissions, it is always possible to
+         * disable an overlay. Due to technical and security reasons it may not
+         * always be possible to enable an overlay, for instance if the overlay
+         * does not successfully overlay any target resources due to
+         * overlayable policy restrictions.
+         *
+         * An enabled overlay is a part of target package's resources, i.e. it will
+         * be part of any lookups performed via {@link android.content.res.Resources}
+         * and {@link android.content.res.AssetManager}. A disabled overlay will no
+         * longer affect the resources of the target package. If the target is
+         * currently running, its outdated resources will be replaced by new ones.
+         *
+         * @param packageName The name of the overlay package.
+         * @param enable true to enable the overlay, false to disable it.
+         * @return this Builder object, so you can chain additional requests
+         */
+        public Builder setEnabled(@NonNull String packageName, boolean enable) {
+            return setEnabled(packageName, enable, UserHandle.myUserId());
+        }
+
+        /**
+         * @hide
+         */
+        public Builder setEnabled(@NonNull String packageName, boolean enable, int userId) {
+            checkNotNull(packageName);
+            @Request.RequestType final int type =
+                enable ? Request.TYPE_SET_ENABLED : Request.TYPE_SET_DISABLED;
+            mRequests.add(new Request(type, packageName, userId));
+            return this;
+        }
+
+        /**
+         * Create a new transaction out of the requests added so far. Execute
+         * the transaction by calling OverlayManager#commit.
+         *
+         * @see OverlayManager#commit
+         * @return a new transaction
+         */
+        public OverlayManagerTransaction build() {
+            return new OverlayManagerTransaction(mRequests);
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        final int size = mRequests.size();
+        dest.writeInt(size);
+        for (int i = 0; i < size; i++) {
+            final Request req = mRequests.get(i);
+            dest.writeInt(req.type);
+            dest.writeString(req.packageName);
+            dest.writeInt(req.userId);
+        }
+    }
+
+    public static final Parcelable.Creator<OverlayManagerTransaction> CREATOR =
+            new Parcelable.Creator<OverlayManagerTransaction>() {
+
+        @Override
+        public OverlayManagerTransaction createFromParcel(Parcel source) {
+            return new OverlayManagerTransaction(source);
+        }
+
+        @Override
+        public OverlayManagerTransaction[] newArray(int size) {
+            return new OverlayManagerTransaction[size];
+        }
+    };
+}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 747f8dc..0433db4 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2589,6 +2589,14 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device is compatible with Android’s security model.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SECURITY_MODEL_COMPATIBLE =
+            "android.hardware.security.model.compatible";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device supports the OpenGL ES
      * <a href="http://www.khronos.org/registry/gles/extensions/ANDROID/ANDROID_extension_pack_es31a.txt">
      * Android Extension Pack</a>.
diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
index fcdf61e..c854ac98 100644
--- a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
@@ -38,7 +38,7 @@
     SensorPropertiesInternal getSensorProperties(String opPackageName);
 
     // Requests a proto dump of the sensor. See biometrics.proto
-    byte[] dumpSensorServiceStateProto();
+    byte[] dumpSensorServiceStateProto(boolean clearSchedulerBuffer);
 
     // This method prepares the service to start authenticating, but doesn't start authentication.
     // This is protected by the MANAGE_BIOMETRIC signature permission. This method should only be
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 5a03ade..421a07b 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -47,28 +47,40 @@
      * begins adjusting the power state to match what was requested.
      * </p>
      *
+     * @param groupId The identifier for the display group being requested to change power state
      * @param request The requested power state.
-     * @param waitForNegativeProximity If true, issues a request to wait for
+     * @param waitForNegativeProximity If {@code true}, issues a request to wait for
      * negative proximity before turning the screen back on, assuming the screen
      * was turned off by the proximity sensor.
-     * @return True if display is ready, false if there are important changes that must
-     * be made asynchronously (such as turning the screen on), in which case the caller
-     * should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()}
-     * then try the request again later until the state converges.
+     * @return {@code true} if display group is ready, {@code false} if there are important
+     * changes that must be made asynchronously (such as turning the screen on), in which case
+     * the caller should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged}
+     * then try the request again later until the state converges. If the provided {@code groupId}
+     * cannot be found then {@code true} will be returned.
      */
-    public abstract boolean requestPowerState(DisplayPowerRequest request,
+    public abstract boolean requestPowerState(int groupId, DisplayPowerRequest request,
             boolean waitForNegativeProximity);
 
     /**
-     * Returns true if the proximity sensor screen-off function is available.
+     * Returns {@code true} if the proximity sensor screen-off function is available.
      */
     public abstract boolean isProximitySensorAvailable();
 
     /**
-     * Returns the id of the {@link com.android.server.display.DisplayGroup} to which the provided
-     * display belongs.
+     * Registers a display group listener which will be informed of the addition, removal, or change
+     * of display groups.
+     *
+     * @param listener The listener to register.
      */
-    public abstract int getDisplayGroupId(int displayId);
+    public abstract void registerDisplayGroupListener(DisplayGroupListener listener);
+
+    /**
+     * Unregisters a display group listener which will be informed of the addition, removal, or
+     * change of display groups.
+     *
+     * @param listener The listener to unregister.
+     */
+    public abstract void unregisterDisplayGroupListener(DisplayGroupListener listener);
 
     /**
      * Screenshot for internal system-only use such as rotation, etc.  This method includes
@@ -451,7 +463,7 @@
         void onStateChanged();
         void onProximityPositive();
         void onProximityNegative();
-        void onDisplayStateChange(int state); // one of the Display state constants
+        void onDisplayStateChange(boolean allInactive, boolean allOff);
 
         void acquireSuspendBlocker();
         void releaseSuspendBlocker();
@@ -465,4 +477,33 @@
     public interface DisplayTransactionListener {
         void onDisplayTransaction(Transaction t);
     }
+
+    /**
+     * Called when there are changes to {@link com.android.server.display.DisplayGroup
+     * DisplayGroups}.
+     */
+    public interface DisplayGroupListener {
+        /**
+         * A new display group with the provided {@code groupId} was added.
+         * This is guaranteed to be called <i>before</i> any corresponding calls to
+         * {@link android.hardware.display.DisplayManager.DisplayListener} are made.
+         */
+        void onDisplayGroupAdded(int groupId);
+
+        /**
+         * The display group with the provided {@code groupId} was removed.
+         *
+         * This is guaranteed to be called <i>after</i> any corresponding calls to
+         * {@link android.hardware.display.DisplayManager.DisplayListener} are made.
+         */
+        void onDisplayGroupRemoved(int groupId);
+
+        /**
+         * The display group with the provided {@code groupId} has changed.
+         *
+         * This is guaranteed to be called <i>after</i> any corresponding calls to
+         * {@link android.hardware.display.DisplayManager.DisplayListener} are made.
+         */
+        void onDisplayGroupChanged(int groupId);
+    }
 }
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 3b19f12..1b188e8 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -35,7 +35,7 @@
     ITestSession createTestSession(int sensorId, String opPackageName);
 
     // Requests a proto dump of the specified sensor
-    byte[] dumpSensorServiceStateProto(int sensorId);
+    byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer);
 
     // Retrieve static sensor properties for all face sensors
     List<FaceSensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName);
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 74c5b58..3657a83 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -36,7 +36,7 @@
     ITestSession createTestSession(int sensorId, String opPackageName);
 
     // Requests a proto dump of the specified sensor
-    byte[] dumpSensorServiceStateProto(int sensorId);
+    byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer);
 
     // Retrieve static sensor properties for all fingerprint sensors
     List<FingerprintSensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 899af5a..2e45ed8 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -19,6 +19,7 @@
 import static android.net.NetworkRequest.Type.LISTEN;
 import static android.net.NetworkRequest.Type.REQUEST;
 import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
+import static android.net.QosCallback.QosCallbackRegistrationException;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
@@ -29,7 +30,6 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.app.PendingIntent;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -4823,6 +4823,8 @@
     /**
      * Simulates a Data Stall for the specified Network.
      *
+     * <p>This method should only be used for tests.
+     *
      * <p>The caller must be the owner of the specified Network.
      *
      * @param detectionMethod The detection method used to identify the Data Stall.
@@ -4832,7 +4834,7 @@
      * @throws SecurityException if the caller is not the owner of the given network.
      * @hide
      */
-    @TestApi
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_TEST_NETWORKS,
             android.Manifest.permission.NETWORK_STACK})
     public void simulateDataStall(int detectionMethod, long timestampMillis,
@@ -4848,4 +4850,118 @@
         Log.d(TAG, "setOemNetworkPreference called with preference: "
                 + preference.toString());
     }
+
+    @NonNull
+    private final List<QosCallbackConnection> mQosCallbackConnections = new ArrayList<>();
+
+    /**
+     * Registers a {@link QosSocketInfo} with an associated {@link QosCallback}.  The callback will
+     * receive available QoS events related to the {@link Network} and local ip + port
+     * specified within socketInfo.
+     * <p/>
+     * The same {@link QosCallback} must be unregistered before being registered a second time,
+     * otherwise {@link QosCallbackRegistrationException} is thrown.
+     * <p/>
+     * This API does not, in itself, require any permission if called with a network that is not
+     * restricted. However, the underlying implementation currently only supports the IMS network,
+     * which is always restricted. That means non-preinstalled callers can't possibly find this API
+     * useful, because they'd never be called back on networks that they would have access to.
+     *
+     * @throws SecurityException if {@link QosSocketInfo#getNetwork()} is restricted and the app is
+     * missing CONNECTIVITY_USE_RESTRICTED_NETWORKS permission.
+     * @throws QosCallback.QosCallbackRegistrationException if qosCallback is already registered.
+     * @throws RuntimeException if the app already has too many callbacks registered.
+     *
+     * Exceptions after the time of registration is passed through
+     * {@link QosCallback#onError(QosCallbackException)}.  see: {@link QosCallbackException}.
+     *
+     * @param socketInfo the socket information used to match QoS events
+     * @param callback receives qos events that satisfy socketInfo
+     * @param executor The executor on which the callback will be invoked. The provided
+     *                 {@link Executor} must run callback sequentially, otherwise the order of
+     *                 callbacks cannot be guaranteed.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void registerQosCallback(@NonNull final QosSocketInfo socketInfo,
+            @NonNull final QosCallback callback,
+            @CallbackExecutor @NonNull final Executor executor) {
+        Objects.requireNonNull(socketInfo, "socketInfo must be non-null");
+        Objects.requireNonNull(callback, "callback must be non-null");
+        Objects.requireNonNull(executor, "executor must be non-null");
+
+        try {
+            synchronized (mQosCallbackConnections) {
+                if (getQosCallbackConnection(callback) == null) {
+                    final QosCallbackConnection connection =
+                            new QosCallbackConnection(this, callback, executor);
+                    mQosCallbackConnections.add(connection);
+                    mService.registerQosSocketCallback(socketInfo, connection);
+                } else {
+                    Log.e(TAG, "registerQosCallback: Callback already registered");
+                    throw new QosCallbackRegistrationException();
+                }
+            }
+        } catch (final RemoteException e) {
+            Log.e(TAG, "registerQosCallback: Error while registering ", e);
+
+            // The same unregister method method is called for consistency even though nothing
+            // will be sent to the ConnectivityService since the callback was never successfully
+            // registered.
+            unregisterQosCallback(callback);
+            e.rethrowFromSystemServer();
+        } catch (final ServiceSpecificException e) {
+            Log.e(TAG, "registerQosCallback: Error while registering ", e);
+            unregisterQosCallback(callback);
+            throw convertServiceException(e);
+        }
+    }
+
+    /**
+     * Unregisters the given {@link QosCallback}.  The {@link QosCallback} will no longer receive
+     * events once unregistered and can be registered a second time.
+     * <p/>
+     * If the {@link QosCallback} does not have an active registration, it is a no-op.
+     *
+     * @param callback the callback being unregistered
+     *
+     * @hide
+     */
+    @SystemApi
+    public void unregisterQosCallback(@NonNull final QosCallback callback) {
+        Objects.requireNonNull(callback, "The callback must be non-null");
+        try {
+            synchronized (mQosCallbackConnections) {
+                final QosCallbackConnection connection = getQosCallbackConnection(callback);
+                if (connection != null) {
+                    connection.stopReceivingMessages();
+                    mService.unregisterQosCallback(connection);
+                    mQosCallbackConnections.remove(connection);
+                } else {
+                    Log.d(TAG, "unregisterQosCallback: Callback not registered");
+                }
+            }
+        } catch (final RemoteException e) {
+            Log.e(TAG, "unregisterQosCallback: Error while unregistering ", e);
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the connection related to the callback.
+     *
+     * @param callback the callback to look up
+     * @return the related connection
+     */
+    @Nullable
+    private QosCallbackConnection getQosCallbackConnection(final QosCallback callback) {
+        for (final QosCallbackConnection connection : mQosCallbackConnections) {
+            // Checking by reference here is intentional
+            if (connection.getCallback() == callback) {
+                return connection;
+            }
+        }
+        return null;
+    }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 5e925b6..6fecee6 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -20,6 +20,8 @@
 import android.net.ConnectionInfo;
 import android.net.ConnectivityDiagnosticsManager;
 import android.net.IConnectivityDiagnosticsCallback;
+import android.net.IQosCallback;
+import android.net.ISocketKeepaliveCallback;
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkAgentConfig;
@@ -27,9 +29,9 @@
 import android.net.NetworkInfo;
 import android.net.NetworkRequest;
 import android.net.NetworkState;
-import android.net.ISocketKeepaliveCallback;
 import android.net.ProxyInfo;
 import android.net.UidRange;
+import android.net.QosSocketInfo;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.INetworkActivityListener;
@@ -206,11 +208,11 @@
     void startNattKeepalive(in Network network, int intervalSeconds,
             in ISocketKeepaliveCallback cb, String srcAddr, int srcPort, String dstAddr);
 
-    void startNattKeepaliveWithFd(in Network network, in FileDescriptor fd, int resourceId,
+    void startNattKeepaliveWithFd(in Network network, in ParcelFileDescriptor pfd, int resourceId,
             int intervalSeconds, in ISocketKeepaliveCallback cb, String srcAddr,
             String dstAddr);
 
-    void startTcpKeepalive(in Network network, in FileDescriptor fd, int intervalSeconds,
+    void startTcpKeepalive(in Network network, in ParcelFileDescriptor pfd, int intervalSeconds,
             in ISocketKeepaliveCallback cb);
 
     void stopKeepalive(in Network network, int slot);
@@ -239,4 +241,7 @@
     void unregisterNetworkActivityListener(in INetworkActivityListener l);
 
     boolean isDefaultNetworkActive();
+
+    void registerQosSocketCallback(in QosSocketInfo socketInfo, in IQosCallback callback);
+    void unregisterQosCallback(in IQosCallback callback);
 }
diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl
index fe9141c..dfb1e99 100644
--- a/core/java/android/net/INetworkPolicyListener.aidl
+++ b/core/java/android/net/INetworkPolicyListener.aidl
@@ -23,6 +23,6 @@
     void onMeteredIfacesChanged(in String[] meteredIfaces);
     void onRestrictBackgroundChanged(boolean restrictBackground);
     void onUidPoliciesChanged(int uid, int uidPolicies);
-    void onSubscriptionOverride(int subId, int overrideMask, int overrideValue);
+    void onSubscriptionOverride(int subId, int overrideMask, int overrideValue, in int[] networkTypes);
     void onSubscriptionPlansChanged(int subId, in SubscriptionPlan[] plans);
 }
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 792e5b4..84a2acc 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -76,9 +76,10 @@
     SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
     void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
     String getSubscriptionPlansOwner(int subId);
-    void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, long timeoutMillis, String callingPackage);
+    void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, in int[] networkTypes, long timeoutMillis, String callingPackage);
 
     void factoryReset(String subscriber);
 
     boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork);
+    boolean isUidRestrictedOnMeteredNetworks(int uid);
 }
diff --git a/core/java/android/net/IQosCallback.aidl b/core/java/android/net/IQosCallback.aidl
new file mode 100644
index 0000000..91c7575
--- /dev/null
+++ b/core/java/android/net/IQosCallback.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.os.Bundle;
+import android.net.QosSession;
+import android.telephony.data.EpsBearerQosSessionAttributes;
+
+/**
+ * AIDL interface for QosCallback
+ *
+ * @hide
+ */
+oneway interface IQosCallback
+{
+     void onQosEpsBearerSessionAvailable(in QosSession session,
+        in EpsBearerQosSessionAttributes attributes);
+     void onQosSessionLost(in QosSession session);
+     void onError(in int type);
+}
diff --git a/core/java/android/net/NattSocketKeepalive.java b/core/java/android/net/NattSocketKeepalive.java
index b0ce0c7..a15d165 100644
--- a/core/java/android/net/NattSocketKeepalive.java
+++ b/core/java/android/net/NattSocketKeepalive.java
@@ -51,7 +51,7 @@
     void startImpl(int intervalSec) {
         mExecutor.execute(() -> {
             try {
-                mService.startNattKeepaliveWithFd(mNetwork, mPfd.getFileDescriptor(), mResourceId,
+                mService.startNattKeepaliveWithFd(mNetwork, mPfd, mResourceId,
                         intervalSec, mCallback,
                         mSource.getHostAddress(), mDestination.getHostAddress());
             } catch (RemoteException e) {
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 4f46736..d22d82d 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -30,6 +30,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.telephony.data.EpsBearerQosSessionAttributes;
 import android.util.Log;
 
 import com.android.connectivity.aidl.INetworkAgent;
@@ -228,12 +229,6 @@
     public static final String REDIRECT_URL_KEY = "redirect URL";
 
     /**
-     * Bundle key for the underlying networks in {@code EVENT_UNDERLYING_NETWORKS_CHANGED}.
-     * @hide
-     */
-    public static final String UNDERLYING_NETWORKS_KEY = "underlyingNetworks";
-
-     /**
      * Sent by the NetworkAgent to ConnectivityService to indicate this network was
      * explicitly selected.  This should be sent before the NetworkInfo is marked
      * CONNECTED so it can be given special treatment at that time.
@@ -347,6 +342,24 @@
      */
     private static final int EVENT_AGENT_DISCONNECTED = BASE + 19;
 
+    /**
+     * Sent by QosCallbackTracker to {@link NetworkAgent} to register a new filter with
+     * callback.
+     *
+     * arg1 = QoS agent callback ID
+     * obj = {@link QosFilter}
+     * @hide
+     */
+    public static final int CMD_REGISTER_QOS_CALLBACK = BASE + 20;
+
+    /**
+     * Sent by QosCallbackTracker to {@link NetworkAgent} to unregister a callback.
+     *
+     * arg1 = QoS agent callback ID
+     * @hide
+     */
+    public static final int CMD_UNREGISTER_QOS_CALLBACK = BASE + 21;
+
     private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
         // The subtype can be changed with (TODO) setLegacySubtype, but it starts
         // with 0 (TelephonyManager.NETWORK_TYPE_UNKNOWN) and an empty description.
@@ -526,6 +539,17 @@
                     onRemoveKeepalivePacketFilter(msg.arg1 /* slot */);
                     break;
                 }
+                case CMD_REGISTER_QOS_CALLBACK: {
+                    onQosCallbackRegistered(
+                            msg.arg1 /* QoS callback id */,
+                            (QosFilter) msg.obj /* QoS filter */);
+                    break;
+                }
+                case CMD_UNREGISTER_QOS_CALLBACK: {
+                    onQosCallbackUnregistered(
+                            msg.arg1 /* QoS callback id */);
+                    break;
+                }
             }
         }
     }
@@ -559,6 +583,8 @@
     }
 
     private static class NetworkAgentBinder extends INetworkAgent.Stub {
+        private static final String LOG_TAG = NetworkAgentBinder.class.getSimpleName();
+
         private final Handler mHandler;
 
         private NetworkAgentBinder(Handler handler) {
@@ -645,6 +671,25 @@
             mHandler.sendMessage(mHandler.obtainMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER,
                     slot, 0));
         }
+
+        @Override
+        public void onQosFilterCallbackRegistered(final int qosCallbackId,
+                final QosFilterParcelable qosFilterParcelable) {
+            if (qosFilterParcelable.getQosFilter() != null) {
+                mHandler.sendMessage(
+                        mHandler.obtainMessage(CMD_REGISTER_QOS_CALLBACK, qosCallbackId, 0,
+                                qosFilterParcelable.getQosFilter()));
+                return;
+            }
+
+            Log.wtf(LOG_TAG, "onQosFilterCallbackRegistered: qos filter is null.");
+        }
+
+        @Override
+        public void onQosCallbackUnregistered(final int qosCallbackId) {
+            mHandler.sendMessage(mHandler.obtainMessage(
+                    CMD_UNREGISTER_QOS_CALLBACK, qosCallbackId, 0, null));
+        }
     }
 
     /**
@@ -1073,8 +1118,68 @@
     protected void preventAutomaticReconnect() {
     }
 
+    /**
+     * Called when a qos callback is registered with a filter.
+     * @param qosCallbackId the id for the callback registered
+     * @param filter the filter being registered
+     */
+    public void onQosCallbackRegistered(final int qosCallbackId, final @NonNull QosFilter filter) {
+    }
+
+    /**
+     * Called when a qos callback is registered with a filter.
+     * <p/>
+     * Any QoS events that are sent with the same callback id after this method is called
+     * are a no-op.
+     *
+     * @param qosCallbackId the id for the callback being unregistered
+     */
+    public void onQosCallbackUnregistered(final int qosCallbackId) {
+    }
+
+
+    /**
+     * Sends the attributes of Eps Bearer Qos Session back to the Application
+     *
+     * @param qosCallbackId the callback id that the session belongs to
+     * @param sessionId the unique session id across all Eps Bearer Qos Sessions
+     * @param attributes the attributes of the Eps Qos Session
+     */
+    public final void sendQosSessionAvailable(final int qosCallbackId, final int sessionId,
+            @NonNull final EpsBearerQosSessionAttributes attributes) {
+        Objects.requireNonNull(attributes, "The attributes must be non-null");
+        queueOrSendMessage(ra -> ra.sendEpsQosSessionAvailable(qosCallbackId,
+                new QosSession(sessionId, QosSession.TYPE_EPS_BEARER),
+                attributes));
+    }
+
+    /**
+     * Sends event that the Eps Qos Session was lost.
+     *
+     * @param qosCallbackId the callback id that the session belongs to
+     * @param sessionId the unique session id across all Eps Bearer Qos Sessions
+     */
+    public final void sendQosSessionLost(final int qosCallbackId, final int sessionId) {
+        queueOrSendMessage(ra -> ra.sendQosSessionLost(qosCallbackId,
+                new QosSession(sessionId, QosSession.TYPE_EPS_BEARER)));
+    }
+
+    /**
+     * Sends the exception type back to the application.
+     *
+     * The NetworkAgent should not send anymore messages with this id.
+     *
+     * @param qosCallbackId the callback id this exception belongs to
+     * @param exceptionType the type of exception
+     */
+    public final void sendQosCallbackError(final int qosCallbackId,
+            @QosCallbackException.ExceptionType final int exceptionType) {
+        queueOrSendMessage(ra -> ra.sendQosCallbackError(qosCallbackId, exceptionType));
+    }
+
+
     /** @hide */
-    protected void log(String s) {
+    protected void log(final String s) {
         Log.d(LOG_TAG, "NetworkAgent: " + s);
     }
 }
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 2d9f6d8..0a895b9 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -23,7 +23,6 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.os.Build;
@@ -576,7 +575,6 @@
      * @hide
      */
     @UnsupportedAppUsage
-    @TestApi
     public @NetCapability int[] getCapabilities() {
         return BitUtils.unpackBits(mNetworkCapabilities);
     }
@@ -821,7 +819,7 @@
      *
      * @hide
      */
-    @TestApi
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final int TRANSPORT_TEST = 7;
 
     /** @hide */
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index cad0db2..ed169e7 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -34,6 +34,7 @@
 import android.os.Build;
 import android.os.Process;
 import android.os.RemoteException;
+import android.telephony.Annotation;
 import android.telephony.SubscriptionPlan;
 import android.util.DebugUtils;
 import android.util.Pair;
@@ -377,6 +378,8 @@
      * @param overrideMask the bitmask that specifies which of the overrides is being
      *            set or cleared.
      * @param overrideValue the override values to set or clear.
+     * @param networkTypes the network types this override applies to.
+     *            {@see TelephonyManager#getAllNetworkTypes()}
      * @param timeoutMillis the timeout after which the requested override will
      *            be automatically cleared, or {@code 0} to leave in the
      *            requested state until explicitly cleared, or the next reboot,
@@ -385,11 +388,12 @@
      * @hide
      */
     public void setSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask,
-            @SubscriptionOverrideMask int overrideValue, long timeoutMillis,
-                    @NonNull String callingPackage) {
+            @SubscriptionOverrideMask int overrideValue,
+            @NonNull @Annotation.NetworkType int[] networkTypes, long timeoutMillis,
+            @NonNull String callingPackage) {
         try {
-            mService.setSubscriptionOverride(subId, overrideMask, overrideValue, timeoutMillis,
-                    callingPackage);
+            mService.setSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes,
+                    timeoutMillis, callingPackage);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -460,6 +464,22 @@
     }
 
     /**
+     * Check that the given uid is restricted from doing networking on metered networks.
+     *
+     * @param uid The target uid.
+     * @return true if the given uid is restricted from doing networking on metered networks.
+     *
+     * @hide
+     */
+    public boolean isUidRestrictedOnMeteredNetworks(int uid) {
+        try {
+            return mService.isUidRestrictedOnMeteredNetworks(uid);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Get multipath preference for the given network.
      */
     public int getMultipathPreference(Network network) {
@@ -597,9 +617,10 @@
          * @param subId the subscriber this override applies to.
          * @param overrideMask a bitmask that specifies which of the overrides is set.
          * @param overrideValue a bitmask that specifies the override values.
+         * @param networkTypes the network types this override applies to.
          */
         public void onSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask,
-                @SubscriptionOverrideMask int overrideValue) {}
+                @SubscriptionOverrideMask int overrideValue, int[] networkTypes) {}
 
         /**
          * Notify of subscription plans change about a given subscription.
@@ -623,8 +644,8 @@
 
         @Override
         public void onSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask,
-                @SubscriptionOverrideMask int overrideValue) {
-            mCallback.onSubscriptionOverride(subId, overrideMask, overrideValue);
+                @SubscriptionOverrideMask int overrideValue, int[] networkTypes) {
+            mCallback.onSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes);
         }
 
         @Override
@@ -640,7 +661,7 @@
         @Override public void onRestrictBackgroundChanged(boolean restrictBackground) { }
         @Override public void onUidPoliciesChanged(int uid, int uidPolicies) { }
         @Override public void onSubscriptionOverride(int subId, int overrideMask,
-                int overrideValue) { }
+                int overrideValue, int[] networkTypes) { }
         @Override public void onSubscriptionPlansChanged(int subId, SubscriptionPlan[] plans) { }
     }
 }
diff --git a/core/java/android/net/NetworkReleasedException.java b/core/java/android/net/NetworkReleasedException.java
new file mode 100644
index 0000000..0629b75
--- /dev/null
+++ b/core/java/android/net/NetworkReleasedException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.SystemApi;
+
+/**
+ * Indicates that the {@link Network} was released and is no longer available.
+ *
+ * @hide
+ */
+@SystemApi
+public class NetworkReleasedException extends Exception {
+    /** @hide */
+    public NetworkReleasedException() {
+        super("The network was released and is no longer available");
+    }
+}
diff --git a/core/java/android/net/QosCallback.java b/core/java/android/net/QosCallback.java
new file mode 100644
index 0000000..22f06bc
--- /dev/null
+++ b/core/java/android/net/QosCallback.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Receives Qos information given a {@link Network}.  The callback is registered with
+ * {@link ConnectivityManager#registerQosCallback}.
+ *
+ * <p>
+ * <br/>
+ * The callback will no longer receive calls if any of the following takes place:
+ * <ol>
+ * <li>{@link ConnectivityManager#unregisterQosCallback(QosCallback)} is called with the same
+ * callback instance.</li>
+ * <li>{@link QosCallback#onError(QosCallbackException)} is called.</li>
+ * <li>A network specific issue occurs.  eg. Congestion on a carrier network.</li>
+ * <li>The network registered with the callback has no associated QoS providers</li>
+ * </ul>
+ * {@hide}
+ */
+@SystemApi
+public abstract class QosCallback {
+    /**
+     * Invoked after an error occurs on a registered callback.  Once called, the callback is
+     * automatically unregistered and the callback will no longer receive calls.
+     *
+     * <p>The underlying exception can either be a runtime exception or a custom exception made for
+     * {@link QosCallback}. see: {@link QosCallbackException}.
+     *
+     * @param exception wraps the underlying cause
+     */
+    public void onError(@NonNull final QosCallbackException exception) {
+    }
+
+    /**
+     * Called when a Qos Session first becomes available to the callback or if its attributes have
+     * changed.
+     * <p>
+     * Note: The callback may be called multiple times with the same attributes.
+     *
+     * @param session the available session
+     * @param sessionAttributes the attributes of the session
+     */
+    public void onQosSessionAvailable(@NonNull final QosSession session,
+            @NonNull final QosSessionAttributes sessionAttributes) {
+    }
+
+    /**
+     * Called after a Qos Session is lost.
+     * <p>
+     * At least one call to
+     * {@link QosCallback#onQosSessionAvailable(QosSession, QosSessionAttributes)}
+     * with the same {@link QosSession} will precede a call to lost.
+     *
+     * @param session the lost session
+     */
+    public void onQosSessionLost(@NonNull final QosSession session) {
+    }
+
+    /**
+     * Thrown when there is a problem registering {@link QosCallback} with
+     * {@link ConnectivityManager#registerQosCallback(QosSocketInfo, QosCallback, Executor)}.
+     */
+    public static class QosCallbackRegistrationException extends RuntimeException {
+        /**
+         * @hide
+         */
+        public QosCallbackRegistrationException() {
+            super();
+        }
+    }
+}
diff --git a/core/java/android/net/QosCallbackConnection.java b/core/java/android/net/QosCallbackConnection.java
new file mode 100644
index 0000000..bdb4ad6
--- /dev/null
+++ b/core/java/android/net/QosCallbackConnection.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.telephony.data.EpsBearerQosSessionAttributes;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Sends messages from {@link com.android.server.ConnectivityService} to the registered
+ * {@link QosCallback}.
+ * <p/>
+ * This is a satellite class of {@link ConnectivityManager} and not meant
+ * to be used in other contexts.
+ *
+ * @hide
+ */
+class QosCallbackConnection extends android.net.IQosCallback.Stub {
+
+    @NonNull private final ConnectivityManager mConnectivityManager;
+    @Nullable private volatile QosCallback mCallback;
+    @NonNull private final Executor mExecutor;
+
+    @VisibleForTesting
+    @Nullable
+    public QosCallback getCallback() {
+        return mCallback;
+    }
+
+    /**
+     * The constructor for the connection
+     *
+     * @param connectivityManager the mgr that created this connection
+     * @param callback the callback to send messages back to
+     * @param executor The executor on which the callback will be invoked. The provided
+     *                 {@link Executor} must run callback sequentially, otherwise the order of
+     *                 callbacks cannot be guaranteed.
+     */
+    QosCallbackConnection(@NonNull final ConnectivityManager connectivityManager,
+            @NonNull final QosCallback callback,
+            @NonNull final Executor executor) {
+        mConnectivityManager = Objects.requireNonNull(connectivityManager,
+                "connectivityManager must be non-null");
+        mCallback = Objects.requireNonNull(callback, "callback must be non-null");
+        mExecutor = Objects.requireNonNull(executor, "executor must be non-null");
+    }
+
+    /**
+     * Called when either the {@link EpsBearerQosSessionAttributes} has changed or on the first time
+     * the attributes have become available.
+     *
+     * @param session the session that is now available
+     * @param attributes the corresponding attributes of session
+     */
+    @Override
+    public void onQosEpsBearerSessionAvailable(@NonNull final QosSession session,
+            @NonNull final EpsBearerQosSessionAttributes attributes) {
+
+        mExecutor.execute(() -> {
+            final QosCallback callback = mCallback;
+            if (callback != null) {
+                callback.onQosSessionAvailable(session, attributes);
+            }
+        });
+    }
+
+    /**
+     * Called when the session is lost.
+     *
+     * @param session the session that was lost
+     */
+    @Override
+    public void onQosSessionLost(@NonNull final QosSession session) {
+        mExecutor.execute(() -> {
+            final QosCallback callback = mCallback;
+            if (callback != null) {
+                callback.onQosSessionLost(session);
+            }
+        });
+    }
+
+    /**
+     * Called when there is an error on the registered callback.
+     *
+     *  @param errorType the type of error
+     */
+    @Override
+    public void onError(@QosCallbackException.ExceptionType final int errorType) {
+        mExecutor.execute(() -> {
+            final QosCallback callback = mCallback;
+            if (callback != null) {
+                // Messages no longer need to be received since there was an error.
+                stopReceivingMessages();
+                mConnectivityManager.unregisterQosCallback(callback);
+                callback.onError(QosCallbackException.createException(errorType));
+            }
+        });
+    }
+
+    /**
+     * The callback will stop receiving messages.
+     * <p/>
+     * There are no synchronization guarantees on exactly when the callback will stop receiving
+     * messages.
+     */
+    void stopReceivingMessages() {
+        mCallback = null;
+    }
+}
diff --git a/core/java/android/net/QosCallbackException.java b/core/java/android/net/QosCallbackException.java
new file mode 100644
index 0000000..7fd9a527e
--- /dev/null
+++ b/core/java/android/net/QosCallbackException.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This is the exception type passed back through the onError method on {@link QosCallback}.
+ * {@link QosCallbackException#getCause()} contains the actual error that caused this exception.
+ *
+ * The possible exception types as causes are:
+ * 1. {@link NetworkReleasedException}
+ * 2. {@link SocketNotBoundException}
+ * 3. {@link UnsupportedOperationException}
+ * 4. {@link SocketLocalAddressChangedException}
+ *
+ * @hide
+ */
+@SystemApi
+public final class QosCallbackException extends Exception {
+
+    /** @hide */
+    @IntDef(prefix = {"EX_TYPE_"}, value = {
+            EX_TYPE_FILTER_NONE,
+            EX_TYPE_FILTER_NETWORK_RELEASED,
+            EX_TYPE_FILTER_SOCKET_NOT_BOUND,
+            EX_TYPE_FILTER_NOT_SUPPORTED,
+            EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ExceptionType {}
+
+    private static final String TAG = "QosCallbackException";
+
+    // Types of exceptions supported //
+    /** {@hide} */
+    public static final int EX_TYPE_FILTER_NONE = 0;
+
+    /** {@hide} */
+    public static final int EX_TYPE_FILTER_NETWORK_RELEASED = 1;
+
+    /** {@hide} */
+    public static final int EX_TYPE_FILTER_SOCKET_NOT_BOUND = 2;
+
+    /** {@hide} */
+    public static final int EX_TYPE_FILTER_NOT_SUPPORTED = 3;
+
+    /** {@hide} */
+    public static final int EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED = 4;
+
+    /**
+     * Creates exception based off of a type and message.  Not all types of exceptions accept a
+     * custom message.
+     *
+     * {@hide}
+     */
+    @NonNull
+    static QosCallbackException createException(@ExceptionType final int type) {
+        switch (type) {
+            case EX_TYPE_FILTER_NETWORK_RELEASED:
+                return new QosCallbackException(new NetworkReleasedException());
+            case EX_TYPE_FILTER_SOCKET_NOT_BOUND:
+                return new QosCallbackException(new SocketNotBoundException());
+            case EX_TYPE_FILTER_NOT_SUPPORTED:
+                return new QosCallbackException(new UnsupportedOperationException(
+                        "This device does not support the specified filter"));
+            case EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED:
+                return new QosCallbackException(
+                        new SocketLocalAddressChangedException());
+            default:
+                Log.wtf(TAG, "create: No case setup for exception type: '" + type + "'");
+                return new QosCallbackException(
+                        new RuntimeException("Unknown exception code: " + type));
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public QosCallbackException(@NonNull final String message) {
+        super(message);
+    }
+
+    /**
+     * @hide
+     */
+    public QosCallbackException(@NonNull final Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/core/java/android/net/QosFilter.java b/core/java/android/net/QosFilter.java
new file mode 100644
index 0000000..0705468
--- /dev/null
+++ b/core/java/android/net/QosFilter.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+/**
+ * Provides the related filtering logic to the {@link NetworkAgent} to match {@link QosSession}s
+ * to their related {@link QosCallback}.
+ *
+ * Used by the {@link com.android.server.ConnectivityService} to validate a {@link QosCallback}
+ * is still able to receive a {@link QosSession}.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class QosFilter {
+
+    /**
+     * The constructor is kept hidden from outside this package to ensure that all derived types
+     * are known and properly handled when being passed to and from {@link NetworkAgent}.
+     *
+     * @hide
+     */
+    QosFilter() {
+    }
+
+    /**
+     * The network used with this filter.
+     *
+     * @return the registered {@link Network}
+     */
+    @NonNull
+    public abstract Network getNetwork();
+
+    /**
+     * Validates that conditions have not changed such that no further {@link QosSession}s should
+     * be passed back to the {@link QosCallback} associated to this filter.
+     *
+     * @return the error code when present, otherwise the filter is valid
+     *
+     * @hide
+     */
+    @QosCallbackException.ExceptionType
+    public abstract int validate();
+}
+
diff --git a/core/java/android/net/QosFilterParcelable.aidl b/core/java/android/net/QosFilterParcelable.aidl
new file mode 100644
index 0000000..312d635
--- /dev/null
+++ b/core/java/android/net/QosFilterParcelable.aidl
@@ -0,0 +1,21 @@
+/*
+**
+** Copyright (C) 2020 The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.net;
+
+parcelable QosFilterParcelable;
+
diff --git a/core/java/android/net/QosFilterParcelable.java b/core/java/android/net/QosFilterParcelable.java
new file mode 100644
index 0000000..da3b2cf
--- /dev/null
+++ b/core/java/android/net/QosFilterParcelable.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.util.Objects;
+
+/**
+ * Aware of how to parcel different types of {@link QosFilter}s.  Any new type of qos filter must
+ * have a specialized case written here.
+ * <p/>
+ * Specifically leveraged when transferring {@link QosFilter} from
+ * {@link com.android.server.ConnectivityService} to {@link NetworkAgent} when the filter is first
+ * registered.
+ * <p/>
+ * This is not meant to be used in other contexts.
+ *
+ * @hide
+ */
+public final class QosFilterParcelable implements Parcelable {
+
+    private static final String LOG_TAG = QosFilterParcelable.class.getSimpleName();
+
+    // Indicates that the filter was not successfully written to the parcel.
+    private static final int NO_FILTER_PRESENT = 0;
+
+    // The parcel is of type qos socket filter.
+    private static final int QOS_SOCKET_FILTER = 1;
+
+    private final QosFilter mQosFilter;
+
+    /**
+     * The underlying qos filter.
+     * <p/>
+     * Null only in the case parceling failed.
+     */
+    @Nullable
+    public QosFilter getQosFilter() {
+        return mQosFilter;
+    }
+
+    public QosFilterParcelable(@NonNull final QosFilter qosFilter) {
+        Objects.requireNonNull(qosFilter, "qosFilter must be non-null");
+
+        // NOTE: Normally a type check would belong here, but doing so breaks unit tests that rely
+        // on mocking qos filter.
+        mQosFilter = qosFilter;
+    }
+
+    private QosFilterParcelable(final Parcel in) {
+        final int filterParcelType = in.readInt();
+
+        switch (filterParcelType) {
+            case QOS_SOCKET_FILTER: {
+                mQosFilter = new QosSocketFilter(QosSocketInfo.CREATOR.createFromParcel(in));
+                break;
+            }
+
+            case NO_FILTER_PRESENT:
+            default: {
+                mQosFilter = null;
+            }
+        }
+    }
+
+    public static final Creator<QosFilterParcelable> CREATOR = new Creator<QosFilterParcelable>() {
+        @Override
+        public QosFilterParcelable createFromParcel(final Parcel in) {
+            return new QosFilterParcelable(in);
+        }
+
+        @Override
+        public QosFilterParcelable[] newArray(final int size) {
+            return new QosFilterParcelable[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(final Parcel dest, final int flags) {
+        if (mQosFilter instanceof QosSocketFilter) {
+            dest.writeInt(QOS_SOCKET_FILTER);
+            final QosSocketFilter qosSocketFilter = (QosSocketFilter) mQosFilter;
+            qosSocketFilter.getQosSocketInfo().writeToParcel(dest, 0);
+            return;
+        }
+        dest.writeInt(NO_FILTER_PRESENT);
+        Log.e(LOG_TAG, "Parceling failed, unknown type of filter present: " + mQosFilter);
+    }
+}
diff --git a/core/java/android/net/QosSession.aidl b/core/java/android/net/QosSession.aidl
new file mode 100644
index 0000000..c2cf366
--- /dev/null
+++ b/core/java/android/net/QosSession.aidl
@@ -0,0 +1,21 @@
+/*
+**
+** Copyright (C) 2020 The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.net;
+
+parcelable QosSession;
+
diff --git a/core/java/android/net/QosSession.java b/core/java/android/net/QosSession.java
new file mode 100644
index 0000000..4f3bb77
--- /dev/null
+++ b/core/java/android/net/QosSession.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Provides identifying information of a QoS session.  Sent to an application through
+ * {@link QosCallback}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class QosSession implements Parcelable {
+
+    /**
+     * The {@link QosSession} is a LTE EPS Session.
+     */
+    public static final int TYPE_EPS_BEARER = 1;
+
+    private final int mSessionId;
+
+    private final int mSessionType;
+
+    /**
+     * Gets the unique id of the session that is used to differentiate sessions across different
+     * types.
+     * <p/>
+     * Note: Different qos sessions can be provided by different actors.
+     *
+     * @return the unique id
+     */
+    public long getUniqueId() {
+        return (long) mSessionType << 32 | mSessionId;
+    }
+
+    /**
+     * Gets the session id that is unique within that type.
+     * <p/>
+     * Note: The session id is set by the actor providing the qos.  It can be either manufactured by
+     * the actor, but also may have a particular meaning within that type.  For example, using the
+     * bearer id as the session id for {@link android.telephony.data.EpsBearerQosSessionAttributes}
+     * is a straight forward way to keep the sessions unique from one another within that type.
+     *
+     * @return the id of the session
+     */
+    public int getSessionId() {
+        return mSessionId;
+    }
+
+    /**
+     * Gets the type of session.
+     */
+    @QosSessionType
+    public int getSessionType() {
+        return mSessionType;
+    }
+
+    /**
+     * Creates a {@link QosSession}.
+     *
+     * @param sessionId uniquely identifies the session across all sessions of the same type
+     * @param sessionType the type of session
+     */
+    public QosSession(final int sessionId, @QosSessionType final int sessionType) {
+        //Ensures the session id is unique across types of sessions
+        mSessionId = sessionId;
+        mSessionType = sessionType;
+    }
+
+
+    @Override
+    public String toString() {
+        return "QosSession{"
+                + "mSessionId=" + mSessionId
+                + ", mSessionType=" + mSessionType
+                + '}';
+    }
+
+    /**
+     * Annotations for types of qos sessions.
+     */
+    @IntDef(value = {
+            TYPE_EPS_BEARER,
+    })
+    @interface QosSessionType {}
+
+    private QosSession(final Parcel in) {
+        mSessionId = in.readInt();
+        mSessionType = in.readInt();
+    }
+
+    @NonNull
+    public static final Creator<QosSession> CREATOR = new Creator<QosSession>() {
+        @NonNull
+        @Override
+        public QosSession createFromParcel(@NonNull final Parcel in) {
+            return new QosSession(in);
+        }
+
+        @NonNull
+        @Override
+        public QosSession[] newArray(final int size) {
+            return new QosSession[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull final Parcel dest, final int flags) {
+        dest.writeInt(mSessionId);
+        dest.writeInt(mSessionType);
+    }
+}
diff --git a/core/java/android/net/QosSessionAttributes.java b/core/java/android/net/QosSessionAttributes.java
new file mode 100644
index 0000000..7a88594
--- /dev/null
+++ b/core/java/android/net/QosSessionAttributes.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.SystemApi;
+
+/**
+ * Implemented by classes that encapsulate Qos related attributes that describe a Qos Session.
+ *
+ * Use the instanceof keyword to determine the underlying type.
+ *
+ * @hide
+ */
+@SystemApi
+public interface QosSessionAttributes {
+}
diff --git a/core/java/android/net/QosSocketFilter.java b/core/java/android/net/QosSocketFilter.java
new file mode 100644
index 0000000..f51a088
--- /dev/null
+++ b/core/java/android/net/QosSocketFilter.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static android.net.QosCallbackException.EX_TYPE_FILTER_NONE;
+import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.ParcelFileDescriptor;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.util.Objects;
+
+/**
+ * Filters a {@link QosSession} according to the binding on the provided {@link Socket}.
+ *
+ * @hide
+ */
+public class QosSocketFilter extends QosFilter {
+
+    private static final String TAG = QosSocketFilter.class.getSimpleName();
+
+    @NonNull
+    private final QosSocketInfo mQosSocketInfo;
+
+    /**
+     * Creates a {@link QosSocketFilter} based off of {@link QosSocketInfo}.
+     *
+     * @param qosSocketInfo the information required to filter and validate
+     */
+    public QosSocketFilter(@NonNull final QosSocketInfo qosSocketInfo) {
+        Objects.requireNonNull(qosSocketInfo, "qosSocketInfo must be non-null");
+        mQosSocketInfo = qosSocketInfo;
+    }
+
+    /**
+     * Gets the parcelable qos socket info that was used to create the filter.
+     */
+    @NonNull
+    public QosSocketInfo getQosSocketInfo() {
+        return mQosSocketInfo;
+    }
+
+    /**
+     * Performs two validations:
+     * 1. If the socket is not bound, then return
+     *    {@link QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_BOUND}. This is detected
+     *    by checking the local address on the filter which becomes null when the socket is no
+     *    longer bound.
+     * 2. In the scenario that the socket is now bound to a different local address, which can
+     *    happen in the case of UDP, then
+     *    {@link QosCallbackException.EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED} is returned.
+     * @return validation error code
+     */
+    @Override
+    public int validate() {
+        final InetSocketAddress sa = getAddressFromFileDescriptor();
+        if (sa == null) {
+            return QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_BOUND;
+        }
+
+        if (!sa.equals(mQosSocketInfo.getLocalSocketAddress())) {
+            return EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED;
+        }
+
+        return EX_TYPE_FILTER_NONE;
+    }
+
+    /**
+     * The local address of the socket's binding.
+     *
+     * Note: If the socket is no longer bound, null is returned.
+     *
+     * @return the local address
+     */
+    @Nullable
+    private InetSocketAddress getAddressFromFileDescriptor() {
+        final ParcelFileDescriptor parcelFileDescriptor = mQosSocketInfo.getParcelFileDescriptor();
+        if (parcelFileDescriptor == null) return null;
+
+        final FileDescriptor fd = parcelFileDescriptor.getFileDescriptor();
+        if (fd == null) return null;
+
+        final SocketAddress address;
+        try {
+            address = Os.getsockname(fd);
+        } catch (final ErrnoException e) {
+            Log.e(TAG, "getAddressFromFileDescriptor: getLocalAddress exception", e);
+            return null;
+        }
+        if (address instanceof InetSocketAddress) {
+            return (InetSocketAddress) address;
+        }
+        return null;
+    }
+
+    /**
+     * The network used with this filter.
+     *
+     * @return the registered {@link Network}
+     */
+    @NonNull
+    @Override
+    public Network getNetwork() {
+        return mQosSocketInfo.getNetwork();
+    }
+}
diff --git a/core/java/android/net/QosSocketInfo.aidl b/core/java/android/net/QosSocketInfo.aidl
new file mode 100644
index 0000000..476c090
--- /dev/null
+++ b/core/java/android/net/QosSocketInfo.aidl
@@ -0,0 +1,21 @@
+/*
+**
+** Copyright (C) 2020 The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.net;
+
+parcelable QosSocketInfo;
+
diff --git a/core/java/android/net/QosSocketInfo.java b/core/java/android/net/QosSocketInfo.java
new file mode 100644
index 0000000..d37c469
--- /dev/null
+++ b/core/java/android/net/QosSocketInfo.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.util.Objects;
+
+/**
+ * Used in conjunction with
+ * {@link ConnectivityManager#registerQosCallback}
+ * in order to receive Qos Sessions related to the local address and port of a bound {@link Socket}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class QosSocketInfo implements Parcelable {
+
+    @NonNull
+    private final Network mNetwork;
+
+    @NonNull
+    private final ParcelFileDescriptor mParcelFileDescriptor;
+
+    @NonNull
+    private final InetSocketAddress mLocalSocketAddress;
+
+    /**
+     * The {@link Network} the socket is on.
+     *
+     * @return the registered {@link Network}
+     */
+    @NonNull
+    public Network getNetwork() {
+        return mNetwork;
+    }
+
+    /**
+     * The parcel file descriptor wrapped around the socket's file descriptor.
+     *
+     * @return the parcel file descriptor of the socket
+     */
+    @NonNull
+    ParcelFileDescriptor getParcelFileDescriptor() {
+        return mParcelFileDescriptor;
+    }
+
+    /**
+     * The local address of the socket passed into {@link QosSocketInfo(Network, Socket)}.
+     * The value does not reflect any changes that occur to the socket after it is first set
+     * in the constructor.
+     *
+     * @return the local address of the socket
+     */
+    @NonNull
+    public InetSocketAddress getLocalSocketAddress() {
+        return mLocalSocketAddress;
+    }
+
+    /**
+     * Creates a {@link QosSocketInfo} given a {@link Network} and bound {@link Socket}.  The
+     * {@link Socket} must remain bound in order to receive {@link QosSession}s.
+     *
+     * @param network the network
+     * @param socket the bound {@link Socket}
+     */
+    public QosSocketInfo(@NonNull final Network network, @NonNull final Socket socket)
+            throws IOException {
+        Objects.requireNonNull(socket, "socket cannot be null");
+
+        mNetwork = Objects.requireNonNull(network, "network cannot be null");
+        mParcelFileDescriptor = ParcelFileDescriptor.dup(socket.getFileDescriptor$());
+        mLocalSocketAddress =
+                new InetSocketAddress(socket.getLocalAddress(), socket.getLocalPort());
+    }
+
+    /* Parcelable methods */
+    private QosSocketInfo(final Parcel in) {
+        mNetwork = Objects.requireNonNull(Network.CREATOR.createFromParcel(in));
+        mParcelFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(in);
+
+        final int addressLength = in.readInt();
+        mLocalSocketAddress = readSocketAddress(in, addressLength);
+    }
+
+    private InetSocketAddress readSocketAddress(final Parcel in, final int addressLength) {
+        final byte[] address = new byte[addressLength];
+        in.readByteArray(address);
+        final int port = in.readInt();
+
+        try {
+            return new InetSocketAddress(InetAddress.getByAddress(address), port);
+        } catch (final UnknownHostException e) {
+            /* The catch block was purposely left empty.  UnknownHostException will never be thrown
+               since the address provided is numeric and non-null. */
+        }
+        return new InetSocketAddress();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull final Parcel dest, final int flags) {
+        mNetwork.writeToParcel(dest, 0);
+        mParcelFileDescriptor.writeToParcel(dest, 0);
+
+        final byte[] address = mLocalSocketAddress.getAddress().getAddress();
+        dest.writeInt(address.length);
+        dest.writeByteArray(address);
+        dest.writeInt(mLocalSocketAddress.getPort());
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<QosSocketInfo> CREATOR =
+            new Parcelable.Creator<QosSocketInfo>() {
+            @NonNull
+            @Override
+            public QosSocketInfo createFromParcel(final Parcel in) {
+                return new QosSocketInfo(in);
+            }
+
+            @NonNull
+            @Override
+            public QosSocketInfo[] newArray(final int size) {
+                return new QosSocketInfo[size];
+            }
+        };
+}
diff --git a/core/java/android/net/SocketLocalAddressChangedException.java b/core/java/android/net/SocketLocalAddressChangedException.java
new file mode 100644
index 0000000..9daad83
--- /dev/null
+++ b/core/java/android/net/SocketLocalAddressChangedException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.SystemApi;
+
+/**
+ * Thrown when the local address of the socket has changed.
+ *
+ * @hide
+ */
+@SystemApi
+public class SocketLocalAddressChangedException extends Exception {
+    /** @hide */
+    public SocketLocalAddressChangedException() {
+        super("The local address of the socket changed");
+    }
+}
diff --git a/core/java/android/net/SocketNotBoundException.java b/core/java/android/net/SocketNotBoundException.java
new file mode 100644
index 0000000..b1d7026
--- /dev/null
+++ b/core/java/android/net/SocketNotBoundException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.SystemApi;
+
+/**
+ * Thrown when a previously bound socket becomes unbound.
+ *
+ * @hide
+ */
+@SystemApi
+public class SocketNotBoundException extends Exception {
+    /** @hide */
+    public SocketNotBoundException() {
+        super("The socket is unbound");
+    }
+}
diff --git a/core/java/android/net/TcpSocketKeepalive.java b/core/java/android/net/TcpSocketKeepalive.java
index 436397e..d89814d 100644
--- a/core/java/android/net/TcpSocketKeepalive.java
+++ b/core/java/android/net/TcpSocketKeepalive.java
@@ -21,7 +21,6 @@
 import android.os.RemoteException;
 import android.util.Log;
 
-import java.io.FileDescriptor;
 import java.util.concurrent.Executor;
 
 /** @hide */
@@ -54,8 +53,7 @@
     void startImpl(int intervalSec) {
         mExecutor.execute(() -> {
             try {
-                final FileDescriptor fd = mPfd.getFileDescriptor();
-                mService.startTcpKeepalive(mNetwork, fd, intervalSec, mCallback);
+                mService.startTcpKeepalive(mNetwork, mPfd, intervalSec, mCallback);
             } catch (RemoteException e) {
                 Log.e(TAG, "Error starting packet keepalive: ", e);
                 throw e.rethrowFromSystemServer();
diff --git a/core/java/android/net/TestNetworkInterface.java b/core/java/android/net/TestNetworkInterface.java
index 8455083..4449ff8 100644
--- a/core/java/android/net/TestNetworkInterface.java
+++ b/core/java/android/net/TestNetworkInterface.java
@@ -15,7 +15,8 @@
  */
 package android.net;
 
-import android.annotation.TestApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
@@ -25,9 +26,11 @@
  *
  * @hide
  */
-@TestApi
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public final class TestNetworkInterface implements Parcelable {
+    @NonNull
     private final ParcelFileDescriptor mFileDescriptor;
+    @NonNull
     private final String mInterfaceName;
 
     @Override
@@ -36,29 +39,32 @@
     }
 
     @Override
-    public void writeToParcel(Parcel out, int flags) {
+    public void writeToParcel(@NonNull Parcel out, int flags) {
         out.writeParcelable(mFileDescriptor, PARCELABLE_WRITE_RETURN_VALUE);
         out.writeString(mInterfaceName);
     }
 
-    public TestNetworkInterface(ParcelFileDescriptor pfd, String intf) {
+    public TestNetworkInterface(@NonNull ParcelFileDescriptor pfd, @NonNull String intf) {
         mFileDescriptor = pfd;
         mInterfaceName = intf;
     }
 
-    private TestNetworkInterface(Parcel in) {
+    private TestNetworkInterface(@NonNull Parcel in) {
         mFileDescriptor = in.readParcelable(ParcelFileDescriptor.class.getClassLoader());
         mInterfaceName = in.readString();
     }
 
+    @NonNull
     public ParcelFileDescriptor getFileDescriptor() {
         return mFileDescriptor;
     }
 
+    @NonNull
     public String getInterfaceName() {
         return mInterfaceName;
     }
 
+    @NonNull
     public static final Parcelable.Creator<TestNetworkInterface> CREATOR =
             new Parcelable.Creator<TestNetworkInterface>() {
                 public TestNetworkInterface createFromParcel(Parcel in) {
diff --git a/core/java/android/net/TestNetworkManager.java b/core/java/android/net/TestNetworkManager.java
index a0a563b..4e89414 100644
--- a/core/java/android/net/TestNetworkManager.java
+++ b/core/java/android/net/TestNetworkManager.java
@@ -17,18 +17,21 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.TestApi;
+import android.annotation.SystemApi;
 import android.os.IBinder;
 import android.os.RemoteException;
 
 import com.android.internal.util.Preconditions;
 
+import java.util.Arrays;
+import java.util.Collection;
+
 /**
  * Class that allows creation and management of per-app, test-only networks
  *
  * @hide
  */
-@TestApi
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public class TestNetworkManager {
     /**
      * Prefix for tun interfaces created by this class.
@@ -57,7 +60,7 @@
      * @param network The test network that should be torn down
      * @hide
      */
-    @TestApi
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public void teardownTestNetwork(@NonNull Network network) {
         try {
             mService.teardownTestNetwork(network.netId);
@@ -102,7 +105,7 @@
      * @param binder A binder object guarding the lifecycle of this test network.
      * @hide
      */
-    @TestApi
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public void setupTestNetwork(@NonNull String iface, @NonNull IBinder binder) {
         setupTestNetwork(iface, null, true, new int[0], binder);
     }
@@ -127,12 +130,29 @@
      * @param linkAddrs an array of LinkAddresses to assign to the TUN interface
      * @return A ParcelFileDescriptor of the underlying TUN interface. Close this to tear down the
      *     TUN interface.
+     * @deprecated Use {@link #createTunInterface(Collection)} instead.
      * @hide
      */
-    @TestApi
+    @Deprecated
+    @NonNull
     public TestNetworkInterface createTunInterface(@NonNull LinkAddress[] linkAddrs) {
+        return createTunInterface(Arrays.asList(linkAddrs));
+    }
+
+    /**
+     * Create a tun interface for testing purposes
+     *
+     * @param linkAddrs an array of LinkAddresses to assign to the TUN interface
+     * @return A ParcelFileDescriptor of the underlying TUN interface. Close this to tear down the
+     *     TUN interface.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @NonNull
+    public TestNetworkInterface createTunInterface(@NonNull Collection<LinkAddress> linkAddrs) {
         try {
-            return mService.createTunInterface(linkAddrs);
+            final LinkAddress[] arr = new LinkAddress[linkAddrs.size()];
+            return mService.createTunInterface(linkAddrs.toArray(arr));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -145,7 +165,8 @@
      *     TAP interface.
      * @hide
      */
-    @TestApi
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @NonNull
     public TestNetworkInterface createTapInterface() {
         try {
             return mService.createTapInterface();
diff --git a/core/java/android/net/util/MultinetworkPolicyTracker.java b/core/java/android/net/util/MultinetworkPolicyTracker.java
index 8dfd4e1..85e3fa3 100644
--- a/core/java/android/net/util/MultinetworkPolicyTracker.java
+++ b/core/java/android/net/util/MultinetworkPolicyTracker.java
@@ -29,7 +29,6 @@
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Handler;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionManager;
@@ -114,8 +113,8 @@
 
         final IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
-        mContext.registerReceiverAsUser(
-                mBroadcastReceiver, UserHandle.ALL, intentFilter, null, mHandler);
+        mContext.registerReceiverForAllUsers(mBroadcastReceiver, intentFilter,
+                null /* broadcastPermission */, mHandler);
 
         reevaluate();
     }
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index ba29a15a..fdc3d65 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -43,6 +43,7 @@
             POWER_COMPONENT_AUDIO,
             POWER_COMPONENT_VIDEO,
             POWER_COMPONENT_FLASHLIGHT,
+            POWER_COMPONENT_MOBILE_RADIO,
             POWER_COMPONENT_SYSTEM_SERVICES,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -57,8 +58,9 @@
     public static final int POWER_COMPONENT_VIDEO = 5;
     public static final int POWER_COMPONENT_FLASHLIGHT = 6;
     public static final int POWER_COMPONENT_SYSTEM_SERVICES = 7;
+    public static final int POWER_COMPONENT_MOBILE_RADIO = 8;
 
-    public static final int POWER_COMPONENT_COUNT = 8;
+    public static final int POWER_COMPONENT_COUNT = 9;
 
     public static final int FIRST_CUSTOM_POWER_COMPONENT_ID = 1000;
     public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999;
@@ -87,6 +89,7 @@
             TIME_COMPONENT_BLUETOOTH,
             TIME_COMPONENT_CAMERA,
             TIME_COMPONENT_FLASHLIGHT,
+            TIME_COMPONENT_MOBILE_RADIO,
     })
     @Retention(RetentionPolicy.SOURCE)
     public static @interface TimeComponent {
@@ -100,8 +103,9 @@
     public static final int TIME_COMPONENT_AUDIO = 5;
     public static final int TIME_COMPONENT_VIDEO = 6;
     public static final int TIME_COMPONENT_FLASHLIGHT = 7;
+    public static final int TIME_COMPONENT_MOBILE_RADIO = 8;
 
-    public static final int TIME_COMPONENT_COUNT = 8;
+    public static final int TIME_COMPONENT_COUNT = 9;
 
     public static final int FIRST_CUSTOM_TIME_COMPONENT_ID = 1000;
     public static final int LAST_CUSTOM_TIME_COMPONENT_ID = 9999;
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 8148c45..e504946 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -318,6 +318,7 @@
          * @see #SDK_INT
          * @hide
          */
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
         @TestApi
         public static final int FIRST_SDK_INT = SystemProperties
                 .getInt("ro.product.first_api_level", 0);
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 6fe5777..b39c182 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -86,7 +86,7 @@
     Bundle getApplicationRestrictionsForUser(in String packageName, int userId);
     void setDefaultGuestRestrictions(in Bundle restrictions);
     Bundle getDefaultGuestRestrictions();
-    int removeUserOrSetEphemeral(int userId);
+    int removeUserOrSetEphemeral(int userId, boolean evenWhenDisallowed);
     boolean markGuestForDeletion(int userId);
     UserInfo findCurrentGuestUser();
     boolean isQuietModeEnabled(int userId);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 814a248..cbb3ba9 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -416,9 +416,21 @@
     public static final int GO_TO_SLEEP_REASON_QUIESCENT = 10;
 
     /**
+     * Go to sleep reason code: The last powered on display group has been removed.
      * @hide
      */
-    public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_QUIESCENT;
+    public static final int GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED = 11;
+
+    /**
+     * Go to sleep reason code: Every display group has been turned off.
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF = 12;
+
+    /**
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF;
 
     /**
      * @hide
@@ -435,6 +447,8 @@
             case GO_TO_SLEEP_REASON_ACCESSIBILITY: return "accessibility";
             case GO_TO_SLEEP_REASON_FORCE_SUSPEND: return "force_suspend";
             case GO_TO_SLEEP_REASON_INATTENTIVE: return "inattentive";
+            case GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED: return "display_group_removed";
+            case GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF: return "display_groups_turned_off";
             default: return Integer.toString(sleepReason);
         }
     }
@@ -521,11 +535,32 @@
             WAKE_REASON_WAKE_KEY,
             WAKE_REASON_WAKE_MOTION,
             WAKE_REASON_HDMI,
+            WAKE_REASON_DISPLAY_GROUP_ADDED,
+            WAKE_REASON_DISPLAY_GROUP_TURNED_ON,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface WakeReason{}
 
     /**
+     * @hide
+     */
+    @IntDef(prefix = { "GO_TO_SLEEP_REASON_" }, value = {
+            GO_TO_SLEEP_REASON_APPLICATION,
+            GO_TO_SLEEP_REASON_DEVICE_ADMIN,
+            GO_TO_SLEEP_REASON_TIMEOUT,
+            GO_TO_SLEEP_REASON_LID_SWITCH,
+            GO_TO_SLEEP_REASON_POWER_BUTTON,
+            GO_TO_SLEEP_REASON_HDMI,
+            GO_TO_SLEEP_REASON_SLEEP_BUTTON,
+            GO_TO_SLEEP_REASON_ACCESSIBILITY,
+            GO_TO_SLEEP_REASON_FORCE_SUSPEND,
+            GO_TO_SLEEP_REASON_INATTENTIVE,
+            GO_TO_SLEEP_REASON_QUIESCENT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface GoToSleepReason{}
+
+    /**
      * Wake up reason code: Waking for an unknown reason.
      * @hide
      */
@@ -589,6 +624,18 @@
     public static final int WAKE_REASON_LID = 9;
 
     /**
+     * Wake up reason code: Waking due to display group being added.
+     * @hide
+     */
+    public static final int WAKE_REASON_DISPLAY_GROUP_ADDED = 10;
+
+    /**
+     * Wake up reason code: Waking due to display group being powered on.
+     * @hide
+     */
+    public static final int WAKE_REASON_DISPLAY_GROUP_TURNED_ON = 11;
+
+    /**
      * Convert the wake reason to a string for debugging purposes.
      * @hide
      */
@@ -604,6 +651,8 @@
             case WAKE_REASON_WAKE_MOTION: return "WAKE_REASON_WAKE_MOTION";
             case WAKE_REASON_HDMI: return "WAKE_REASON_HDMI";
             case WAKE_REASON_LID: return "WAKE_REASON_LID";
+            case WAKE_REASON_DISPLAY_GROUP_ADDED: return "WAKE_REASON_DISPLAY_GROUP_ADDED";
+            case WAKE_REASON_DISPLAY_GROUP_TURNED_ON: return "WAKE_REASON_DISPLAY_GROUP_TURNED_ON";
             default: return Integer.toString(wakeReason);
         }
     }
@@ -1195,8 +1244,15 @@
         }
     }
 
-   /**
-     * Forces the device to go to sleep.
+    /**
+     * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group}
+     * to turn off.
+     *
+     * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is
+     * turned on it will be turned off. If all displays are off as a result of this action the
+     * device will be put to sleep. If the {@link com.android.server.display.DisplayGroup#DEFAULT
+     * default display group} is already off then nothing will happen.
+     *
      * <p>
      * Overrides all the wake locks that are held.
      * This is what happens when the power key is pressed to turn off the screen.
@@ -1219,7 +1275,14 @@
     }
 
     /**
-     * Forces the device to go to sleep.
+     * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group}
+     * to turn off.
+     *
+     * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is
+     * turned on it will be turned off. If all displays are off as a result of this action the
+     * device will be put to sleep. If the {@link com.android.server.display.DisplayGroup#DEFAULT
+     * default display group} is already off then nothing will happen.
+     *
      * <p>
      * Overrides all the wake locks that are held.
      * This is what happens when the power key is pressed to turn off the screen.
@@ -1249,9 +1312,15 @@
     }
 
     /**
-     * Forces the device to wake up from sleep.
+     * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group}
+     * to turn on.
+     *
+     * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is
+     * turned off it will be turned on. Additionally, if the device is asleep it will be awoken. If
+     * the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is already
+     * on then nothing will happen.
+     *
      * <p>
-     * If the device is currently asleep, wakes it up, otherwise does nothing.
      * This is what happens when the power key is pressed to turn on the screen.
      * </p><p>
      * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
@@ -1274,9 +1343,15 @@
     }
 
     /**
-     * Forces the device to wake up from sleep.
+     * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group}
+     * to turn on.
+     *
+     * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is
+     * turned off it will be turned on. Additionally, if the device is asleep it will be awoken. If
+     * the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is already
+     * on then nothing will happen.
+     *
      * <p>
-     * If the device is currently asleep, wakes it up, otherwise does nothing.
      * This is what happens when the power key is pressed to turn on the screen.
      * </p><p>
      * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
@@ -1303,9 +1378,13 @@
     }
 
     /**
-     * Forces the device to wake up from sleep.
+     * Forces the {@link android.view.Display#DEFAULT_DISPLAY default display} to turn on.
+     *
+     * <p>If the {@link android.view.Display#DEFAULT_DISPLAY default display} is turned off it will
+     * be turned on. Additionally, if the device is asleep it will be awoken. If the {@link
+     * android.view.Display#DEFAULT_DISPLAY default display} is already on then nothing will happen.
+     *
      * <p>
-     * If the device is currently asleep, wakes it up, otherwise does nothing.
      * This is what happens when the power key is pressed to turn on the screen.
      * </p><p>
      * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
diff --git a/core/java/android/os/SystemBatteryConsumer.java b/core/java/android/os/SystemBatteryConsumer.java
index 08e358f..49bf084 100644
--- a/core/java/android/os/SystemBatteryConsumer.java
+++ b/core/java/android/os/SystemBatteryConsumer.java
@@ -41,7 +41,7 @@
             // Reserved: APP
             DRAIN_TYPE_BLUETOOTH,
             DRAIN_TYPE_CAMERA,
-            DRAIN_TYPE_CELL,
+            DRAIN_TYPE_MOBILE_RADIO,
             DRAIN_TYPE_FLASHLIGHT,
             DRAIN_TYPE_IDLE,
             DRAIN_TYPE_MEMORY,
@@ -59,7 +59,7 @@
     public static final int DRAIN_TYPE_AMBIENT_DISPLAY = 0;
     public static final int DRAIN_TYPE_BLUETOOTH = 2;
     public static final int DRAIN_TYPE_CAMERA = 3;
-    public static final int DRAIN_TYPE_CELL = 4;
+    public static final int DRAIN_TYPE_MOBILE_RADIO = 4;
     public static final int DRAIN_TYPE_FLASHLIGHT = 5;
     public static final int DRAIN_TYPE_IDLE = 6;
     public static final int DRAIN_TYPE_MEMORY = 7;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index ed60baf..77183ac 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4058,14 +4058,18 @@
      * the current user, then set the user as ephemeral so that it will be removed when it is
      * stopped.
      *
+     * @param evenWhenDisallowed when {@code true}, user is removed even if the caller user has the
+     * {@link #DISALLOW_REMOVE_USER} or {@link #DISALLOW_REMOVE_MANAGED_PROFILE} restriction
+     *
      * @return the {@link RemoveResult} code
      * @hide
      */
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
             Manifest.permission.CREATE_USERS})
-    public @RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId) {
+    public @RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId,
+            boolean evenWhenDisallowed) {
         try {
-            return mService.removeUserOrSetEphemeral(userId);
+            return mService.removeUserOrSetEphemeral(userId, evenWhenDisallowed);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
diff --git a/core/java/android/os/storage/OWNERS b/core/java/android/os/storage/OWNERS
index 8af7de5..7e17a08 100644
--- a/core/java/android/os/storage/OWNERS
+++ b/core/java/android/os/storage/OWNERS
@@ -5,3 +5,6 @@
 corinac@google.com
 zezeozue@google.com
 maco@google.com
+sahanas@google.com
+abkaur@google.com
+chiangi@google.com
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index c28b59b..db55e1c 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -662,6 +662,11 @@
             // usage for that uid, keep it. Otherwise, remove it
             boolean isMostRecentForUid = true;
             for (int otherUsageNum = 0; otherUsageNum < rawUsages.size(); otherUsageNum++) {
+                // Do not compare this usage to itself
+                if (otherUsageNum == usageNum) {
+                    continue;
+                }
+
                 OpUsage otherUsage = rawUsages.get(otherUsageNum);
                 if (otherUsage.uid == usage.uid) {
                     if (otherUsage.isRunning && !usage.isRunning) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1a58bd7..0bb5365 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3083,7 +3083,9 @@
      */
     public static boolean canDrawOverlays(Context context) {
         return Settings.isCallingPackageAllowedToDrawOverlays(context, Process.myUid(),
-                context.getOpPackageName(), false);
+                context.getOpPackageName(), false) || context.checkSelfPermission(
+                Manifest.permission.SYSTEM_APPLICATION_OVERLAY)
+                == PackageManager.PERMISSION_GRANTED;
     }
 
     /**
diff --git a/core/java/android/se/OWNERS b/core/java/android/se/OWNERS
index f1539dc..5682fd3 100644
--- a/core/java/android/se/OWNERS
+++ b/core/java/android/se/OWNERS
@@ -1,4 +1,5 @@
 # Bug component: 456592
 
-cbrubaker@google.com
-vishwath@google.com
+zachoverflow@google.com
+alisher@google.com
+jackcwyu@google.com
diff --git a/core/java/android/se/omapi/OWNERS b/core/java/android/se/omapi/OWNERS
index f1539dc..5682fd3 100644
--- a/core/java/android/se/omapi/OWNERS
+++ b/core/java/android/se/omapi/OWNERS
@@ -1,4 +1,5 @@
 # Bug component: 456592
 
-cbrubaker@google.com
-vishwath@google.com
+zachoverflow@google.com
+alisher@google.com
+jackcwyu@google.com
diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java
index a5c5c61..333af91 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/core/java/android/se/omapi/SEService.java
@@ -22,7 +22,10 @@
 
 package android.se.omapi;
 
+import android.annotation.BroadcastBehavior;
 import android.annotation.NonNull;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -71,6 +74,28 @@
     }
 
     /**
+     * Broadcast Action: Intent to notify if the secure element state is changed.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(registeredOnly = true, protectedBroadcast = true)
+    public static final String ACTION_SECURE_ELEMENT_STATE_CHANGED =
+            "android.se.omapi.action.SECURE_ELEMENT_STATE_CHANGED";
+
+    /**
+     * Mandatory extra containing the reader name of the state changed secure element.
+     *
+     * @see Reader#getName()
+     */
+    public static final String EXTRA_READER_NAME = "android.se.omapi.extra.READER_NAME";
+
+    /**
+     * Mandatory extra containing the connected state of the state changed secure element.
+     *
+     * True if the secure element is connected correctly, false otherwise.
+     */
+    public static final String EXTRA_READER_STATE = "android.se.omapi.extra.READER_STATE";
+
+    /**
      * Listener object that allows the notification of the caller if this
      * SEService could be bound to the backend.
      */
diff --git a/core/java/android/service/attestation/IImpressionAttestationService.aidl b/core/java/android/service/attestation/IImpressionAttestationService.aidl
index fcbc51f..5ff8f17 100644
--- a/core/java/android/service/attestation/IImpressionAttestationService.aidl
+++ b/core/java/android/service/attestation/IImpressionAttestationService.aidl
@@ -39,7 +39,7 @@
      *        {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS}.
      * @param Callback The callback invoked to send back the impression token.
      */
-    void generateImpressionToken(in String salt, in HardwareBuffer screenshot, in Rect bounds,
+    void generateImpressionToken(in byte[] salt, in HardwareBuffer screenshot, in Rect bounds,
                                  in String hashAlgorithm, in RemoteCallback callback);
 
     /**
@@ -51,6 +51,6 @@
      * @param impressionToken The token to verify that it was generated by the system.
      * @param callback The callback invoked to send back the verification status.
      */
-    void verifyImpressionToken(in String salt, in ImpressionToken impressionToken,
+    void verifyImpressionToken(in byte[] salt, in ImpressionToken impressionToken,
                                in RemoteCallback callback);
 }
diff --git a/core/java/android/service/attestation/ImpressionAttestationService.java b/core/java/android/service/attestation/ImpressionAttestationService.java
index 05ad5f0..968d533 100644
--- a/core/java/android/service/attestation/ImpressionAttestationService.java
+++ b/core/java/android/service/attestation/ImpressionAttestationService.java
@@ -100,7 +100,7 @@
      * Returns null when the arguments sent are invalid.
      */
     @Nullable
-    public abstract ImpressionToken onGenerateImpressionToken(@NonNull String salt,
+    public abstract ImpressionToken onGenerateImpressionToken(@NonNull byte[] salt,
             @NonNull HardwareBuffer screenshot, @NonNull Rect bounds,
             @NonNull String hashAlgorithm);
 
@@ -109,16 +109,16 @@
      *
      * @param salt            The salt value to use when verifying the hmac. This should be the
      *                        same value that was passed to
-     *                        {@link #onGenerateImpressionToken(String,
+     *                        {@link #onGenerateImpressionToken(byte[],
      *                        HardwareBuffer, Rect, String)} to
      *                        generate the token.
      * @param impressionToken The token to verify that it was generated by the system.
      * @return true if the token can be verified that it was generated by the system.
      */
-    public abstract boolean onVerifyImpressionToken(@NonNull String salt,
+    public abstract boolean onVerifyImpressionToken(@NonNull byte[] salt,
             @NonNull ImpressionToken impressionToken);
 
-    private void generateImpressionToken(String salt, HardwareBuffer screenshot, Rect bounds,
+    private void generateImpressionToken(byte[] salt, HardwareBuffer screenshot, Rect bounds,
             String hashAlgorithm, RemoteCallback callback) {
         ImpressionToken impressionToken = onGenerateImpressionToken(salt, screenshot, bounds,
                 hashAlgorithm);
@@ -127,7 +127,7 @@
         callback.sendResult(data);
     }
 
-    private void verifyImpressionToken(String salt, ImpressionToken impressionToken,
+    private void verifyImpressionToken(byte[] salt, ImpressionToken impressionToken,
             RemoteCallback callback) {
         boolean verificationStatus = onVerifyImpressionToken(salt, impressionToken);
         final Bundle data = new Bundle();
@@ -138,7 +138,7 @@
     private final class ImpressionAttestationServiceWrapper extends
             IImpressionAttestationService.Stub {
         @Override
-        public void generateImpressionToken(String salt, HardwareBuffer screenshot, Rect bounds,
+        public void generateImpressionToken(byte[] salt, HardwareBuffer screenshot, Rect bounds,
                 String hashAlgorithm, RemoteCallback callback) {
             mHandler.sendMessage(
                     obtainMessage(ImpressionAttestationService::generateImpressionToken,
@@ -147,7 +147,7 @@
         }
 
         @Override
-        public void verifyImpressionToken(String salt, ImpressionToken impressionToken,
+        public void verifyImpressionToken(byte[] salt, ImpressionToken impressionToken,
                 RemoteCallback callback) {
             mHandler.sendMessage(obtainMessage(ImpressionAttestationService::verifyImpressionToken,
                     ImpressionAttestationService.this, salt, impressionToken, callback));
diff --git a/core/java/android/service/resumeonreboot/IResumeOnRebootService.aidl b/core/java/android/service/resumeonreboot/IResumeOnRebootService.aidl
new file mode 100644
index 0000000..d9b403c
--- /dev/null
+++ b/core/java/android/service/resumeonreboot/IResumeOnRebootService.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.resumeonreboot;
+
+import android.os.RemoteCallback;
+
+/** @hide */
+interface IResumeOnRebootService {
+    oneway void wrapSecret(in byte[] unwrappedBlob, in long lifeTimeInMillis, in RemoteCallback resultCallback);
+    oneway void unwrap(in byte[] wrappedBlob, in RemoteCallback resultCallback);
+}
\ No newline at end of file
diff --git a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
new file mode 100644
index 0000000..4ebaa96
--- /dev/null
+++ b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.resumeonreboot;
+
+import android.annotation.DurationMillisLong;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ParcelableException;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+
+import com.android.internal.os.BackgroundThread;
+
+import java.io.IOException;
+
+/**
+ * Base class for service that provides wrapping/unwrapping of the opaque blob needed for
+ * ResumeOnReboot operation. The package needs to provide a wrap/unwrap implementation for handling
+ * the opaque blob, that's secure even when on device keystore and clock is compromised. This can
+ * be achieved by using tamper-resistant hardware such as a secure element with a secure clock, or
+ * using a remote server to store and retrieve data and manage timing.
+ *
+ * <p>To extend this class, you must declare the service in your manifest file with the
+ * {@link android.Manifest.permission#BIND_RESUME_ON_REBOOT_SERVICE} permission,
+ * include an intent filter with the {@link #SERVICE_INTERFACE} action and mark the service as
+ * direct-boot aware. In addition, the package that contains the service must be granted
+ * {@link android.Manifest.permission#BIND_RESUME_ON_REBOOT_SERVICE}.
+ * For example:</p>
+ * <pre>
+ *     &lt;service android:name=".FooResumeOnRebootService"
+ *             android:exported="true"
+ *             android:priority="100"
+ *             android:directBootAware="true"
+ *             android:permission="android.permission.BIND_RESUME_ON_REBOOT_SERVICE"&gt;
+ *         &lt;intent-filter&gt;
+ *             &lt;action android:name="android.service.resumeonreboot.ResumeOnRebootService" /&gt;
+ *         &lt;/intent-filter&gt;
+ *     &lt;/service&gt;
+ * </pre>
+ *
+ * //TODO: Replace this with public link when available.
+ *
+ * @hide
+ * @see
+ * <a href="https://goto.google.com/server-based-ror">https://goto.google.com/server-based-ror</a>
+ */
+@SystemApi
+public abstract class ResumeOnRebootService extends Service {
+
+    /**
+     * The intent that the service must respond to. Add it to the intent filter of the service.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE =
+            "android.service.resumeonreboot.ResumeOnRebootService";
+    /** @hide */
+    public static final String UNWRAPPED_BLOB_KEY = "unrwapped_blob_key";
+    /** @hide */
+    public static final String WRAPPED_BLOB_KEY = "wrapped_blob_key";
+    /** @hide */
+    public static final String EXCEPTION_KEY = "exception_key";
+
+    private final Handler mHandler = BackgroundThread.getHandler();
+
+    /**
+     * Implementation for wrapping the opaque blob used for resume-on-reboot prior to
+     * reboot. The service should not assume any structure of the blob to be wrapped. The
+     * implementation should wrap the opaque blob in a reasonable time or throw {@link IOException}
+     * if it's unable to complete the action.
+     *
+     * @param blob             The opaque blob with size on the order of 100 bytes.
+     * @param lifeTimeInMillis The life time of the blob. This must be strictly enforced by the
+     *                         implementation and any attempt to unWrap the wrapped blob returned by
+     *                         this function after expiration should
+     *                         fail.
+     * @return Wrapped blob to be persisted across reboot with size on the order of 100 bytes.
+     * @throws IOException if the implementation is unable to wrap the blob successfully.
+     */
+    @NonNull
+    public abstract byte[] onWrap(@NonNull byte[] blob, @DurationMillisLong long lifeTimeInMillis)
+            throws IOException;
+
+    /**
+     * Implementation for unwrapping the wrapped blob used for resume-on-reboot after reboot. This
+     * operation would happen after reboot during direct boot mode (i.e before device is unlocked
+     * for the first time). The implementation should unwrap the wrapped blob in a reasonable time
+     * and returns the result or throw {@link IOException} if it's unable to complete the action
+     * and {@link IllegalArgumentException} if {@code unwrapBlob} fails because the wrappedBlob is
+     * stale.
+     *
+     * @param wrappedBlob The wrapped blob with size on the order of 100 bytes.
+     * @return Unwrapped blob used for resume-on-reboot with the size on the order of 100 bytes.
+     * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully.
+     */
+    @NonNull
+    public abstract byte[] onUnwrap(@NonNull byte[] wrappedBlob) throws IOException;
+
+    private final android.service.resumeonreboot.IResumeOnRebootService mInterface =
+            new android.service.resumeonreboot.IResumeOnRebootService.Stub() {
+
+                @Override
+                public void wrapSecret(byte[] unwrappedBlob,
+                        @DurationMillisLong long lifeTimeInMillis,
+                        RemoteCallback resultCallback) throws RemoteException {
+                    mHandler.post(() -> {
+                        try {
+                            byte[] wrappedBlob = onWrap(unwrappedBlob,
+                                    lifeTimeInMillis);
+                            Bundle bundle = new Bundle();
+                            bundle.putByteArray(WRAPPED_BLOB_KEY, wrappedBlob);
+                            resultCallback.sendResult(bundle);
+                        } catch (Throwable e) {
+                            Bundle bundle = new Bundle();
+                            bundle.putParcelable(EXCEPTION_KEY, new ParcelableException(e));
+                            resultCallback.sendResult(bundle);
+                        }
+                    });
+                }
+
+                @Override
+                public void unwrap(byte[] wrappedBlob, RemoteCallback resultCallback)
+                        throws RemoteException {
+                    mHandler.post(() -> {
+                        try {
+                            byte[] unwrappedBlob = onUnwrap(wrappedBlob);
+                            Bundle bundle = new Bundle();
+                            bundle.putByteArray(UNWRAPPED_BLOB_KEY, unwrappedBlob);
+                            resultCallback.sendResult(bundle);
+                        } catch (Throwable e) {
+                            Bundle bundle = new Bundle();
+                            bundle.putParcelable(EXCEPTION_KEY, new ParcelableException(e));
+                            resultCallback.sendResult(bundle);
+                        }
+                    });
+                }
+            };
+
+    @Nullable
+    @Override
+    public IBinder onBind(@Nullable Intent intent) {
+        return mInterface.asBinder();
+    }
+}
diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java
index bd1b44c..e492584 100644
--- a/core/java/android/service/textservice/SpellCheckerService.java
+++ b/core/java/android/service/textservice/SpellCheckerService.java
@@ -16,11 +16,6 @@
 
 package android.service.textservice;
 
-import com.android.internal.textservice.ISpellCheckerService;
-import com.android.internal.textservice.ISpellCheckerServiceCallback;
-import com.android.internal.textservice.ISpellCheckerSession;
-import com.android.internal.textservice.ISpellCheckerSessionListener;
-
 import android.app.Service;
 import android.content.Intent;
 import android.os.Bundle;
@@ -34,6 +29,11 @@
 import android.view.textservice.SuggestionsInfo;
 import android.view.textservice.TextInfo;
 
+import com.android.internal.textservice.ISpellCheckerService;
+import com.android.internal.textservice.ISpellCheckerServiceCallback;
+import com.android.internal.textservice.ISpellCheckerSession;
+import com.android.internal.textservice.ISpellCheckerSessionListener;
+
 import java.lang.ref.WeakReference;
 import java.text.BreakIterator;
 import java.util.ArrayList;
@@ -231,6 +231,18 @@
         public Bundle getBundle() {
             return mInternalSession.getBundle();
         }
+
+        /**
+         * Returns result attributes supported for this session.
+         *
+         * <p>The session implementation should not set attributes that are not included in the
+         * return value of {@code getSupportedAttributes()} when creating {@link SuggestionsInfo}.
+         *
+         * @return The supported result attributes for this session
+         */
+        public @SuggestionsInfo.ResultAttrs int getSupportedAttributes() {
+            return mInternalSession.getSupportedAttributes();
+        }
     }
 
     // Preventing from exposing ISpellCheckerSession.aidl, create an internal class.
@@ -239,13 +251,16 @@
         private final Session mSession;
         private final String mLocale;
         private final Bundle mBundle;
+        private final @SuggestionsInfo.ResultAttrs int mSupportedAttributes;
 
         public InternalISpellCheckerSession(String locale, ISpellCheckerSessionListener listener,
-                Bundle bundle, Session session) {
+                Bundle bundle, Session session,
+                @SuggestionsInfo.ResultAttrs int supportedAttributes) {
             mListener = listener;
             mSession = session;
             mLocale = locale;
             mBundle = bundle;
+            mSupportedAttributes = supportedAttributes;
             session.setInternalISpellCheckerSession(this);
         }
 
@@ -303,6 +318,10 @@
         public Bundle getBundle() {
             return mBundle;
         }
+
+        public @SuggestionsInfo.ResultAttrs int getSupportedAttributes() {
+            return mSupportedAttributes;
+        }
     }
 
     private static class SpellCheckerServiceBinder extends ISpellCheckerService.Stub {
@@ -323,11 +342,14 @@
          *                 {@link Session#onGetSuggestionsMultiple(TextInfo[], int, boolean)} and
          *                 {@link Session#onGetSuggestions(TextInfo, int)}
          * @param bundle bundle to be returned from {@link Session#getBundle()}
+         * @param supportedAttributes A union of {@link SuggestionsInfo} attributes that the spell
+         *                            checker can set in the spell checking results.
          * @param callback IPC channel to return the result to the caller in an asynchronous manner
          */
         @Override
         public void getISpellCheckerSession(
                 String locale, ISpellCheckerSessionListener listener, Bundle bundle,
+                @SuggestionsInfo.ResultAttrs int supportedAttributes,
                 ISpellCheckerServiceCallback callback) {
             final SpellCheckerService service = mInternalServiceRef.get();
             final InternalISpellCheckerSession internalSession;
@@ -337,8 +359,8 @@
                 internalSession = null;
             } else {
                 final Session session = service.createSession();
-                internalSession =
-                        new InternalISpellCheckerSession(locale, listener, bundle, session);
+                internalSession = new InternalISpellCheckerSession(
+                        locale, listener, bundle, session, supportedAttributes);
                 session.onCreate();
             }
             try {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c2d990a..8b8645c 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2151,9 +2151,8 @@
          * visible window.
          * @hide
          */
-        @SystemApi
         @RequiresPermission(permission.SYSTEM_APPLICATION_OVERLAY)
-        public static final int SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY = 0x00000008;
+        public static final int PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY = 0x00000008;
 
         /** In a multiuser system if this flag is set and the owner is a system process then this
          * window will appear on all user screens. This overrides the default behavior of window
@@ -2352,7 +2351,6 @@
         @IntDef(flag = true, prefix = { "SYSTEM_FLAG_" }, value = {
                 SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
                 SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
-                SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY,
         })
         public @interface SystemFlags {}
 
@@ -2386,7 +2384,7 @@
                 PRIVATE_FLAG_TRUSTED_OVERLAY,
                 PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME,
                 PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP,
-                SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY,
+                PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY,
         })
         public @interface PrivateFlags {}
 
@@ -2501,9 +2499,9 @@
                         equals = PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP,
                         name = "INTERCEPT_GLOBAL_DRAG_AND_DROP"),
                 @ViewDebug.FlagToString(
-                        mask = SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY,
-                        equals = SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY,
-                        name = "SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY")
+                        mask = PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY,
+                        equals = PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY,
+                        name = "PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY")
         })
         @PrivateFlags
         @TestApi
@@ -3375,6 +3373,37 @@
         }
 
         /**
+         * When set on {@link LayoutParams#TYPE_APPLICATION_OVERLAY} windows they stay visible,
+         * even if {@link LayoutParams#SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS} is set for
+         * another visible window.
+         * @hide
+         */
+        @SystemApi
+        @RequiresPermission(permission.SYSTEM_APPLICATION_OVERLAY)
+        public void setSystemApplicationOverlay(boolean isSystemApplicationOverlay) {
+            if (isSystemApplicationOverlay) {
+                privateFlags |= PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY;
+            } else {
+                privateFlags &= ~PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY;
+            }
+        }
+
+        /**
+         * Returns if this window is marked as being a system application overlay.
+         * @see LayoutParams#setSystemApplicationOverlay(boolean)
+         *
+         * <p>Note: the owner of the window must hold
+         * {@link android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY} for this to have any
+         * effect.
+         * @hide
+         */
+        @SystemApi
+        public boolean isSystemApplicationOverlay() {
+            return (privateFlags & PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY)
+                    == PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY;
+        }
+
+        /**
          * @return the insets types that this window is avoiding overlapping.
          */
         public @InsetsType int getFitInsetsTypes() {
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index dd55f04..7668d80 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.annotation.IntDef;
+import android.os.PowerManager;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -108,6 +109,27 @@
         void onPointerEvent(MotionEvent motionEvent);
     }
 
+    @IntDef(prefix = { "OFF_BECAUSE_OF_" }, value = {
+            OFF_BECAUSE_OF_ADMIN,
+            OFF_BECAUSE_OF_USER,
+            OFF_BECAUSE_OF_TIMEOUT,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface OffReason{}
+
+    static @OffReason int translateSleepReasonToOffReason(
+            @PowerManager.GoToSleepReason int reason) {
+        switch (reason) {
+            case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
+                return OFF_BECAUSE_OF_ADMIN;
+            case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
+            case PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE:
+                return OFF_BECAUSE_OF_TIMEOUT;
+            default:
+                return OFF_BECAUSE_OF_USER;
+        }
+    }
+
     /** Screen turned off because of a device admin */
     int OFF_BECAUSE_OF_ADMIN = 1;
     /** Screen turned off because of power button */
@@ -137,6 +159,23 @@
         }
     }
 
+    static @OnReason int translateWakeReasonToOnReason(@PowerManager.WakeReason int reason) {
+        switch (reason) {
+            case PowerManager.WAKE_REASON_POWER_BUTTON:
+            case PowerManager.WAKE_REASON_PLUGGED_IN:
+            case PowerManager.WAKE_REASON_GESTURE:
+            case PowerManager.WAKE_REASON_CAMERA_LAUNCH:
+            case PowerManager.WAKE_REASON_WAKE_KEY:
+            case PowerManager.WAKE_REASON_WAKE_MOTION:
+            case PowerManager.WAKE_REASON_LID:
+                return ON_BECAUSE_OF_USER;
+            case PowerManager.WAKE_REASON_APPLICATION:
+                return ON_BECAUSE_OF_APPLICATION;
+            default:
+                return ON_BECAUSE_OF_UNKNOWN;
+        }
+    }
+
     /** Screen turned on because of a user-initiated action. */
     int ON_BECAUSE_OF_USER = 1;
     /** Screen turned on because of an application request or event */
diff --git a/core/java/android/view/textservice/SuggestionsInfo.java b/core/java/android/view/textservice/SuggestionsInfo.java
index 1301c49..775a6bd 100644
--- a/core/java/android/view/textservice/SuggestionsInfo.java
+++ b/core/java/android/view/textservice/SuggestionsInfo.java
@@ -16,11 +16,15 @@
 
 package android.view.textservice;
 
+import android.annotation.IntDef;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import com.android.internal.util.ArrayUtils;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * This class contains a metadata of suggestions from the text service
  */
@@ -28,6 +32,22 @@
     private static final String[] EMPTY = ArrayUtils.emptyArray(String.class);
 
     /**
+     * An internal annotation to indicate that one ore more combinations of
+     * <code>RESULT_ATTR_</code> flags are expected.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "RESULT_ATTR_" }, value = {
+            RESULT_ATTR_IN_THE_DICTIONARY,
+            RESULT_ATTR_LOOKS_LIKE_TYPO,
+            RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS,
+            RESULT_ATTR_LOOKS_LIKE_GRAMMAR_ERROR,
+            RESULT_ATTR_DONT_SHOW_UI_FOR_SUGGESTIONS,
+    })
+    public @interface ResultAttrs {}
+
+    /**
      * Flag of the attributes of the suggestions that can be obtained by
      * {@link #getSuggestionsAttributes}: this tells that the requested word was found
      * in the dictionary in the text service.
@@ -63,7 +83,7 @@
      */
     public static final int RESULT_ATTR_DONT_SHOW_UI_FOR_SUGGESTIONS = 0x0010;
 
-    private final int mSuggestionsAttributes;
+    private final @ResultAttrs int mSuggestionsAttributes;
     private final String[] mSuggestions;
     private final boolean mSuggestionsAvailable;
     private int mCookie;
@@ -85,8 +105,8 @@
      * @param cookie the cookie of the input TextInfo
      * @param sequence the cookie of the input TextInfo
      */
-    public SuggestionsInfo(
-            int suggestionsAttributes, String[] suggestions, int cookie, int sequence) {
+    public SuggestionsInfo(@ResultAttrs int suggestionsAttributes, String[] suggestions, int cookie,
+            int sequence) {
         if (suggestions == null) {
             mSuggestions = EMPTY;
             mSuggestionsAvailable = false;
@@ -152,7 +172,7 @@
      * in its dictionary or not and whether the spell checker has confident suggestions for the
      * word or not.
      */
-    public int getSuggestionsAttributes() {
+    public @ResultAttrs int getSuggestionsAttributes() {
         return mSuggestionsAttributes;
     }
 
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index 578ed8c..0a1aea3 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.SystemService;
 import android.annotation.UserIdInt;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -139,21 +140,58 @@
     }
 
     /**
-     * Get a spell checker session for the specified spell checker
-     * @param locale the locale for the spell checker. If {@code locale} is null and
-     * referToSpellCheckerLanguageSettings is true, the locale specified in Settings will be
-     * returned. If {@code locale} is not null and referToSpellCheckerLanguageSettings is true,
-     * the locale specified in Settings will be returned only when it is same as {@code locale}.
-     * Exceptionally, when referToSpellCheckerLanguageSettings is true and {@code locale} is
-     * only language (e.g. "en"), the specified locale in Settings (e.g. "en_US") will be
-     * selected.
-     * @param listener a spell checker session lister for getting results from a spell checker.
-     * @param referToSpellCheckerLanguageSettings if true, the session for one of enabled
-     * languages in settings will be returned.
-     * @return the spell checker session of the spell checker
+     * Get a spell checker session from the spell checker.
+     *
+     * <p>{@link SuggestionsInfo#RESULT_ATTR_IN_THE_DICTIONARY},
+     * {@link SuggestionsInfo#RESULT_ATTR_LOOKS_LIKE_TYPO}, and
+     * {@link SuggestionsInfo#RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS} will be passed to the spell
+     * checker as supported attributes.
+     *
+     * @see #newSpellCheckerSession(Bundle, Locale, SpellCheckerSessionListener, boolean, int)
+     * @param bundle A bundle to pass to the spell checker.
+     * @param locale The locale for the spell checker.
+     * @param listener A spell checker session lister for getting results from the spell checker.
+     * @param referToSpellCheckerLanguageSettings If true, the session for one of enabled
+     *                                            languages in settings will be used.
+     * @return A spell checker session from the spell checker.
      */
-    public SpellCheckerSession newSpellCheckerSession(Bundle bundle, Locale locale,
-            SpellCheckerSessionListener listener, boolean referToSpellCheckerLanguageSettings) {
+    @Nullable
+    public SpellCheckerSession newSpellCheckerSession(@Nullable Bundle bundle,
+            @Nullable Locale locale,
+            @NonNull SpellCheckerSessionListener listener,
+            boolean referToSpellCheckerLanguageSettings) {
+        return newSpellCheckerSession(bundle, locale, listener, referToSpellCheckerLanguageSettings,
+                SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY
+                        | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO
+                        | SuggestionsInfo.RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS);
+    }
+
+    /**
+     * Get a spell checker session from the spell checker.
+     *
+     * <p>If {@code locale} is null and {@code referToSpellCheckerLanguageSettings} is true, the
+     * locale specified in Settings will be used. If {@code locale} is not null and
+     * {@code referToSpellCheckerLanguageSettings} is true, the locale specified in Settings will be
+     * returned only when it is same as {@code locale}.
+     * Exceptionally, when {@code referToSpellCheckerLanguageSettings} is true and {@code locale} is
+     * language only (e.g. "en"), the specified locale in Settings (e.g. "en_US") will be
+     * selected.
+     *
+     * @param bundle A bundle to pass to the spell checker.
+     * @param locale The locale for the spell checker.
+     * @param listener A spell checker session lister for getting results from a spell checker.
+     * @param referToSpellCheckerLanguageSettings If true, the session for one of enabled
+     *                                            languages in settings will be used.
+     * @param supportedAttributes A union of {@link SuggestionsInfo} attributes that the spell
+     *                            checker can set in the spell checking results.
+     * @return The spell checker session of the spell checker.
+     */
+    @Nullable
+    public SpellCheckerSession newSpellCheckerSession(@Nullable Bundle bundle,
+            @SuppressLint("UseIcu") @Nullable Locale locale,
+            @NonNull SpellCheckerSessionListener listener,
+            @SuppressLint("ListenerLast") boolean referToSpellCheckerLanguageSettings,
+            @SuppressLint("ListenerLast") @SuggestionsInfo.ResultAttrs int supportedAttributes) {
         if (listener == null) {
             throw new NullPointerException();
         }
@@ -210,7 +248,7 @@
         try {
             mService.getSpellCheckerService(mUserId, sci.getId(), subtypeInUse.getLocale(),
                     session.getTextServicesSessionListener(),
-                    session.getSpellCheckerSessionListener(), bundle);
+                    session.getSpellCheckerSessionListener(), bundle, supportedAttributes);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index b06fa1a..97d98fd 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -129,7 +129,11 @@
             mSpellCheckerSession = mTextServicesManager.newSpellCheckerSession(
                     null /* Bundle not currently used by the textServicesManager */,
                     mCurrentLocale, this,
-                    false /* means any available languages from current spell checker */);
+                    false /* means any available languages from current spell checker */,
+                    SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY
+                            | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO
+                            | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_GRAMMAR_ERROR
+                            | SuggestionsInfo.RESULT_ATTR_DONT_SHOW_UI_FOR_SUGGESTIONS);
             mIsSentenceSpellCheckSupported = true;
         }
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index f105320..ee3c12c 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -860,14 +860,11 @@
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     protected int mScreenState = Display.STATE_UNKNOWN;
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    protected StopwatchTimer mScreenOnTimer;
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    protected StopwatchTimer mScreenDozeTimer;
+    StopwatchTimer mScreenOnTimer;
+    StopwatchTimer mScreenDozeTimer;
 
     int mScreenBrightnessBin = -1;
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    protected final StopwatchTimer[] mScreenBrightnessTimer =
+    final StopwatchTimer[] mScreenBrightnessTimer =
             new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS];
 
     boolean mPretendScreenOff;
@@ -912,8 +909,7 @@
     int mUsbDataState = USB_DATA_UNKNOWN;
 
     int mGpsSignalQualityBin = -1;
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    protected final StopwatchTimer[] mGpsSignalQualityTimer =
+    final StopwatchTimer[] mGpsSignalQualityTimer =
         new StopwatchTimer[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];
 
     int mPhoneSignalStrengthBin = -1;
@@ -929,6 +925,7 @@
 
     final LongSamplingCounter[] mNetworkByteActivityCounters =
             new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
+
     final LongSamplingCounter[] mNetworkPacketActivityCounters =
             new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
 
@@ -948,8 +945,7 @@
     /**
      * The Bluetooth controller activity (time in tx, rx, idle, and power consumed) for the device.
      */
-    @VisibleForTesting
-    protected ControllerActivityCounterImpl mBluetoothActivity;
+    ControllerActivityCounterImpl mBluetoothActivity;
 
     /**
      * The Modem controller activity (time in tx, rx, idle, and power consumed) for the device.
@@ -993,8 +989,7 @@
     StopwatchTimer mWifiActiveTimer;
 
     int mBluetoothScanNesting;
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    protected StopwatchTimer mBluetoothScanTimer;
+    StopwatchTimer mBluetoothScanTimer;
 
     int mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
     long mMobileRadioActiveStartTimeMs;
@@ -10718,6 +10713,31 @@
         mHandler = new MyHandler(handler.getLooper());
         mConstants = new Constants(mHandler);
         mStartCount++;
+        initTimersAndCounters();
+        mOnBattery = mOnBatteryInternal = false;
+        long uptimeUs = mClocks.uptimeMillis() * 1000;
+        long realtimeUs = mClocks.elapsedRealtime() * 1000;
+        initTimes(uptimeUs, realtimeUs);
+        mStartPlatformVersion = mEndPlatformVersion = Build.ID;
+        mDischargeStartLevel = 0;
+        mDischargeUnplugLevel = 0;
+        mDischargePlugLevel = -1;
+        mDischargeCurrentLevel = 0;
+        mCurrentBatteryLevel = 0;
+        initDischarge(realtimeUs);
+        clearHistoryLocked();
+        updateDailyDeadlineLocked();
+        mPlatformIdleStateCallback = cb;
+        mMeasuredEnergyRetriever = energyStatsCb;
+        mUserInfoProvider = userInfoProvider;
+
+        // Notify statsd that the system is initially not in doze.
+        mDeviceIdleMode = DEVICE_IDLE_MODE_OFF;
+        FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mDeviceIdleMode);
+    }
+
+    @VisibleForTesting
+    protected void initTimersAndCounters() {
         mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase);
         mScreenDozeTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase);
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
@@ -10789,26 +10809,6 @@
         mDischargeLightDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
         mDischargeDeepDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
         mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
-        mOnBattery = mOnBatteryInternal = false;
-        long uptimeUs = mClocks.uptimeMillis() * 1000;
-        long realtimeUs = mClocks.elapsedRealtime() * 1000;
-        initTimes(uptimeUs, realtimeUs);
-        mStartPlatformVersion = mEndPlatformVersion = Build.ID;
-        mDischargeStartLevel = 0;
-        mDischargeUnplugLevel = 0;
-        mDischargePlugLevel = -1;
-        mDischargeCurrentLevel = 0;
-        mCurrentBatteryLevel = 0;
-        initDischarge(realtimeUs);
-        clearHistoryLocked();
-        updateDailyDeadlineLocked();
-        mPlatformIdleStateCallback = cb;
-        mMeasuredEnergyRetriever = energyStatsCb;
-        mUserInfoProvider = userInfoProvider;
-
-        // Notify statsd that the system is initially not in doze.
-        mDeviceIdleMode = DEVICE_IDLE_MODE_OFF;
-        FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mDeviceIdleMode);
     }
 
     @UnsupportedAppUsage
@@ -11623,7 +11623,8 @@
     @GuardedBy("mModemNetworkLock")
     private NetworkStats mLastModemNetworkStats = new NetworkStats(0, -1);
 
-    private NetworkStats readNetworkStatsLocked(String[] ifaces) {
+    @VisibleForTesting
+    protected NetworkStats readNetworkStatsLocked(String[] ifaces) {
         try {
             if (!ArrayUtils.isEmpty(ifaces)) {
                 INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
@@ -11922,7 +11923,7 @@
     /**
      * Distribute Cell radio energy info and network traffic to apps.
      */
-    public void updateMobileRadioState(@Nullable final ModemActivityInfo activityInfo,
+    public void noteModemControllerActivity(@Nullable final ModemActivityInfo activityInfo,
             long elapsedRealtimeMs, long uptimeMs) {
         if (DEBUG_ENERGY) {
             Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
index 790d2e5..e3bd64d 100644
--- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
+++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
@@ -15,7 +15,12 @@
  */
 package com.android.internal.os;
 
+import android.os.BatteryConsumer;
 import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.SystemBatteryConsumer;
+import android.os.UidBatteryConsumer;
 import android.os.UserHandle;
 import android.telephony.CellSignalStrength;
 import android.util.Log;
@@ -26,103 +31,146 @@
 public class MobileRadioPowerCalculator extends PowerCalculator {
     private static final String TAG = "MobRadioPowerCalculator";
     private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
-    private final double mPowerRadioOn;
-    private final double[] mPowerBins = new double[CellSignalStrength.getNumSignalStrengthLevels()];
-    private final double mPowerScan;
-    private BatteryStats mStats;
-    private long mTotalAppMobileActiveMs = 0;
 
-    /**
-     * Return estimated power (in mAs) of sending or receiving a packet with the mobile radio.
-     */
-    private double getMobilePowerPerPacket(long rawRealtimeUs, int statsType) {
-        final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system
-        final double MOBILE_POWER = mPowerRadioOn / 3600;
+    private static final int NUM_SIGNAL_STRENGTH_LEVELS =
+            CellSignalStrength.getNumSignalStrengthLevels();
 
-        final long mobileRx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
-                statsType);
-        final long mobileTx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,
-                statsType);
-        final long mobileData = mobileRx + mobileTx;
+    private final UsageBasedPowerEstimator mActivePowerEstimator;
+    private final UsageBasedPowerEstimator[] mIdlePowerEstimators =
+            new UsageBasedPowerEstimator[NUM_SIGNAL_STRENGTH_LEVELS];
+    private final UsageBasedPowerEstimator mScanPowerEstimator;
 
-        final long radioDataUptimeMs =
-                mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
-        final double mobilePps = (mobileData != 0 && radioDataUptimeMs != 0)
-                ? (mobileData / (double) radioDataUptimeMs)
-                : (((double) MOBILE_BPS) / 8 / 2048);
-        return (MOBILE_POWER / mobilePps) / (60 * 60);
+    private static class PowerAndDuration {
+        public long durationMs;
+        public double powerMah;
+        public long totalAppDurationMs;
+        public long signalDurationMs;
+        public long noCoverageDurationMs;
     }
 
     public MobileRadioPowerCalculator(PowerProfile profile) {
-        double temp =
+        // Power consumption when radio is active
+        double powerRadioActiveMa =
                 profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ACTIVE, -1);
-        if (temp != -1) {
-            mPowerRadioOn = temp;
-        } else {
+        if (powerRadioActiveMa == -1) {
             double sum = 0;
             sum += profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
-            for (int i = 0; i < mPowerBins.length; i++) {
+            for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) {
                 sum += profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
             }
-            mPowerRadioOn = sum / (mPowerBins.length + 1);
+            powerRadioActiveMa = sum / (NUM_SIGNAL_STRENGTH_LEVELS + 1);
         }
 
-        temp = profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ON, -1);
-        if (temp != -1) {
-            for (int i = 0; i < mPowerBins.length; i++) {
-                mPowerBins[i] = profile.getAveragePower(PowerProfile.POWER_RADIO_ON, i);
+        mActivePowerEstimator = new UsageBasedPowerEstimator(powerRadioActiveMa);
+
+        // Power consumption when radio is on, but idle
+        if (profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ON, -1) != -1) {
+            for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) {
+                mIdlePowerEstimators[i] = new UsageBasedPowerEstimator(
+                        profile.getAveragePower(PowerProfile.POWER_RADIO_ON, i));
             }
         } else {
             double idle = profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE);
-            mPowerBins[0] = idle * 25 / 180;
-            for (int i = 1; i < mPowerBins.length; i++) {
-                mPowerBins[i] = Math.max(1, idle / 256);
+
+            // Magical calculations preserved for historical compatibility
+            mIdlePowerEstimators[0] = new UsageBasedPowerEstimator(idle * 25 / 180);
+            for (int i = 1; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) {
+                mIdlePowerEstimators[i] = new UsageBasedPowerEstimator(Math.max(1, idle / 256));
             }
         }
 
-        mPowerScan = profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_SCANNING, 0);
+        mScanPowerEstimator = new UsageBasedPowerEstimator(
+                profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_SCANNING, 0));
+    }
+
+    @Override
+    public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
+            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
+            SparseArray<UserHandle> asUsers) {
+
+        PowerAndDuration total = new PowerAndDuration();
+
+        final double powerPerPacketMah = getMobilePowerPerPacket(batteryStats, rawRealtimeUs,
+                BatteryStats.STATS_SINCE_CHARGED);
+        final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
+                builder.getUidBatteryConsumerBuilders();
+        for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
+            final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
+            final BatteryStats.Uid uid = app.getBatteryStatsUid();
+            calculateApp(app, uid, powerPerPacketMah, total);
+        }
+
+        calculateRemaining(total, batteryStats, rawRealtimeUs);
+
+        if (total.powerMah != 0) {
+            builder.getOrCreateSystemBatteryConsumerBuilder(
+                    SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO)
+                    .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_MOBILE_RADIO,
+                            total.durationMs)
+                    .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, total.powerMah);
+        }
+    }
+
+    private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+            double powerPerPacketMah, PowerAndDuration total) {
+        final long radioActiveDurationMs = calculateDuration(u, BatteryStats.STATS_SINCE_CHARGED);
+        total.totalAppDurationMs += radioActiveDurationMs;
+
+        final double powerMah = calculatePower(u, powerPerPacketMah, radioActiveDurationMs);
+
+        app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_MOBILE_RADIO,
+                radioActiveDurationMs)
+                .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, powerMah);
     }
 
     @Override
     public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
             long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
-        mStats = batteryStats;
-        super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers);
+        final double mobilePowerPerPacket = getMobilePowerPerPacket(batteryStats, rawRealtimeUs,
+                statsType);
+        PowerAndDuration total = new PowerAndDuration();
+        for (int i = sippers.size() - 1; i >= 0; i--) {
+            final BatterySipper app = sippers.get(i);
+            if (app.drainType == BatterySipper.DrainType.APP) {
+                final BatteryStats.Uid u = app.uidObj;
+                calculateApp(app, u, statsType, mobilePowerPerPacket, total);
+            }
+        }
 
         BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);
-        calculateRemaining(radio, mStats, rawRealtimeUs, rawUptimeUs, statsType);
-        radio.sumPower();
+        calculateRemaining(total, batteryStats, rawRealtimeUs);
+        if (total.powerMah != 0) {
+            if (total.signalDurationMs != 0) {
+                radio.noCoveragePercent =
+                        total.noCoverageDurationMs * 100.0 / total.signalDurationMs;
+            }
+            radio.mobileActive = total.durationMs;
+            radio.mobileActiveCount = batteryStats.getMobileRadioActiveUnknownCount(statsType);
+            radio.mobileRadioPowerMah = total.powerMah;
+            radio.sumPower();
+        }
         if (radio.totalPowerMah > 0) {
             sippers.add(radio);
         }
     }
 
-    @Override
-    protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
-            long rawUptimeUs, int statsType) {
+    private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType,
+            double powerPerPacketMah, PowerAndDuration total) {
+        app.mobileActive = calculateDuration(u, statsType);
+        app.mobileRadioPowerMah = calculatePower(u, powerPerPacketMah, app.mobileActive);
+        total.totalAppDurationMs += app.mobileActive;
+
         // Add cost of mobile traffic.
         app.mobileRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
                 statsType);
         app.mobileTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,
                 statsType);
-        app.mobileActive = u.getMobileRadioActiveTime(statsType) / 1000;
         app.mobileActiveCount = u.getMobileRadioActiveCount(statsType);
         app.mobileRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_RX_DATA,
                 statsType);
         app.mobileTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_TX_DATA,
                 statsType);
 
-        if (app.mobileActive > 0) {
-            // We are tracking when the radio is up, so can use the active time to
-            // determine power use.
-            mTotalAppMobileActiveMs += app.mobileActive;
-            app.mobileRadioPowerMah = (app.mobileActive * mPowerRadioOn) / (1000 * 60 * 60);
-        } else {
-            // We are not tracking when the radio is up, so must approximate power use
-            // based on the number of packets.
-            app.mobileRadioPowerMah = (app.mobileRxPackets + app.mobileTxPackets)
-                    * getMobilePowerPerPacket(rawRealtimeUs, statsType);
-        }
         if (DEBUG && app.mobileRadioPowerMah != 0) {
             Log.d(TAG, "UID " + u.getUid() + ": mobile packets "
                     + (app.mobileRxPackets + app.mobileTxPackets)
@@ -131,52 +179,80 @@
         }
     }
 
-    private void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
-            long rawUptimeUs, int statsType) {
-        double power = 0;
+    private long calculateDuration(BatteryStats.Uid u, int statsType) {
+        return u.getMobileRadioActiveTime(statsType) / 1000;
+    }
+
+    private double calculatePower(BatteryStats.Uid u, double powerPerPacketMah,
+            long radioActiveDurationMs) {
+        if (radioActiveDurationMs > 0) {
+            // We are tracking when the radio is up, so can use the active time to
+            // determine power use.
+            return mActivePowerEstimator.calculatePower(radioActiveDurationMs);
+        } else {
+            // We are not tracking when the radio is up, so must approximate power use
+            // based on the number of packets.
+            final long mobileRxPackets = u.getNetworkActivityPackets(
+                    BatteryStats.NETWORK_MOBILE_RX_DATA,
+                    BatteryStats.STATS_SINCE_CHARGED);
+            final long mobileTxPackets = u.getNetworkActivityPackets(
+                    BatteryStats.NETWORK_MOBILE_TX_DATA,
+                    BatteryStats.STATS_SINCE_CHARGED);
+            return (mobileRxPackets + mobileTxPackets) * powerPerPacketMah;
+        }
+    }
+
+    private void calculateRemaining(MobileRadioPowerCalculator.PowerAndDuration total,
+            BatteryStats batteryStats, long rawRealtimeUs) {
         long signalTimeMs = 0;
-        long noCoverageTimeMs = 0;
-        for (int i = 0; i < mPowerBins.length; i++) {
-            long strengthTimeMs = stats.getPhoneSignalStrengthTime(i, rawRealtimeUs, statsType)
-                    / 1000;
-            final double p = (strengthTimeMs * mPowerBins[i]) / (60 * 60 * 1000);
+        double powerMah = 0;
+        for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) {
+            long strengthTimeMs = batteryStats.getPhoneSignalStrengthTime(i, rawRealtimeUs,
+                    BatteryStats.STATS_SINCE_CHARGED) / 1000;
+            final double p = mIdlePowerEstimators[i].calculatePower(strengthTimeMs);
             if (DEBUG && p != 0) {
                 Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power="
                         + formatCharge(p));
             }
-            power += p;
+            powerMah += p;
             signalTimeMs += strengthTimeMs;
             if (i == 0) {
-                noCoverageTimeMs = strengthTimeMs;
+                total.noCoverageDurationMs = strengthTimeMs;
             }
         }
 
-        final long scanningTimeMs = stats.getPhoneSignalScanningTime(rawRealtimeUs, statsType)
-                / 1000;
-        final double p = (scanningTimeMs * mPowerScan) / (60 * 60 * 1000);
+        final long scanningTimeMs = batteryStats.getPhoneSignalScanningTime(rawRealtimeUs,
+                BatteryStats.STATS_SINCE_CHARGED) / 1000;
+        final double p = mScanPowerEstimator.calculatePower(scanningTimeMs);
         if (DEBUG && p != 0) {
             Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs + " power=" + formatCharge(p));
         }
-        power += p;
-        long radioActiveTimeMs = mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
-        long remainingActiveTimeMs = radioActiveTimeMs - mTotalAppMobileActiveMs;
+        powerMah += p;
+        long radioActiveTimeMs = batteryStats.getMobileRadioActiveTime(rawRealtimeUs,
+                BatteryStats.STATS_SINCE_CHARGED) / 1000;
+        long remainingActiveTimeMs = radioActiveTimeMs - total.totalAppDurationMs;
         if (remainingActiveTimeMs > 0) {
-            power += (mPowerRadioOn * remainingActiveTimeMs) / (1000 * 60 * 60);
+            powerMah += mActivePowerEstimator.calculatePower(remainingActiveTimeMs);
         }
-
-        if (power != 0) {
-            if (signalTimeMs != 0) {
-                app.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs;
-            }
-            app.mobileActive = remainingActiveTimeMs;
-            app.mobileActiveCount = stats.getMobileRadioActiveUnknownCount(statsType);
-            app.mobileRadioPowerMah = power;
-        }
+        total.durationMs = radioActiveTimeMs;
+        total.powerMah = powerMah;
+        total.signalDurationMs = signalTimeMs;
     }
 
-    @Override
-    public void reset() {
-        mTotalAppMobileActiveMs = 0;
-        mStats = null;
+    /**
+     * Return estimated power (in mAh) of sending or receiving a packet with the mobile radio.
+     */
+    private double getMobilePowerPerPacket(BatteryStats stats, long rawRealtimeUs, int statsType) {
+        final long radioDataUptimeMs =
+                stats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
+        final double mobilePower = mActivePowerEstimator.calculatePower(radioDataUptimeMs);
+
+        final long mobileRx = stats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
+                statsType);
+        final long mobileTx = stats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,
+                statsType);
+        final long mobilePackets = mobileRx + mobileTx;
+
+        return mobilePackets != 0 ? mobilePower / mobilePackets : 0;
     }
 }
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index 54f31f9..b01e4a8 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -42,26 +42,29 @@
     /**
      * Called when the device has started going to sleep.
      *
-     * @param why {@link #OFF_BECAUSE_OF_USER}, {@link #OFF_BECAUSE_OF_ADMIN},
-     * or {@link #OFF_BECAUSE_OF_TIMEOUT}.
+     * @param pmSleepReason One of PowerManager.GO_TO_SLEEP_REASON_*, detailing the specific reason
+     * we're going to sleep, such as GO_TO_SLEEP_REASON_POWER_BUTTON or GO_TO_SLEEP_REASON_TIMEOUT.
      */
-    void onStartedGoingToSleep(int reason);
+    void onStartedGoingToSleep(int pmSleepReason);
 
     /**
      * Called when the device has finished going to sleep.
      *
-     * @param why {@link #OFF_BECAUSE_OF_USER}, {@link #OFF_BECAUSE_OF_ADMIN},
-     *            or {@link #OFF_BECAUSE_OF_TIMEOUT}.
+     * @param pmSleepReason One of PowerManager.GO_TO_SLEEP_REASON_*, detailing the specific reason
+     * we're going to sleep, such as GO_TO_SLEEP_REASON_POWER_BUTTON or GO_TO_SLEEP_REASON_TIMEOUT.
      * @param cameraGestureTriggered whether the camera gesture was triggered between
      *                               {@link #onStartedGoingToSleep} and this method; if it's been
      *                               triggered, we shouldn't lock the device.
      */
-    void onFinishedGoingToSleep(int reason, boolean cameraGestureTriggered);
+    void onFinishedGoingToSleep(int pmSleepReason, boolean cameraGestureTriggered);
 
     /**
      * Called when the device has started waking up.
+
+     * @param pmWakeReason One of PowerManager.WAKE_REASON_*, detailing the reason we're waking up,
+     * such as WAKE_REASON_POWER_BUTTON or WAKE_REASON_GESTURE.
      */
-    void onStartedWakingUp();
+    void onStartedWakingUp(int pmWakeReason);
 
     /**
      * Called when the device has finished waking up.
diff --git a/core/java/com/android/internal/textservice/ISpellCheckerService.aidl b/core/java/com/android/internal/textservice/ISpellCheckerService.aidl
index 6a25964..295107b 100644
--- a/core/java/com/android/internal/textservice/ISpellCheckerService.aidl
+++ b/core/java/com/android/internal/textservice/ISpellCheckerService.aidl
@@ -39,9 +39,11 @@
      *                 {@link android.service.textservice.SpellCheckerService.Session#onGetSuggestionsMultiple(TextInfo[], int, boolean)} and
      *                 {@link android.service.textservice.SpellCheckerService.Session#onGetSuggestions(TextInfo, int)}
      * @param bundle bundle to be returned from {@link android.service.textservice.SpellCheckerService.Session#getBundle()}
+     * @param supportedAttributes supported attributes to be returned from {@link android.service.textservice.SpellCheckerService.Session#getSupportedAttributes()}
      * @param callback IPC channel to return the result to the caller in an asynchronous manner
      */
     void getISpellCheckerSession(
             String locale, ISpellCheckerSessionListener listener, in Bundle bundle,
+            int supportedAttributes,
             ISpellCheckerServiceCallback callback);
 }
diff --git a/core/java/com/android/internal/textservice/ITextServicesManager.aidl b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
index 8022949..dce67e7 100644
--- a/core/java/com/android/internal/textservice/ITextServicesManager.aidl
+++ b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
@@ -34,7 +34,7 @@
             boolean allowImplicitlySelectedSubtype);
     oneway void getSpellCheckerService(int userId, String sciId, in String locale,
             in ITextServicesSessionListener tsListener,
-            in ISpellCheckerSessionListener scListener, in Bundle bundle);
+            in ISpellCheckerSessionListener scListener, in Bundle bundle, int supportedAttributes);
     oneway void finishSpellCheckerService(int userId, in ISpellCheckerSessionListener listener);
     boolean isSpellCheckerEnabled(int userId);
     SpellCheckerInfo[] getEnabledSpellCheckers(int userId);
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 8dc56ed..94bd28a 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1023,7 +1023,9 @@
     audio_channel_mask_t nMask;
     jint jMask;
 
-    int gainIndex = nAudioPortConfig->gain.index;
+    int gainIndex = (nAudioPortConfig->config_mask & AUDIO_PORT_CONFIG_GAIN)
+            ? nAudioPortConfig->gain.index
+            : -1;
     if (gainIndex >= 0) {
         ALOGV("convertAudioPortConfigFromNative gain found with index %d mode %x",
               gainIndex, nAudioPortConfig->gain.mode);
@@ -1120,7 +1122,9 @@
             goto exit;
         }
     }
-    nMask = nAudioPortConfig->channel_mask;
+    nMask = (nAudioPortConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK)
+            ? nAudioPortConfig->channel_mask
+            : AUDIO_CONFIG_BASE_INITIALIZER.channel_mask;
     if (useInMask) {
         jMask = inChannelMaskFromNative(nMask);
         ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask);
@@ -1129,12 +1133,17 @@
         ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask);
     }
 
-    *jAudioPortConfig = env->NewObject(clazz, methodID,
-                                       jAudioPort,
-                                       nAudioPortConfig->sample_rate,
-                                       jMask,
-                                       audioFormatFromNative(nAudioPortConfig->format),
-                                       jAudioGainConfig);
+    *jAudioPortConfig =
+            env->NewObject(clazz, methodID, jAudioPort,
+                           (nAudioPortConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE)
+                                   ? nAudioPortConfig->sample_rate
+                                   : AUDIO_CONFIG_BASE_INITIALIZER.sample_rate,
+                           jMask,
+                           audioFormatFromNative(
+                                   (nAudioPortConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT)
+                                           ? nAudioPortConfig->format
+                                           : AUDIO_CONFIG_BASE_INITIALIZER.format),
+                           jAudioGainConfig);
     if (*jAudioPortConfig == NULL) {
         ALOGV("convertAudioPortConfigFromNative could not create new port config");
         jStatus = (jint)AUDIO_JAVA_ERROR;
@@ -1936,6 +1945,7 @@
     nAudioMix->mCbFlags = env->GetIntField(jAudioMix, gAudioMixFields.mCallbackFlags);
 
     jobject jFormat = env->GetObjectField(jAudioMix, gAudioMixFields.mFormat);
+    nAudioMix->mFormat = AUDIO_CONFIG_INITIALIZER;
     nAudioMix->mFormat.sample_rate = env->GetIntField(jFormat,
                                                      gAudioFormatFields.mSampleRate);
     nAudioMix->mFormat.channel_mask = outChannelMaskToNative(env->GetIntField(jFormat,
diff --git a/core/proto/android/server/biometrics.proto b/core/proto/android/server/biometrics.proto
index 14b5c52..900235e 100644
--- a/core/proto/android/server/biometrics.proto
+++ b/core/proto/android/server/biometrics.proto
@@ -120,8 +120,8 @@
 
     optional Modality modality = 2;
 
-    // State of the sensor's scheduler. True if currently handling an operation, false if idle.
-    optional bool is_busy = 3;
+    // State of the sensor's scheduler.
+    optional BiometricSchedulerProto scheduler = 3;
 
     // User states for this sensor.
     repeated UserStateProto user_states = 4;
@@ -136,4 +136,39 @@
 
     // Number of fingerprints enrolled
     optional int32 num_enrolled = 2;
+}
+
+// BiometricScheduler dump
+message BiometricSchedulerProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // Operation currently being handled by the BiometricScheduler
+    optional ClientMonitorEnum current_operation = 1;
+
+    // Total number of operations that have been handled, not including the current one if one
+    // exists. Kept in FIFO order (most recent at the end of the array)
+    optional int32 total_operations = 2;
+
+    // A list of recent past operations in the order which they were handled
+    repeated ClientMonitorEnum recent_operations = 3;
+}
+
+// BaseClientMonitor subtypes
+enum ClientMonitorEnum {
+    CM_NONE = 0;
+    CM_UPDATE_ACTIVE_USER = 1;
+    CM_ENROLL = 2;
+    CM_AUTHENTICATE = 3;
+    CM_REMOVE = 4;
+    CM_GET_AUTHENTICATOR_ID = 5;
+    CM_ENUMERATE = 6;
+    CM_INTERNAL_CLEANUP = 7;
+    CM_SET_FEATURE = 8;
+    CM_GET_FEATURE = 9;
+    CM_GENERATE_CHALLENGE = 10;
+    CM_REVOKE_CHALLENGE = 11;
+    CM_RESET_LOCKOUT = 12;
+    CM_DETECT_INTERACTION = 13;
+    CM_INVALIDATION_REQUESTER = 14;
+    CM_INVALIDATE = 15;
 }
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 76acc00..f543373 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -321,6 +321,9 @@
 
     <protected-broadcast android:name="android.net.nsd.STATE_CHANGED" />
 
+    <!-- For OMAPI -->
+    <protected-broadcast android:name="android.se.omapi.action.SECURE_ELEMENT_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" />
@@ -686,6 +689,10 @@
     <!-- Made protected in S (was added in R) -->
     <protected-broadcast android:name="com.android.internal.intent.action.BUGREPORT_REQUESTED" />
 
+    <!-- Added in S -->
+    <protected-broadcast android:name="android.app.action.MANAGED_PROFILE_CREATED" />
+    <protected-broadcast android:name="android.app.action.PROVISIONED_MANAGED_DEVICE" />
+
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->
@@ -3109,6 +3116,12 @@
     <permission android:name="android.permission.RECOVERY"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows an application to do certain operations needed for
+         resume on reboot feature.
+         @hide -->
+    <permission android:name="android.permission.BIND_RESUME_ON_REBOOT_SERVICE"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows an application to read system update info.
          @hide -->
     <permission android:name="android.permission.READ_SYSTEM_UPDATE_INFO"
@@ -4336,7 +4349,7 @@
          <p>Not for use by third-party applications.
     -->
     <permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"
-        android:protectionLevel="signature|recents" />
+        android:protectionLevel="signature|privileged|recents" />
     <uses-permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" />
 
     <!-- Allows an application to broadcast a notification that an application
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 54e9dfd..0c2cfbf 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -149,8 +149,7 @@
     <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Nur WLAN"</string>
     <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
     <skip />
-    <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) -->
-    <skip />
+    <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> SIM-übergreifende Anrufe"</string>
     <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nicht weitergeleitet"</string>
     <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g><xliff:g id="DIALING_NUMBER">{1}</xliff:g> nach <xliff:g id="TIME_DELAY">{2}</xliff:g> Sekunden."</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 7c7c178..52baecf 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -57,8 +57,7 @@
     <string name="imei" msgid="2157082351232630390">"IMEI"</string>
     <string name="meid" msgid="3291227361605924674">"MEID"</string>
     <string name="ClipMmi" msgid="4110549342447630629">"Incoming Caller ID"</string>
-    <!-- no translation found for ClirMmi (6752346475055446417) -->
-    <skip />
+    <string name="ClirMmi" msgid="6752346475055446417">"Hide outgoing caller ID"</string>
     <string name="ColpMmi" msgid="4736462893284419302">"Connected Line ID"</string>
     <string name="ColrMmi" msgid="5889782479745764278">"Connected Line ID Restriction"</string>
     <string name="CfMmi" msgid="8390012691099787178">"Call forwarding"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 0cbbb01..c99fc18 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -57,8 +57,7 @@
     <string name="imei" msgid="2157082351232630390">"IMEI"</string>
     <string name="meid" msgid="3291227361605924674">"MEID"</string>
     <string name="ClipMmi" msgid="4110549342447630629">"Incoming Caller ID"</string>
-    <!-- no translation found for ClirMmi (6752346475055446417) -->
-    <skip />
+    <string name="ClirMmi" msgid="6752346475055446417">"Hide outgoing caller ID"</string>
     <string name="ColpMmi" msgid="4736462893284419302">"Connected Line ID"</string>
     <string name="ColrMmi" msgid="5889782479745764278">"Connected Line ID Restriction"</string>
     <string name="CfMmi" msgid="8390012691099787178">"Call forwarding"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index e923d65..d12f810 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -57,8 +57,7 @@
     <string name="imei" msgid="2157082351232630390">"IMEI"</string>
     <string name="meid" msgid="3291227361605924674">"MEID"</string>
     <string name="ClipMmi" msgid="4110549342447630629">"Incoming Caller ID"</string>
-    <!-- no translation found for ClirMmi (6752346475055446417) -->
-    <skip />
+    <string name="ClirMmi" msgid="6752346475055446417">"Hide outgoing caller ID"</string>
     <string name="ColpMmi" msgid="4736462893284419302">"Connected Line ID"</string>
     <string name="ColrMmi" msgid="5889782479745764278">"Connected Line ID Restriction"</string>
     <string name="CfMmi" msgid="8390012691099787178">"Call forwarding"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 6e17242..f781191 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -57,8 +57,7 @@
     <string name="imei" msgid="2157082351232630390">"IMEI"</string>
     <string name="meid" msgid="3291227361605924674">"MEID"</string>
     <string name="ClipMmi" msgid="4110549342447630629">"Incoming Caller ID"</string>
-    <!-- no translation found for ClirMmi (6752346475055446417) -->
-    <skip />
+    <string name="ClirMmi" msgid="6752346475055446417">"Hide outgoing caller ID"</string>
     <string name="ColpMmi" msgid="4736462893284419302">"Connected Line ID"</string>
     <string name="ColrMmi" msgid="5889782479745764278">"Connected Line ID Restriction"</string>
     <string name="CfMmi" msgid="8390012691099787178">"Call forwarding"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 759fe01..4a87b1e 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -57,8 +57,7 @@
     <string name="imei" msgid="2157082351232630390">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‎‏‎‎‎‎‎‎‎‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎IMEI‎‏‎‎‏‎"</string>
     <string name="meid" msgid="3291227361605924674">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‏‎‏‏‎‎‏‏‎‎‏‎‎‏‏‏‎‏‏‎‎‏‎‎‎‎‎‎‏‏‏‎‎‏‎‎‎‏‎‎‏‏‎‏‏‏‎‏‎‎‎‎‏‎‎MEID‎‏‎‎‏‎"</string>
     <string name="ClipMmi" msgid="4110549342447630629">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎‏‎‏‏‎‏‏‎‎‏‎‎‏‎‏‏‎‎‎‏‎‎‏‏‏‎‎‏‏‏‏‎‎‏‎‎‏‎‎‏‎‏‎Incoming Caller ID‎‏‎‎‏‎"</string>
-    <!-- no translation found for ClirMmi (6752346475055446417) -->
-    <skip />
+    <string name="ClirMmi" msgid="6752346475055446417">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‏‎‏‎‎‏‎‎‏‏‏‎‏‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‎‎‏‎‎‎‏‎Hide Outgoing Caller ID‎‏‎‎‏‎"</string>
     <string name="ColpMmi" msgid="4736462893284419302">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‏‏‏‎‏‏‎‏‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‎‎‎‎‎‏‎‎‏‎‏‏‎‎‏‏‎‏‏‏‎‎‏‏‎‎Connected Line ID‎‏‎‎‏‎"</string>
     <string name="ColrMmi" msgid="5889782479745764278">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‏‏‏‎‎‏‎‏‏‎‏‎‏‏‏‏‏‎‏‎‏‏‎‏‏‏‏‎‏‏‎‎‏‎‎‎‎‏‏‎‎‏‎‏‏‏‎‏‏‎‏‏‎‎Connected Line ID Restriction‎‏‎‎‏‎"</string>
     <string name="CfMmi" msgid="8390012691099787178">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‏‏‏‎‏‎‏‎‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‎‎‏‎‎‎‎‎‎‏‎‏‏‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎Call forwarding‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 64bc3d1..bf4c0f6 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -57,8 +57,7 @@
     <string name="imei" msgid="2157082351232630390">"IMEI"</string>
     <string name="meid" msgid="3291227361605924674">"MEID"</string>
     <string name="ClipMmi" msgid="4110549342447630629">"Identificador de llamadas entrantes"</string>
-    <!-- no translation found for ClirMmi (6752346475055446417) -->
-    <skip />
+    <string name="ClirMmi" msgid="6752346475055446417">"Ocultar identificador de llamadas salientes"</string>
     <string name="ColpMmi" msgid="4736462893284419302">"ID de línea conectada"</string>
     <string name="ColrMmi" msgid="5889782479745764278">"Restricción de ID de línea conectada"</string>
     <string name="CfMmi" msgid="8390012691099787178">"Desvío de llamadas"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index a591d31..fb7511a 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -149,8 +149,7 @@
     <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"ફક્ત વાઇ-ફાઇ"</string>
     <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
     <skip />
-    <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) -->
-    <skip />
+    <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> ક્રૉસ સિમ કૉલિંગ"</string>
     <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ફોરવર્ડ કર્યો નથી"</string>
     <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="TIME_DELAY">{2}</xliff:g> સેકન્ડ પછી <xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 2bdabc8..8cff250 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -151,8 +151,7 @@
     <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"‏Wi-Fi בלבד"</string>
     <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
     <skip />
-    <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) -->
-    <skip />
+    <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"‏העברת שיחות בין כרטיסי SIM של <xliff:g id="SPN">%s</xliff:g>"</string>
     <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ללא העברה"</string>
     <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> כעבור <xliff:g id="TIME_DELAY">{2}</xliff:g> שניות"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 9fc9f36..b158306 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -149,8 +149,7 @@
     <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"ವೈ-ಫೈ ಮಾತ್ರ"</string>
     <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
     <skip />
-    <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) -->
-    <skip />
+    <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> ಕ್ರಾಸ್-ಸಿಮ್ ಕರೆ ಮಾಡುವಿಕೆ"</string>
     <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ಫಾರ್ವರ್ಡ್ ಮಾಡಲಾಗಿಲ್ಲ"</string>
     <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> ಸೆಕೆಂಡುಗಳ ನಂತರ <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index dc5175b..37b2c11 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -149,8 +149,7 @@
     <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"केवळ वाय-फाय"</string>
     <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
     <skip />
-    <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) -->
-    <skip />
+    <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> क्रॉस सिम कॉलिंग"</string>
     <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: अग्रेषित केला नाही"</string>
     <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> सेकंदांनंतर <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index a14ea62..686bbe6 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -149,8 +149,7 @@
     <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi sahaja"</string>
     <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
     <skip />
-    <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) -->
-    <skip />
+    <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Panggilan Silang Sim"</string>
     <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Tidak dimajukan"</string>
     <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> selepas <xliff:g id="TIME_DELAY">{2}</xliff:g> saat"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 4ada7a2..3b69609 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -149,7 +149,7 @@
     <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Alleen wifi"</string>
     <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
     <skip />
-    <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Cross-sim-bellen van <xliff:g id="SPN">%s</xliff:g>"</string>
+    <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Bellen met meerdere simkaarten van <xliff:g id="SPN">%s</xliff:g>"</string>
     <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: niet doorgeschakeld"</string>
     <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> na <xliff:g id="TIME_DELAY">{2}</xliff:g> seconden"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 54e064d..4b55e2d 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -149,8 +149,7 @@
     <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"କେବଳ ୱାଇ-ଫାଇ"</string>
     <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
     <skip />
-    <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) -->
-    <skip />
+    <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> କ୍ରସ୍ SIM କଲିଂ"</string>
     <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ଫରୱାର୍ଡ କରାଯାଇନାହିଁ"</string>
     <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> ସେକେଣ୍ଡ ପରେ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 804b77d..8fc507f 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -149,8 +149,7 @@
     <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"ਸਿਰਫ਼ ਵਾਈ-ਫਾਈ"</string>
     <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
     <skip />
-    <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) -->
-    <skip />
+    <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> ਕ੍ਰਾਸ-ਸਿਮ ਕਾਲਿੰਗ"</string>
     <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ਅੱਗੇ ਨਹੀਂ ਭੇਜਿਆ ਗਿਆ"</string>
     <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> ਸਕਿੰਟਾਂ ਬਾਅਦ"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 9837b88..bdcf7af 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -57,8 +57,7 @@
     <string name="imei" msgid="2157082351232630390">"IMEI"</string>
     <string name="meid" msgid="3291227361605924674">"MEID"</string>
     <string name="ClipMmi" msgid="4110549342447630629">"Identificador de chamadas recebidas"</string>
-    <!-- no translation found for ClirMmi (6752346475055446417) -->
-    <skip />
+    <string name="ClirMmi" msgid="6752346475055446417">"Ocultar identificador de chamadas realizadas"</string>
     <string name="ColpMmi" msgid="4736462893284419302">"ID de linha conectada"</string>
     <string name="ColrMmi" msgid="5889782479745764278">"Restrição de ID de linha conectada"</string>
     <string name="CfMmi" msgid="8390012691099787178">"Encaminhamento de chamada"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index ef7ee98..e065e55 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -57,8 +57,7 @@
     <string name="imei" msgid="2157082351232630390">"IMEI"</string>
     <string name="meid" msgid="3291227361605924674">"MEID"</string>
     <string name="ClipMmi" msgid="4110549342447630629">"ID do Autor da Chamada"</string>
-    <!-- no translation found for ClirMmi (6752346475055446417) -->
-    <skip />
+    <string name="ClirMmi" msgid="6752346475055446417">"Ocultar identificação do autor da chamada efetuada"</string>
     <string name="ColpMmi" msgid="4736462893284419302">"ID de linha ligada"</string>
     <string name="ColrMmi" msgid="5889782479745764278">"Restrição de ID de linha ligada"</string>
     <string name="CfMmi" msgid="8390012691099787178">"Encaminhamento de chamadas"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 9837b88..bdcf7af 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -57,8 +57,7 @@
     <string name="imei" msgid="2157082351232630390">"IMEI"</string>
     <string name="meid" msgid="3291227361605924674">"MEID"</string>
     <string name="ClipMmi" msgid="4110549342447630629">"Identificador de chamadas recebidas"</string>
-    <!-- no translation found for ClirMmi (6752346475055446417) -->
-    <skip />
+    <string name="ClirMmi" msgid="6752346475055446417">"Ocultar identificador de chamadas realizadas"</string>
     <string name="ColpMmi" msgid="4736462893284419302">"ID de linha conectada"</string>
     <string name="ColrMmi" msgid="5889782479745764278">"Restrição de ID de linha conectada"</string>
     <string name="CfMmi" msgid="8390012691099787178">"Encaminhamento de chamada"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index cfa9bf1..a56a5f8 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -57,8 +57,7 @@
     <string name="imei" msgid="2157082351232630390">"IMEI"</string>
     <string name="meid" msgid="3291227361605924674">"MEID"</string>
     <string name="ClipMmi" msgid="4110549342447630629">"පැමිණෙන අමතන්නාගේ ID"</string>
-    <!-- no translation found for ClirMmi (6752346475055446417) -->
-    <skip />
+    <string name="ClirMmi" msgid="6752346475055446417">"යන ඇමතුම්කරු ID සඟවන්න"</string>
     <string name="ColpMmi" msgid="4736462893284419302">"සම්බන්ධ කළ Line ID"</string>
     <string name="ColrMmi" msgid="5889782479745764278">"සම්බන්ධ කළ Line ID සීමා කිරීම්"</string>
     <string name="CfMmi" msgid="8390012691099787178">"ඇමතුම ඉදිරියට යැවීම"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 5ee9031..8feea9c 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -149,8 +149,7 @@
     <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Vetëm Wi-Fi"</string>
     <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
     <skip />
-    <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) -->
-    <skip />
+    <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Telefonatat e kryqëzuara të kartës SIM nga <xliff:g id="SPN">%s</xliff:g>"</string>
     <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nuk u transferua"</string>
     <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> pas <xliff:g id="TIME_DELAY">{2}</xliff:g> sekondash"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 2687aa1..29d90bd 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -236,9 +236,9 @@
     <string name="shutdown_confirm" product="default" msgid="136816458966692315">"మీ ఫోన్ షట్‌డౌన్ చేయబడుతుంది."</string>
     <string name="shutdown_confirm_question" msgid="796151167261608447">"మీరు షట్ డౌన్ చేయాలనుకుంటున్నారా?"</string>
     <string name="reboot_safemode_title" msgid="5853949122655346734">"సురక్షిత మోడ్‌కు రీబూట్ చేయండి"</string>
-    <string name="reboot_safemode_confirm" msgid="1658357874737219624">"మీరు సురక్షిత మోడ్‌లోకి రీబూట్ చేయాలనుకుంటున్నారా? దీని వలన మీరు ఇన్‌స్టాల్ చేసిన అన్ని మూడవ పక్షం అనువర్తనాలు నిలిపివేయబడతాయి. ఇవి మీరు మళ్లీ రీబూట్ చేసినప్పుడు పునరుద్ధరించబడతాయి."</string>
+    <string name="reboot_safemode_confirm" msgid="1658357874737219624">"మీరు సురక్షిత మోడ్‌లోకి రీబూట్ చేయాలనుకుంటున్నారా? దీని వలన మీరు ఇన్‌స్టాల్ చేసిన అన్ని మూడవ పక్షం యాప్‌లు నిలిపివేయబడతాయి. ఇవి మీరు మళ్లీ రీబూట్ చేసినప్పుడు పునరుద్ధరించబడతాయి."</string>
     <string name="recent_tasks_title" msgid="8183172372995396653">"ఇటీవలివి"</string>
-    <string name="no_recent_tasks" msgid="9063946524312275906">"ఇటీవలి అనువర్తనాలు ఏవీ లేవు."</string>
+    <string name="no_recent_tasks" msgid="9063946524312275906">"ఇటీవలి యాప్‌లు ఏవీ లేవు."</string>
     <string name="global_actions" product="tablet" msgid="4412132498517933867">"టాబ్లెట్ ఎంపికలు"</string>
     <string name="global_actions" product="tv" msgid="3871763739487450369">"Android TV ఎంపికలు"</string>
     <string name="global_actions" product="default" msgid="6410072189971495460">"ఫోన్ ఎంపికలు"</string>
@@ -344,13 +344,13 @@
     <string name="permlab_expandStatusBar" msgid="1184232794782141698">"స్థితి పట్టీని విస్తరింపజేయడం/కుదించడం"</string>
     <string name="permdesc_expandStatusBar" msgid="7180756900448498536">"స్థితి బార్‌ను విస్తరింపజేయడానికి లేదా కుదించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_install_shortcut" msgid="7451554307502256221">"షార్ట్‌కట్‌లను ఇన్‌స్టాల్ చేయడం"</string>
-    <string name="permdesc_install_shortcut" msgid="4476328467240212503">"వినియోగదారు ప్రమేయం లేకుండానే హోమ్‌స్క్రీన్ సత్వరమార్గాలను జోడించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_install_shortcut" msgid="4476328467240212503">"వినియోగదారు ప్రమేయం లేకుండానే హోమ్‌స్క్రీన్ సత్వరమార్గాలను జోడించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_uninstall_shortcut" msgid="295263654781900390">"సత్వరమార్గాలను అన్ఇన్‌స్టాల్ చేయడం"</string>
-    <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"వినియోగదారు ప్రమేయం లేకుండానే హోమ్‌స్క్రీన్ సత్వరమార్గాలను తీసివేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"వినియోగదారు ప్రమేయం లేకుండానే హోమ్‌స్క్రీన్ సత్వరమార్గాలను తీసివేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"అవుట్‌గోయింగ్ కాల్‌లను దారి మళ్లించడం"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"కాల్‌ను వేరే నంబర్‌కు దారి మళ్లించే లేదా మొత్తంగా కాల్‌ను ఆపివేసే ఎంపిక సహాయంతో అవుట్‌గోయింగ్ కాల్ సమయంలో డయల్ చేయబడుతున్న నంబర్‌ను చూడటానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"కాల్‌ను వేరే నంబర్‌కు దారి మళ్లించే లేదా మొత్తంగా కాల్‌ను ఆపివేసే ఎంపిక సహాయంతో అవుట్‌గోయింగ్ కాల్ సమయంలో డయల్ చేయబడుతున్న నంబర్‌ను చూడటానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"ఫోన్ కాల్‌లకు సమాధానమివ్వు"</string>
-    <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"ఇన్‌కమింగ్ ఫోన్ కాల్‌లకు సమాధానమివ్వడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"ఇన్‌కమింగ్ ఫోన్ కాల్‌లకు సమాధానమివ్వడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_receiveSms" msgid="505961632050451881">"వచన సందేశాలను (SMS) స్వీకరించడం"</string>
     <string name="permdesc_receiveSms" msgid="1797345626687832285">"SMS సందేశాలను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. యాప్ మీ డివైజ్‌కు పంపబడిన సందేశాలను మీకు చూపకుండానే పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string>
     <string name="permlab_receiveMms" msgid="4000650116674380275">"వచన సందేశాలను (MMS) స్వీకరించడం"</string>
@@ -396,7 +396,7 @@
     <string name="permlab_getPackageSize" msgid="375391550792886641">"యాప్ నిల్వ స్థలాన్ని అంచనా వేయడం"</string>
     <string name="permdesc_getPackageSize" msgid="742743530909966782">"యాప్‌ కోడ్, డేటా మరియు కాష్ పరిమాణాలను తిరిగి పొందడానికి దాన్ని అనుమతిస్తుంది"</string>
     <string name="permlab_writeSettings" msgid="8057285063719277394">"సిస్టమ్ సెట్టింగ్‌లను మార్చడం"</string>
-    <string name="permdesc_writeSettings" msgid="8293047411196067188">"సిస్టమ్ యొక్క సెట్టింగ్‌ల డేటాను సవరించడానికి అనువర్తనాన్ని అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ సిస్టమ్ యొక్క కాన్ఫిగరేషన్‌ను నాశనం చేయవచ్చు."</string>
+    <string name="permdesc_writeSettings" msgid="8293047411196067188">"సిస్టమ్ యొక్క సెట్టింగ్‌ల డేటాను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ సిస్టమ్ యొక్క కాన్ఫిగరేషన్‌ను నాశనం చేయవచ్చు."</string>
     <string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"ప్రారంభంలో అమలు చేయడం"</string>
     <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"సిస్టమ్ బూటింగ్‌ను పూర్తి చేసిన వెంటనే దానికదే ప్రారంభించబడటానికి యాప్‌ను అనుమతిస్తుంది. ఇది టాబ్లెట్‌ను ప్రారంభించడానికి ఎక్కువ సమయం పట్టేలా చేయవచ్చు మరియు ఎల్లప్పుడూ అమలు చేయడం ద్వారా మొత్తం టాబ్లెట్‌ను నెమ్మదిగా పని చేయడానికి యాప్‌ను అనుమతించేలా చేయవచ్చు."</string>
     <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"సిస్టమ్ బూటింగ్‌ను పూర్తి చేసిన వెంటనే యాప్ దానికదే ప్రారంభం కావడానికి అనుమతిస్తుంది. ఇది మీ Android TV పరికరం ప్రారంభం కావడానికి ఎక్కువ సమయం పట్టేలా చేయవచ్చు మరియు ఎల్లప్పుడూ అమలు కావడం ద్వారా మొత్తం పరికరం పనితీరును నెమ్మది చేయడానికి యాప్‌ను అనుమతించవచ్చు."</string>
@@ -444,7 +444,7 @@
     <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"బ్యాక్‌గ్రౌండ్‌లో ఆడియోను రికార్డ్ చేయగలదు"</string>
     <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"మైక్రోఫోన్‌ను ఉపయోగించి ఈ యాప్ ఎప్పుడైనా ఆడియోను రికార్డ్ చేయగలదు."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"SIMకి ఆదేశాలను పంపడం"</string>
-    <string name="permdesc_sim_communication" msgid="4179799296415957960">"సిమ్‌కు ఆదేశాలను పంపడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఇది చాలా ప్రమాదకరం."</string>
+    <string name="permdesc_sim_communication" msgid="4179799296415957960">"సిమ్‌కు ఆదేశాలను పంపడానికి యాప్‌ను అనుమతిస్తుంది. ఇది చాలా ప్రమాదకరం."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"భౌతిక కార్యాకలాపాన్ని గుర్తించండి"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"ఈ యాప్ మీ భౌతిక కార్యాకలాపాన్ని గుర్తించగలదు."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"చిత్రాలు మరియు వీడియోలు తీయడం"</string>
@@ -461,11 +461,11 @@
     <string name="permlab_callPhone" msgid="1798582257194643320">"నేరుగా కాల్ చేసే ఫోన్ నంబర్‌లు"</string>
     <string name="permdesc_callPhone" msgid="5439809516131609109">"మీ ప్రమేయం లేకుండా ఫోన్ నంబర్‌లకు కాల్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. దీని వలన అనుకోని ఛార్జీలు విధించబడవచ్చు లేదా కాల్‌లు రావచ్చు. ఇది అత్యవసర నంబర్‌లకు కాల్ చేయడానికి యాప్‌ను అనుమతించదని గుర్తుంచుకోండి. హానికరమైన యాప్‌లు మీ నిర్ధారణ లేకుండానే కాల్‌లు చేయడం ద్వారా మీకు డబ్బు ఖర్చయ్యేలా చేయవచ్చు."</string>
     <string name="permlab_accessImsCallService" msgid="442192920714863782">"IMS కాల్ సేవ యాక్సెస్ అనుమతి"</string>
-    <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"మీ ప్రమేయం లేకుండా కాల్‌లు చేయడం కోసం IMS సేవను ఉపయోగించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"మీ ప్రమేయం లేకుండా కాల్‌లు చేయడం కోసం IMS సేవను ఉపయోగించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_readPhoneState" msgid="8138526903259297969">"ఫోన్ స్థితి మరియు గుర్తింపుని చదవడం"</string>
     <string name="permdesc_readPhoneState" msgid="7229063553502788058">"పరికరం యొక్క ఫోన్ ఫీచర్‌లను యాక్సెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి ఫోన్ నంబర్ మరియు పరికరం IDలను, కాల్ సక్రియంగా ఉందా లేదా అనే విషయాన్ని మరియు కాల్ ద్వారా కనెక్ట్ చేయబడిన రిమోట్ నంబర్‌ను కనుగొనడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_manageOwnCalls" msgid="9033349060307561370">"కాల్‌లను సిస్టమ్ ద్వారా వెళ్లేలా చేయి"</string>
-    <string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"కాలింగ్ అనుభవాన్ని మెరుగుపరచడం కోసం తన కాల్‌లను సిస్టమ్ ద్వారా వెళ్లేలా చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"కాలింగ్ అనుభవాన్ని మెరుగుపరచడం కోసం తన కాల్‌లను సిస్టమ్ ద్వారా వెళ్లేలా చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_callCompanionApp" msgid="3654373653014126884">"సిస్టమ్ ద్వారా కాల్‌లను చూసి, నియంత్రించండి."</string>
     <string name="permdesc_callCompanionApp" msgid="8474168926184156261">"పరికరంలో కొనసాగుతున్న కాల్‌లను చూడడానికి మరియు నియంత్రించడానికి యాప్‌ను అనుమతిస్తుంది. ఇందులో కాల్ కోసం కాల్‌ల నంబర్‌లు మరియు రాష్ట్ర కాల్ వంటి సమాచారం ఉంటుంది."</string>
     <string name="permlab_exemptFromAudioRecordRestrictions" msgid="1164725468350759486">"ఆడియో రికార్డ్ పరిమితుల నుండి మినహాయింపు"</string>
@@ -483,9 +483,9 @@
     <string name="permdesc_wakeLock" product="tv" msgid="2329298966735118796">"మీ Android TV పరికరం స్లీప్ మోడ్‌లోకి వెళ్లకుండా నివారించడానికి యాప్‌ని అనుమతిస్తుంది."</string>
     <string name="permdesc_wakeLock" product="default" msgid="3689523792074007163">"నిద్రావస్థకి వెళ్లకుండా ఫోన్‌ను నిరోధించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_transmitIr" msgid="8077196086358004010">"ఇన్‌ఫ్రారెడ్ ప్రసరణ"</string>
-    <string name="permdesc_transmitIr" product="tablet" msgid="5884738958581810253">"టాబ్లెట్ యొక్క ఇన్‌ఫ్రారెడ్ ట్రాన్స్‌మిటర్‌ను ఉపయోగించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_transmitIr" product="tablet" msgid="5884738958581810253">"టాబ్లెట్ యొక్క ఇన్‌ఫ్రారెడ్ ట్రాన్స్‌మిటర్‌ను ఉపయోగించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permdesc_transmitIr" product="tv" msgid="3278506969529173281">"మీ Android TV పరికరం యొక్క ఇన్‌ఫ్రారెడ్ ట్రాన్స్‌మిటర్‌ని ఉపయోగించడానికి యాప్‌ని అనుమతిస్తుంది."</string>
-    <string name="permdesc_transmitIr" product="default" msgid="8484193849295581808">"ఫోన్ యొక్క ఇన్‌ఫ్రారెడ్ ట్రాన్స్‌మిటర్‌ను ఉపయోగించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_transmitIr" product="default" msgid="8484193849295581808">"ఫోన్ యొక్క ఇన్‌ఫ్రారెడ్ ట్రాన్స్‌మిటర్‌ను ఉపయోగించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_setWallpaper" msgid="6959514622698794511">"వాల్‌పేపర్‌ను సెట్ చేయడం"</string>
     <string name="permdesc_setWallpaper" msgid="2973996714129021397">"సిస్టమ్ వాల్‌పేపర్‌ను సెట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_setWallpaperHints" msgid="1153485176642032714">"మీ వాల్‌పేపర్ పరిమాణాన్ని సర్దుబాటు చేయడం"</string>
@@ -507,13 +507,13 @@
     <string name="permlab_changeTetherState" msgid="9079611809931863861">"టీథర్ చేయబడిన కనెక్టివిటీని మార్చడం"</string>
     <string name="permdesc_changeTetherState" msgid="3025129606422533085">"టీథర్ చేసిన నెట్‌వర్క్ కనెక్టివిటీ యొక్క స్థితిని మార్చడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_accessWifiState" msgid="5552488500317911052">"Wi-Fi కనెక్షన్‌లను వీక్షించడం"</string>
-    <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Wi-Fi ప్రారంభించబడిందా, లేదా మరియు కనెక్ట్ చేయబడిన Wi-Fi పరికరాల పేరు వంటి Wi-Fi నెట్‌వర్కింగ్ గురించి సమాచారాన్ని వీక్షించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Wi-Fi ప్రారంభించబడిందా, లేదా మరియు కనెక్ట్ చేయబడిన Wi-Fi పరికరాల పేరు వంటి Wi-Fi నెట్‌వర్కింగ్ గురించి సమాచారాన్ని వీక్షించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_changeWifiState" msgid="7947824109713181554">"Wi-Fiకి కనెక్ట్ చేయడం మరియు దాని నుండి డిస్‌కనెక్ట్ చేయడం"</string>
     <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Wi-Fi యాక్సెస్ స్థానాలకు కనెక్ట్ చేయడానికి మరియు వాటి నుండి డిస్‌కనెక్ట్ చేయడానికి మరియు Wi-Fi నెట్‌వర్క్‌ల కోసం పరికర కాన్ఫిగరేషన్‌కు మార్పులు చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"Wi-Fi Multicast స్వీకరణను అనుమతించడం"</string>
     <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"మల్టీక్యాస్ట్ చిరునామాలను ఉపయోగించి మీ టాబ్లెట్‌కు మాత్రమే కాకుండా Wi-Fi నెట్‌వర్క్‌లోని అన్ని పరికరాలకు పంపబడిన ప్యాకెట్‌లను స్వీకరించడానికి యాప్‌ను అనుమతిస్తుంది. మల్టీక్యాస్ట్ యేతర మోడ్ కంటే ఇది ఎక్కువ పవర్ ఉపయోగిస్తుంది."</string>
     <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"మల్టీక్యాస్ట్ చిరునామాలను ఉపయోగించి మీ Android TV పరికరానికి మాత్రమే కాకుండా Wi-Fi నెట్‌వర్క్‌లోని అన్ని పరికరాలకు పంపిన ప్యాకెట్‌లను స్వీకరించడానికి యాప్‌ని అనుమతిస్తుంది. ఇది మల్టీక్యాస్ట్ యేతర మోడ్ కంటే ఎక్కువ పవర్‌ను ఉపయోగిస్తుంది."</string>
-    <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"మల్టీక్యాస్ట్ చిరునామాలను ఉపయోగించి మీ ఫోన్‌కు మాత్రమే కాకుండా Wi-Fi నెట్‌వర్క్‌లోని అన్ని పరికరాలకు పంపబడిన ప్యాకెట్‌లను స్వీకరించడానికి అనువర్తనాన్ని అనుమతిస్తుంది. మల్టీక్యాస్ట్ యేతర మోడ్ కంటే ఇది ఎక్కువ పవర్ ఉపయోగిస్తుంది."</string>
+    <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"మల్టీక్యాస్ట్ చిరునామాలను ఉపయోగించి మీ ఫోన్‌కు మాత్రమే కాకుండా Wi-Fi నెట్‌వర్క్‌లోని అన్ని పరికరాలకు పంపబడిన ప్యాకెట్‌లను స్వీకరించడానికి యాప్‌ను అనుమతిస్తుంది. మల్టీక్యాస్ట్ యేతర మోడ్ కంటే ఇది ఎక్కువ పవర్ ఉపయోగిస్తుంది."</string>
     <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"బ్లూటూత్ సెట్టింగ్‌లను యాక్సెస్ చేయడం"</string>
     <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"స్థానిక బ్లూటూత్ టాబ్లెట్‌ను కాన్ఫిగర్ చేయడానికి మరియు రిమోట్ పరికరాలతో దాన్ని కనుగొనడానికి మరియు జత చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"మీ Android TV పరికరంలో బ్లూటూత్‌ను కాన్ఫిగర్ చేయడానికి మరియు రిమోట్ పరికరాలతో దాన్ని కనుగొని, జత చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
@@ -521,7 +521,7 @@
     <string name="permlab_accessWimaxState" msgid="7029563339012437434">"WiMAXకు కనెక్ట్ చేయడం మరియు దాని నుండి డిస్‌కనెక్ట్ చేయడం"</string>
     <string name="permdesc_accessWimaxState" msgid="5372734776802067708">"Wi-Fi ప్రారంభించబడిందా, లేదా మరియు కనెక్ట్ చేయబడిన WiMAX నెట్‌వర్క్‌ల గురించి సమాచారాన్ని కనుగొనడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_changeWimaxState" msgid="6223305780806267462">"WiMAX స్థితిని మార్చడం"</string>
-    <string name="permdesc_changeWimaxState" product="tablet" msgid="4011097664859480108">"WiMAX నెట్‌వర్క్‌లకు టాబ్లెట్‌ను కనెక్ట్ చేయడానికి మరియు వాటి నుండి టాబ్లెట్‌ను డిస్‌కనెక్ట్ చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_changeWimaxState" product="tablet" msgid="4011097664859480108">"WiMAX నెట్‌వర్క్‌లకు టాబ్లెట్‌ను కనెక్ట్ చేయడానికి మరియు వాటి నుండి టాబ్లెట్‌ను డిస్‌కనెక్ట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"మీ Android TV పరికరాన్ని WiMAX నెట్‌వర్క్‌లకు కనెక్ట్ చేయడానికి లేదా డిస్‌కనెక్ట్ చేయడానికి యాప్‌ని అనుమతిస్తుంది."</string>
     <string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"WiMAX నెట్‌వర్క్‌లకు ఫోన్‌ను కనెక్ట్ చేయడానికి మరియు వాటి నుండి ఫోన్‌ను డిస్‌కనెక్ట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_bluetooth" msgid="586333280736937209">"బ్లూటూత్ పరికరాలతో జత చేయడం"</string>
@@ -637,19 +637,19 @@
     <string name="permlab_sdcardWrite" msgid="4863021819671416668">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్‌లను సవరించండి లేదా తొలగించండి"</string>
     <string name="permdesc_sdcardWrite" msgid="8376047679331387102">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్‌లను రాయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_use_sip" msgid="8250774565189337477">"SIP కాల్‌లను చేయడానికి/స్వీకరించడానికి"</string>
-    <string name="permdesc_use_sip" msgid="3590270893253204451">"SIP కాల్‌లను చేయడానికి మరియు స్వీకరించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_use_sip" msgid="3590270893253204451">"SIP కాల్‌లను చేయడానికి మరియు స్వీకరించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_register_sim_subscription" msgid="1653054249287576161">"కొత్త టెలికామ్ SIM కనెక్షన్‌లను నమోదు చేయడం"</string>
-    <string name="permdesc_register_sim_subscription" msgid="4183858662792232464">"కొత్త టెలికామ్ SIM కనెక్షన్‌లను నమోదు చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_register_sim_subscription" msgid="4183858662792232464">"కొత్త టెలికామ్ SIM కనెక్షన్‌లను నమోదు చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_register_call_provider" msgid="6135073566140050702">"కొత్త టెలికామ్ కనెక్షన్‌లను నమోదు చేయడం"</string>
-    <string name="permdesc_register_call_provider" msgid="4201429251459068613">"కొత్త టెలికామ్ కనెక్షన్‌లను నమోదు చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_register_call_provider" msgid="4201429251459068613">"కొత్త టెలికామ్ కనెక్షన్‌లను నమోదు చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_connection_manager" msgid="3179365584691166915">"టెలికామ్ కనెక్షన్‌లను నిర్వహించడం"</string>
-    <string name="permdesc_connection_manager" msgid="1426093604238937733">"టెలికామ్ కనెక్షన్‌లను నిర్వహించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_connection_manager" msgid="1426093604238937733">"టెలికామ్ కనెక్షన్‌లను నిర్వహించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_bind_incall_service" msgid="5990625112603493016">"ఇన్-కాల్ స్క్రీన్‌తో పరస్పర చర్య చేయడం"</string>
-    <string name="permdesc_bind_incall_service" msgid="4124917526967765162">"వినియోగదారునికి ఇన్-కాల్ స్క్రీన్ ఎప్పుడు, ఎలా కనిపించాలనే దాన్ని నియంత్రించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_bind_incall_service" msgid="4124917526967765162">"వినియోగదారునికి ఇన్-కాల్ స్క్రీన్ ఎప్పుడు, ఎలా కనిపించాలనే దాన్ని నియంత్రించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_bind_connection_service" msgid="5409268245525024736">"టెలిఫోన్ సేవలతో పరస్పర చర్య చేయడం"</string>
-    <string name="permdesc_bind_connection_service" msgid="6261796725253264518">"కాల్‌లు చేయడం/స్వీకరించడం కోసం టెలిఫోన్ సేవలతో పరస్పర చర్య చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_bind_connection_service" msgid="6261796725253264518">"కాల్‌లు చేయడం/స్వీకరించడం కోసం టెలిఫోన్ సేవలతో పరస్పర చర్య చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_control_incall_experience" msgid="6436863486094352987">"ఇన్-కాల్ వినియోగదారు అనుభవాన్ని అందించడం"</string>
-    <string name="permdesc_control_incall_experience" msgid="5896723643771737534">"ఇన్-కాల్ వినియోగదారుని అనుభవాన్ని అందించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_control_incall_experience" msgid="5896723643771737534">"ఇన్-కాల్ వినియోగదారుని అనుభవాన్ని అందించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_readNetworkUsageHistory" msgid="8470402862501573795">"చారిత్రక నెట్‌వర్క్ వినియోగాన్ని చదవడం"</string>
     <string name="permdesc_readNetworkUsageHistory" msgid="1112962304941637102">"నిర్దిష్ట నెట్‌వర్క్‌లు మరియు యాప్‌ల కోసం చారిత్రాత్మక నెట్‌వర్క్ వినియోగాన్ని చదవడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_manageNetworkPolicy" msgid="6872549423152175378">"నెట్‌వర్క్ విధానాన్ని నిర్వహించడం"</string>
@@ -657,31 +657,31 @@
     <string name="permlab_modifyNetworkAccounting" msgid="7448790834938749041">"నెట్‌వర్క్ వినియోగ అకౌంటింగ్‌ను సవరించడం"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5076042642247205390">"యాప్‌లలో నెట్‌వర్క్ వినియోగం ఎలా గణించాలనే దాన్ని సవరించడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌ల ద్వారా ఉపయోగించడానికి ఉద్దేశించినది కాదు."</string>
     <string name="permlab_accessNotifications" msgid="7130360248191984741">"నోటిఫికేషన్‌లను యాక్సెస్ చేయడం"</string>
-    <string name="permdesc_accessNotifications" msgid="761730149268789668">"నోటిఫికేషన్‌లను, ఇతర అనువర్తనాల ద్వారా పోస్ట్ చేయబడిన వాటిని తిరిగి పొందడానికి, పరిశీలించడానికి మరియు క్లియర్ చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_accessNotifications" msgid="761730149268789668">"నోటిఫికేషన్‌లను, ఇతర అనువర్తనాల ద్వారా పోస్ట్ చేయబడిన వాటిని తిరిగి పొందడానికి, పరిశీలించడానికి మరియు క్లియర్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_bindNotificationListenerService" msgid="5848096702733262458">"నోటిఫికేషన్ పరిశీలన సేవకు అనుబంధించడం"</string>
     <string name="permdesc_bindNotificationListenerService" msgid="4970553694467137126">"నోటిఫికేషన్ పరిశీలన సేవ యొక్క అగ్ర-స్థాయి ఇంటర్‌ఫేస్‌కు అనుబంధించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాల కోసం ఎప్పటికీ అవసరం ఉండకూడదు."</string>
     <string name="permlab_bindConditionProviderService" msgid="5245421224814878483">"షరతు ప్రదాత సేవకు అనుబంధించడం"</string>
     <string name="permdesc_bindConditionProviderService" msgid="6106018791256120258">"షరతు ప్రదాత సేవ యొక్క అగ్ర-స్థాయి ఇంటర్‌ఫేస్‌కు అనుబంధించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
     <string name="permlab_bindDreamService" msgid="4776175992848982706">"డ్రీమ్ సేవ‌కి అనుబంధించడం"</string>
     <string name="permdesc_bindDreamService" msgid="9129615743300572973">"డ్రీమ్ సేవ యొక్క అగ్ర-స్థాయి ఇంటర్‌ఫేస్‌కు అనుబంధించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
-    <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"క్యారియర్ అందించిన కాన్ఫిగరేషన్ అనువర్తనాన్ని అభ్యర్థించడం"</string>
-    <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"క్యారియర్ అందించిన కాన్ఫిగరేషన్ అనువర్తనాన్ని అభ్యర్థించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాల కోసం ఎప్పటికీ అవసరం ఉండకూడదు."</string>
+    <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"క్యారియర్ అందించిన కాన్ఫిగరేషన్ యాప్‌ను అభ్యర్థించడం"</string>
+    <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"క్యారియర్ అందించిన కాన్ఫిగరేషన్ యాప్‌ను అభ్యర్థించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాల కోసం ఎప్పటికీ అవసరం ఉండకూడదు."</string>
     <string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"నెట్‌వర్క్ పరిస్థితులపై పరిశీలనల గురించి తెలుసుకోవడం"</string>
-    <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"నెట్‌వర్క్ పరిస్థితులపై పరిశీలనల గురించి తెలుసుకోవడానికి అనువర్తనాన్ని అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండకూడదు."</string>
+    <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"నెట్‌వర్క్ పరిస్థితులపై పరిశీలనల గురించి తెలుసుకోవడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండకూడదు."</string>
     <string name="permlab_setInputCalibration" msgid="932069700285223434">"ఇన్‌పుట్ పరికరం క్రమాంకనాన్ని మార్చండి"</string>
-    <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"టచ్ స్క్రీన్ యొక్క క్రమాంకన పరామితులను సవరించడానికి అనువర్తనాన్ని అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
+    <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"టచ్ స్క్రీన్ యొక్క క్రమాంకన పరామితులను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
     <string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"DRM ప్రమాణపత్రాలను యాక్సెస్ చేయడం"</string>
-    <string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"DRM ప్రమాణపత్రాలను కేటాయించడానికి మరియు ఉపయోగించడానికి అనువర్తనాన్ని అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
+    <string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"DRM ప్రమాణపత్రాలను కేటాయించడానికి మరియు ఉపయోగించడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
     <string name="permlab_handoverStatus" msgid="7620438488137057281">"Android Beam బదిలీ స్థితిని స్వీకరించడం"</string>
-    <string name="permdesc_handoverStatus" msgid="3842269451732571070">"ప్రస్తుత Android Beam బదిలీలకు సంబంధించిన సమాచారాన్ని స్వీకరించడానికి ఈ అనువర్తనాన్ని అనుమతిస్తుంది"</string>
+    <string name="permdesc_handoverStatus" msgid="3842269451732571070">"ప్రస్తుత Android Beam బదిలీలకు సంబంధించిన సమాచారాన్ని స్వీకరించడానికి ఈ యాప్‌ను అనుమతిస్తుంది"</string>
     <string name="permlab_removeDrmCertificates" msgid="710576248717404416">"DRM ప్రమాణపత్రాలను తీసివేయడం"</string>
-    <string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"DRM ప్రమాణపత్రాలను తీసివేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
+    <string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"DRM ప్రమాణపత్రాలను తీసివేయడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
     <string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"క్యారియర్ సందేశ సేవకు అనుబంధించడం"</string>
     <string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"క్యారియర్ సందేశ సేవ యొక్క అగ్ర-స్థాయి ఇంటర్‌ఫేస్‌కు అనుబంధించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
     <string name="permlab_bindCarrierServices" msgid="2395596978626237474">"క్యారియర్ సేవలకు అనుబంధించడం"</string>
     <string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"క్యారియర్ సేవలకు అనుబంధించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
     <string name="permlab_access_notification_policy" msgid="5524112842876975537">"అంతరాయం కలిగించవద్దును యాక్సెస్ చేయడం"</string>
-    <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"అంతరాయం కలిగించవద్దు ఎంపిక కాన్ఫిగరేషన్ చదవడానికి మరియు వ్రాయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"అంతరాయం కలిగించవద్దు ఎంపిక కాన్ఫిగరేషన్ చదవడానికి మరియు వ్రాయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"వీక్షణ అనుమతి వినియోగాన్ని ప్రారంభించండి"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"యాప్‌నకు అనుమతి వినియోగాన్ని ప్రారంభించడానికి హోల్డర్‌‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ ఇటువంటి అనుమతి అవసరం ఉండదు."</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"పాస్‌వర్డ్ నియమాలను సెట్ చేయండి"</string>
@@ -1126,7 +1126,7 @@
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"కొన్ని సిస్టమ్ కార్యాచరణలు పని చేయకపోవచ్చు"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"సిస్టమ్ కోసం తగినంత నిల్వ లేదు. మీకు 250MB ఖాళీ స్థలం ఉందని నిర్ధారించుకుని, పునఃప్రారంభించండి."</string>
     <string name="app_running_notification_title" msgid="8985999749231486569">"<xliff:g id="APP_NAME">%1$s</xliff:g> అమలులో ఉంది"</string>
-    <string name="app_running_notification_text" msgid="5120815883400228566">"మరింత సమాచారం కోసం లేదా అనువర్తనాన్ని ఆపివేయడం కోసం నొక్కండి."</string>
+    <string name="app_running_notification_text" msgid="5120815883400228566">"మరింత సమాచారం కోసం లేదా యాప్‌ను ఆపివేయడం కోసం నొక్కండి."</string>
     <string name="ok" msgid="2646370155170753815">"సరే"</string>
     <string name="cancel" msgid="6908697720451760115">"రద్దు చేయి"</string>
     <string name="yes" msgid="9069828999585032361">"సరే"</string>
@@ -1159,28 +1159,28 @@
     <string name="whichSendToApplication" msgid="77101541959464018">"దీన్ని ఉపయోగించి పంపండి"</string>
     <string name="whichSendToApplicationNamed" msgid="3385686512014670003">"%1$sని ఉపయోగించి పంపండి"</string>
     <string name="whichSendToApplicationLabel" msgid="3543240188816513303">"పంపు"</string>
-    <string name="whichHomeApplication" msgid="8276350727038396616">"హోమ్ అనువర్తనాన్ని ఎంచుకోండి"</string>
+    <string name="whichHomeApplication" msgid="8276350727038396616">"హోమ్ యాప్‌ను ఎంచుకోండి"</string>
     <string name="whichHomeApplicationNamed" msgid="5855990024847433794">"%1$sని హోమ్‌గా ఉపయోగించండి"</string>
     <string name="whichHomeApplicationLabel" msgid="8907334282202933959">"చిత్రాన్ని క్యాప్చర్ చేయి"</string>
     <string name="whichImageCaptureApplication" msgid="2737413019463215284">"దీనితో చిత్రాన్ని క్యాప్చర్ చేయి"</string>
     <string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"%1$sతో చిత్రాన్ని క్యాప్చర్ చేయండి"</string>
     <string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"చిత్రాన్ని క్యాప్చర్ చేయి"</string>
     <string name="alwaysUse" msgid="3153558199076112903">"ఈ చర్యకు డిఫాల్ట్‌గా ఉపయోగించండి."</string>
-    <string name="use_a_different_app" msgid="4987790276170972776">"వేరొక అనువర్తనాన్ని ఉపయోగించండి"</string>
-    <string name="clearDefaultHintMsg" msgid="1325866337702524936">"సిస్టమ్ సెట్టింగ్‌లు &gt; అనువర్తనాలు &gt; డౌన్‌లోడ్ చేయబడినవిలో డిఫాల్ట్‌ను క్లియర్ చేయి."</string>
+    <string name="use_a_different_app" msgid="4987790276170972776">"వేరొక యాప్‌ను ఉపయోగించండి"</string>
+    <string name="clearDefaultHintMsg" msgid="1325866337702524936">"సిస్టమ్ సెట్టింగ్‌లు &gt; యాప్‌లు &gt; డౌన్‌లోడ్ చేయబడినవిలో డిఫాల్ట్‌ను క్లియర్ చేయి."</string>
     <string name="chooseActivity" msgid="8563390197659779956">"చర్యను ఎంచుకోండి"</string>
     <string name="chooseUsbActivity" msgid="2096269989990986612">"USB పరికరం కోసం యాప్‌ను ఎంచుకోండి"</string>
-    <string name="noApplications" msgid="1186909265235544019">"ఈ చర్యను అమలు చేయగల అనువర్తనాలు ఏవీ లేవు."</string>
+    <string name="noApplications" msgid="1186909265235544019">"ఈ చర్యను అమలు చేయగల యాప్‌లు ఏవీ లేవు."</string>
     <string name="aerr_application" msgid="4090916809370389109">"<xliff:g id="APPLICATION">%1$s</xliff:g> ఆపివేయబడింది"</string>
     <string name="aerr_process" msgid="4268018696970966407">"<xliff:g id="PROCESS">%1$s</xliff:g> ఆపివేయబడింది"</string>
     <string name="aerr_application_repeated" msgid="7804378743218496566">"<xliff:g id="APPLICATION">%1$s</xliff:g> పునరావృతంగా ఆపివేయబడుతోంది"</string>
     <string name="aerr_process_repeated" msgid="1153152413537954974">"<xliff:g id="PROCESS">%1$s</xliff:g> పునరావృతంగా ఆపివేయబడుతోంది"</string>
-    <string name="aerr_restart" msgid="2789618625210505419">"అనువర్తనాన్ని మళ్లీ తెరువు"</string>
+    <string name="aerr_restart" msgid="2789618625210505419">"యాప్‌ను మళ్లీ తెరువు"</string>
     <string name="aerr_report" msgid="3095644466849299308">"ఫీడ్‌బ్యాక్‌ను పంపు"</string>
     <string name="aerr_close" msgid="3398336821267021852">"మూసివేయి"</string>
     <string name="aerr_mute" msgid="2304972923480211376">"పరికరం పునఃప్రారంభమయ్యే వరకు మ్యూట్ చేయి"</string>
     <string name="aerr_wait" msgid="3198677780474548217">"వేచి ఉండండి"</string>
-    <string name="aerr_close_app" msgid="8318883106083050970">"అనువర్తనాన్ని మూసివేయి"</string>
+    <string name="aerr_close_app" msgid="8318883106083050970">"యాప్‌ను మూసివేయి"</string>
     <string name="anr_title" msgid="7290329487067300120"></string>
     <string name="anr_activity_application" msgid="8121716632960340680">"<xliff:g id="APPLICATION">%2$s</xliff:g> ప్రతిస్పందించడం లేదు"</string>
     <string name="anr_activity_process" msgid="3477362583767128667">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ప్రతిస్పందించడం లేదు"</string>
@@ -1195,7 +1195,7 @@
     <string name="launch_warning_original" msgid="3332206576800169626">"<xliff:g id="APP_NAME">%1$s</xliff:g> వాస్తవంగా ప్రారంభించబడింది."</string>
     <string name="screen_compat_mode_scale" msgid="8627359598437527726">"ప్రమాణం"</string>
     <string name="screen_compat_mode_show" msgid="5080361367584709857">"ఎల్లప్పుడూ చూపు"</string>
-    <string name="screen_compat_mode_hint" msgid="4032272159093750908">"సిస్టమ్ సెట్టింగ్‌లు &gt; అనువర్తనాలు &gt; డౌన్‌లోడ్ చేసినవిలో దీన్ని పునఃప్రారంభించండి."</string>
+    <string name="screen_compat_mode_hint" msgid="4032272159093750908">"సిస్టమ్ సెట్టింగ్‌లు &gt; యాప్‌లు &gt; డౌన్‌లోడ్ చేసినవిలో దీన్ని పునఃప్రారంభించండి."</string>
     <string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> ప్రస్తుత ప్రదర్శన పరిమాణ సెట్టింగ్‌కు మద్దతు ఇవ్వదు, దీని వలన ఊహించని సమస్యలు తలెత్తవచ్చు."</string>
     <string name="unsupported_display_size_show" msgid="980129850974919375">"ఎల్లప్పుడూ చూపు"</string>
     <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"Android OS యొక్క అననుకూల వెర్షన్ కోసం <xliff:g id="APP_NAME">%1$s</xliff:g> రూపొందించబడింది మరియు ఊహించని సమస్యలు తలెత్తవచ్చు. యాప్ యొక్క అప్‌డేట్ చేసిన వెర్షన్ అందుబాటులో ఉండవచ్చు."</string>
@@ -1340,7 +1340,7 @@
     <string name="taking_remote_bugreport_notification_title" msgid="1582531382166919850">"బగ్ నివేదికను తీస్తోంది…"</string>
     <string name="share_remote_bugreport_notification_title" msgid="6708897723753334999">"బగ్ నివేదికను భాగస్వామ్యం చేయాలా?"</string>
     <string name="sharing_remote_bugreport_notification_title" msgid="3077385149217638550">"బగ్ నివేదికను భాగస్వామ్యం చేస్తోంది..."</string>
-    <string name="share_remote_bugreport_notification_message_finished" msgid="7325635795739260135">"మీ నిర్వాహకులు ఈ పరికరం సమస్యకు పరిష్కారాన్ని కనుగొనడంలో సహాయం కోసం బగ్ నివేదికను అభ్యర్థించారు. అనువర్తనాలు మరియు డేటా భాగస్వామ్యం చేయబడవచ్చు."</string>
+    <string name="share_remote_bugreport_notification_message_finished" msgid="7325635795739260135">"మీ నిర్వాహకులు ఈ పరికరం సమస్యకు పరిష్కారాన్ని కనుగొనడంలో సహాయం కోసం బగ్ నివేదికను అభ్యర్థించారు. యాప్‌లు మరియు డేటా భాగస్వామ్యం చేయబడవచ్చు."</string>
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"షేర్ చేయి"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"తిరస్కరిస్తున్నాను"</string>
     <string name="select_input_method" msgid="3971267998568587025">"ఇన్‌పుట్ పద్ధతిని ఎంచుకోండి"</string>
@@ -1403,13 +1403,13 @@
     <string name="ext_media_status_missing" msgid="6520746443048867314">"చొప్పించబడలేదు"</string>
     <string name="activity_list_empty" msgid="4219430010716034252">"సరిపోలే కార్యాచరణలు కనుగొనబడలేదు."</string>
     <string name="permlab_route_media_output" msgid="8048124531439513118">"మీడియా అవుట్‌పుట్‌ను మళ్లించడం"</string>
-    <string name="permdesc_route_media_output" msgid="1759683269387729675">"మీడియా అవుట్‌పుట్‌ను ఇతర బాహ్య పరికరాలకు మళ్లించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_route_media_output" msgid="1759683269387729675">"మీడియా అవుట్‌పుట్‌ను ఇతర బాహ్య పరికరాలకు మళ్లించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_readInstallSessions" msgid="7279049337895583621">"ఇన్‌స్టాల్ సెషన్‌లను చదవడం"</string>
-    <string name="permdesc_readInstallSessions" msgid="4012608316610763473">"ఇన్‌స్టాల్ సెషన్‌లను చదవడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఇది సక్రియ ప్యాకేజీ ఇన్‌స్టాలేషన్‌ల గురించి వివరాలను చూడటానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_readInstallSessions" msgid="4012608316610763473">"ఇన్‌స్టాల్ సెషన్‌లను చదవడానికి యాప్‌ను అనుమతిస్తుంది. ఇది సక్రియ ప్యాకేజీ ఇన్‌స్టాలేషన్‌ల గురించి వివరాలను చూడటానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_requestInstallPackages" msgid="7600020863445351154">"ఇన్‌స్టాల్ ప్యాకేజీలను అభ్యర్థించడం"</string>
-    <string name="permdesc_requestInstallPackages" msgid="3969369278325313067">"ప్యాకేజీల ఇన్‌స్టాలేషన్ అభ్యర్థించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_requestInstallPackages" msgid="3969369278325313067">"ప్యాకేజీల ఇన్‌స్టాలేషన్ అభ్యర్థించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_requestDeletePackages" msgid="2541172829260106795">"ప్యాకేజీలను తొలగించడానికి అభ్యర్థించు"</string>
-    <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ప్యాకేజీల తొలగింపును అభ్యర్థించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ప్యాకేజీల తొలగింపును అభ్యర్థించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"బ్యాటరీ అనుకూలీకరణలను విస్మరించడానికి అడగాలి"</string>
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ఆ యాప్ కోసం బ్యాటరీ అనుకూలీకరణలు విస్మరించేలా అనుమతి కోరడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"జూమ్ నియంత్రణ కోసం రెండుసార్లు నొక్కండి"</string>
@@ -1430,8 +1430,8 @@
     <string name="deny" msgid="6632259981847676572">"తిరస్కరించండి"</string>
     <string name="permission_request_notification_title" msgid="1810025922441048273">"అనుమతి అభ్యర్థించబడింది"</string>
     <string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"ఖాతా <xliff:g id="ACCOUNT">%s</xliff:g> కోసం\nఅనుమతి అభ్యర్థించబడింది."</string>
-    <string name="forward_intent_to_owner" msgid="4620359037192871015">"మీరు మీ కార్యాలయ ప్రొఫైల్‌కు వెలుపల ఈ అనువర్తనాన్ని ఉపయోగిస్తున్నారు"</string>
-    <string name="forward_intent_to_work" msgid="3620262405636021151">"మీరు మీ కార్యాలయ ప్రొఫైల్‌లో ఈ అనువర్తనాన్ని ఉపయోగిస్తున్నారు"</string>
+    <string name="forward_intent_to_owner" msgid="4620359037192871015">"మీరు మీ కార్యాలయ ప్రొఫైల్‌కు వెలుపల ఈ యాప్‌ను ఉపయోగిస్తున్నారు"</string>
+    <string name="forward_intent_to_work" msgid="3620262405636021151">"మీరు మీ కార్యాలయ ప్రొఫైల్‌లో ఈ యాప్‌ను ఉపయోగిస్తున్నారు"</string>
     <string name="input_method_binding_label" msgid="1166731601721983656">"ఇన్‌పుట్ పద్ధతి"</string>
     <string name="sync_binding_label" msgid="469249309424662147">"సమకాలీకరణ"</string>
     <string name="accessibility_binding_label" msgid="1974602776545801715">"యాక్సెస్ సామర్థ్యం"</string>
@@ -1909,7 +1909,7 @@
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ఈ యాప్ పాత వెర్షన్ Android కోసం రూపొందించబడింది మరియు అది సరిగ్గా పని చేయకపోవచ్చు. అప్‌డేట్‌ల కోసం తనిఖీ చేయడానికి ప్రయత్నించండి లేదా డెవలపర్‌ని సంప్రదించండి."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"అప్‌డేట్ కోసం తనిఖీ చేయండి"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"మీకు కొత్త సందేశాలు ఉన్నాయి"</string>
-    <string name="new_sms_notification_content" msgid="3197949934153460639">"వీక్షించడానికి SMS అనువర్తనాన్ని తెరవండి"</string>
+    <string name="new_sms_notification_content" msgid="3197949934153460639">"వీక్షించడానికి SMS యాప్‌ను తెరవండి"</string>
     <string name="profile_encrypted_title" msgid="9001208667521266472">"కొంత ఫంక్షనాలిటీ పరిమితం కావచ్చు"</string>
     <string name="profile_encrypted_detail" msgid="5279730442756849055">"కార్యాలయ ప్రొఫైల్ లాక్ అయింది"</string>
     <string name="profile_encrypted_message" msgid="1128512616293157802">"కార్యాలయ ప్రొఫైల్ అన్‌లాక్ చేయుటకు నొక్కండి"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 8f54c27..b66a80e 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -149,8 +149,7 @@
     <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"‏صرف Wi-Fi"</string>
     <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
     <skip />
-    <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) -->
-    <skip />
+    <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"‏<xliff:g id="SPN">%s</xliff:g> کراس sim کالنگ"</string>
     <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : فارورڈ نہیں کی گئی"</string>
     <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> بعد از <xliff:g id="TIME_DELAY">{2}</xliff:g> سیکنڈ"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index a8bc4ba..2371950 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -57,8 +57,7 @@
     <string name="imei" msgid="2157082351232630390">"IMEI"</string>
     <string name="meid" msgid="3291227361605924674">"MEID"</string>
     <string name="ClipMmi" msgid="4110549342447630629">"Kiruvchi raqami"</string>
-    <!-- no translation found for ClirMmi (6752346475055446417) -->
-    <skip />
+    <string name="ClirMmi" msgid="6752346475055446417">"Chaqiruvchi raqamini yashirish"</string>
     <string name="ColpMmi" msgid="4736462893284419302">"Qo‘ng‘iroq qiluvchining raqami"</string>
     <string name="ColrMmi" msgid="5889782479745764278">"Qo‘ng‘iroq qiluvchining raqamini cheklash"</string>
     <string name="CfMmi" msgid="8390012691099787178">"Chaqiruvlarni uzatish"</string>
@@ -149,7 +148,7 @@
     <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Faqat Wi-Fi"</string>
     <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
     <skip />
-    <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Umumiy sim chaqiruvlar"</string>
+    <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Kross-SIM chaqiruvlar"</string>
     <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Yo‘naltirilmadi"</string>
     <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>  <xliff:g id="TIME_DELAY">{2}</xliff:g> soniyadan so‘ng"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index bb26655..c16588c 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2487,7 +2487,7 @@
         <attr name="directBootAware" />
         <attr name="visibleToInstantApps" />
         <!-- The code for this component is located in the given split.
-             @deprecated Do not use it. This is not supported. -->
+             <p>NOTE: This is only applicable to instant app. -->
         <attr name="splitName" />
     </declare-styleable>
 
@@ -2604,7 +2604,7 @@
         <attr name="externalService" format="boolean" />
         <attr name="visibleToInstantApps" />
         <!-- The code for this component is located in the given split.
-             @deprecated Do not use it. This is not supported. -->
+             <p>NOTE: This is only applicable to instant app. -->
         <attr name="splitName" />
         <!-- If true, and this is an {@link android.R.attr#isolatedProcess} service, the service
              will be spawned from an Application Zygote, instead of the regular Zygote.
diff --git a/core/res/res/values/dimens_car.xml b/core/res/res/values/dimens_car.xml
index c3cd80b..0ef60c4 100644
--- a/core/res/res/values/dimens_car.xml
+++ b/core/res/res/values/dimens_car.xml
@@ -17,6 +17,7 @@
 -->
 <resources>
     <dimen name="car_large_avatar_size">96dp</dimen>
+    <dimen name="car_large_avatar_badge_size">32dp</dimen>
     <!-- Application Bar -->
     <dimen name="car_app_bar_height">80dp</dimen>
     <!-- Margin -->
diff --git a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
index d8ed805..daae957 100644
--- a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
+++ b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
@@ -76,6 +76,12 @@
     private static final long DUMPSTATE_STARTUP_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10);
     private static final long UIAUTOMATOR_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10);
 
+
+    // A small timeout used when waiting for the result of a BugreportCallback to be received.
+    // This value must be at least 1000ms since there is an intentional delay in
+    // BugreportManagerServiceImpl in the error case.
+    private static final long CALLBACK_RESULT_TIMEOUT_MS = 1500;
+
     // Sent by Shell when its bugreport finishes (contains final bugreport/screenshot file name
     // associated with the bugreport).
     private static final String INTENT_BUGREPORT_FINISHED =
@@ -185,7 +191,7 @@
         ParcelFileDescriptor bugreportFd2 = parcelFd(bugreportFile2);
         ParcelFileDescriptor screenshotFd2 = parcelFd(screenshotFile2);
         mBrm.startBugreport(bugreportFd2, screenshotFd2, wifi(), mExecutor, callback2);
-        Thread.sleep(500 /* .5s */);
+        Thread.sleep(CALLBACK_RESULT_TIMEOUT_MS);
 
         // Verify #2 encounters an error.
         assertThat(callback2.getErrorCode()).isEqualTo(
@@ -194,7 +200,7 @@
 
         // Cancel #1 so we can move on to the next test.
         mBrm.cancelBugreport();
-        Thread.sleep(500 /* .5s */);
+        waitTillDoneOrTimeout(callback);
         assertThat(callback.isDone()).isTrue();
         assertFdsAreClosed(mBugreportFd, mScreenshotFd);
     }
@@ -220,7 +226,7 @@
         // Try again, with DUMP permission.
         getPermissions();
         mBrm.cancelBugreport();
-        Thread.sleep(500 /* .5s */);
+        waitTillDoneOrTimeout(callback);
         assertThat(callback.isDone()).isTrue();
         assertFdsAreClosed(mBugreportFd, mScreenshotFd);
     }
diff --git a/core/tests/coretests/src/android/graphics/FontFileUtilTest.java b/core/tests/coretests/src/android/graphics/FontFileUtilTest.java
index 1771671..52cc4ca 100644
--- a/core/tests/coretests/src/android/graphics/FontFileUtilTest.java
+++ b/core/tests/coretests/src/android/graphics/FontFileUtilTest.java
@@ -17,11 +17,15 @@
 package android.graphics;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
 import android.content.res.AssetManager;
+import android.graphics.fonts.Font;
 import android.graphics.fonts.FontFileUtil;
 import android.graphics.fonts.FontVariationAxis;
+import android.graphics.fonts.SystemFonts;
 import android.util.Log;
 import android.util.Pair;
 
@@ -144,4 +148,18 @@
             assertEquals(path + "#" + axes, italic, FontFileUtil.unpackItalic(packed));
         }
     }
+
+    @Test
+    public void testExtension() throws IOException {
+        for (Font f : SystemFonts.getAvailableFonts()) {
+            String name = f.getFile().getName();
+            if (!name.endsWith(".ttf") && !name.endsWith(".otf")) {
+                continue;  // Only test ttf/otf file
+            }
+            int isOtfFile = FontFileUtil.isPostScriptType1Font(f.getBuffer(), 0);
+            assertNotEquals(-1, isOtfFile);
+            String extension = isOtfFile == 1 ? ".otf" : ".ttf";
+            assertTrue(name.endsWith(extension));
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
index 0f17d27..6be9306 100644
--- a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
+++ b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
@@ -254,7 +254,7 @@
         assertEquals("19–22 de ene. de 2009",
                 formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY,
                         FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-        assertEquals("lun., 19 de ene. – jue., 22 de ene. de 2009",
+        assertEquals("lun, 19 de ene. – jue, 22 de ene. de 2009",
                 formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY,
                         FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
         assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009",
@@ -265,7 +265,7 @@
         assertEquals("19 de ene. – 22 de abr. 2009",
                 formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH,
                         FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-        assertEquals("lun., 19 de ene. – mié., 22 de abr. de 2009",
+        assertEquals("lun, 19 de ene. – mié, 22 de abr. de 2009",
                 formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH,
                         FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
         assertEquals("enero–abril de 2009",
@@ -286,9 +286,9 @@
 
         assertEquals("19–22 de enero de 2009",
                 formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, 0));
-        assertEquals("19–22 ene. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY,
+        assertEquals("19–22 ene 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY,
                 FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-        assertEquals("lun., 19 ene. – jue., 22 ene. 2009",
+        assertEquals("lun, 19 ene – jue, 22 ene 2009",
                 formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY,
                         FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
         assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009",
@@ -296,19 +296,19 @@
 
         assertEquals("19 de enero–22 de abril de 2009",
                 formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, 0));
-        assertEquals("19 ene. – 22 abr. 2009",
+        assertEquals("19 ene – 22 abr 2009",
                 formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH,
                         FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-        assertEquals("lun., 19 ene. – mié., 22 abr. 2009",
+        assertEquals("lun, 19 ene – mié, 22 abr 2009",
                 formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH,
                         FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
         assertEquals("enero–abril de 2009",
                 formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
 
-        assertEquals("19 ene. 2009 – 9 feb. 2012",
+        assertEquals("19 ene 2009 – 9 feb 2012",
                 formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR,
                         FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-        assertEquals("ene. 2009 – feb. 2012",
+        assertEquals("ene 2009 – feb 2012",
                 formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR,
                         FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
         assertEquals("19 de enero de 2009–9 de febrero de 2012",
diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java
index 068d047..5612833 100644
--- a/core/tests/coretests/src/android/text/format/FormatterTest.java
+++ b/core/tests/coretests/src/android/text/format/FormatterTest.java
@@ -212,7 +212,7 @@
 
         // Make sure it works on different locales.
         setLocale(new Locale("ru", "RU"));
-        assertEquals("1 мин.", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
+        assertEquals("1 мин", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
                 mContext, 1 * SECOND));
     }
 
diff --git a/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java b/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java
index 4b3b573..b342516 100644
--- a/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java
+++ b/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java
@@ -755,8 +755,8 @@
         final Locale locale = new Locale("fr");
         android.icu.text.RelativeDateTimeFormatter icuFormatter =
                 android.icu.text.RelativeDateTimeFormatter.getInstance(locale);
-        assertEquals("D à T", icuFormatter.combineDateAndTime("D", "T"));
+        assertEquals("D, T", icuFormatter.combineDateAndTime("D", "T"));
         // Ensure single quote ' and curly braces {} are not interpreted in input values.
-        assertEquals("D'x' à T{0}", icuFormatter.combineDateAndTime("D'x'", "T{0}"));
+        assertEquals("D'x', T{0}", icuFormatter.combineDateAndTime("D'x'", "T{0}"));
     }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/ConversationActionTest.java b/core/tests/coretests/src/android/view/textclassifier/ConversationActionTest.java
index 07eeae0..e1b403f 100644
--- a/core/tests/coretests/src/android/view/textclassifier/ConversationActionTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/ConversationActionTest.java
@@ -39,7 +39,8 @@
     @Test
     public void toBuilder() {
         final Context context = InstrumentationRegistry.getTargetContext();
-        final PendingIntent intent = PendingIntent.getActivity(context, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
+        final PendingIntent intent = PendingIntent.getActivity(
+                context, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE);
         final Icon icon = Icon.createWithData(new byte[]{0}, 0, 1);
         final Bundle extras = new Bundle();
         extras.putInt("key", 5);
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index db62e17..c393d68 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -16,6 +16,8 @@
 
 package android.view.textclassifier;
 
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -79,7 +81,7 @@
         final String primaryDescription = "primaryDescription";
         final Intent primaryIntent = new Intent("primaryIntentAction");
         final PendingIntent primaryPendingIntent = PendingIntent.getActivity(context, 0,
-                primaryIntent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
+                primaryIntent, FLAG_IMMUTABLE);
         final RemoteAction remoteAction0 = new RemoteAction(primaryIcon, primaryLabel,
                 primaryDescription, primaryPendingIntent);
 
@@ -88,7 +90,7 @@
         final String secondaryDescription = "secondaryDescription";
         final Intent secondaryIntent = new Intent("secondaryIntentAction");
         final PendingIntent secondaryPendingIntent = PendingIntent.getActivity(context, 0,
-                secondaryIntent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
+                secondaryIntent, FLAG_IMMUTABLE);
         final RemoteAction remoteAction1 = new RemoteAction(secondaryIcon, secondaryLabel,
                 secondaryDescription, secondaryPendingIntent);
 
@@ -156,7 +158,7 @@
         final int iconColor = Color.RED;
         final String label = "label";
         final PendingIntent pendingIntent = PendingIntent.getActivity(
-                context, 0, new Intent("ACTION_0"), PendingIntent.FLAG_MUTABLE_UNAUDITED);
+                context, 0, new Intent("ACTION_0"), FLAG_IMMUTABLE);
         final RemoteAction remoteAction = new RemoteAction(
                 generateTestIcon(width, height, iconColor),
                 label,
@@ -239,7 +241,8 @@
                 .setIntent(new Intent("action"))
                 .setOnClickListener(view -> { })
                 .addAction(new RemoteAction(icon1, "title1", "desc1",
-                          PendingIntent.getActivity(context, 0, new Intent("action1"), PendingIntent.FLAG_MUTABLE_UNAUDITED)))
+                        PendingIntent.getActivity(
+                                context, 0, new Intent("action1"), FLAG_IMMUTABLE)))
                 .addAction(new RemoteAction(icon1, "title2", "desc2",
                           PendingIntent.getActivity(context, 0, new Intent("action2"), 0)))
                 .setEntityType(TextClassifier.TYPE_EMAIL, 0.5f)
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
index 5def552..14c077c 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
@@ -16,6 +16,8 @@
 
 package android.view.textclassifier;
 
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
@@ -128,7 +130,8 @@
 
         final TextClassification classification = new TextClassification.Builder()
                 .addAction(new RemoteAction(icon1, "title1", "desc1",
-                        PendingIntent.getActivity(context, 0, new Intent("action1"), 0)))
+                        PendingIntent.getActivity(
+                                context, 0, new Intent("action1"), FLAG_IMMUTABLE)))
                 .setEntityType(TextClassifier.TYPE_ADDRESS, 1.0f)
                 .build();
         final TextSelection textSelection = new TextSelection.Builder(startIndex, endIndex)
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 8ff318e..dc33253 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -58,6 +58,7 @@
         KernelWakelockReaderTest.class,
         LongSamplingCounterTest.class,
         LongSamplingCounterArrayTest.class,
+        MobileRadioPowerCalculatorTest.class,
         PowerCalculatorTest.class,
         PowerProfileTest.class,
         ScreenPowerCalculatorTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
index 55f64f9..59534e4 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
@@ -16,10 +16,13 @@
 
 package com.android.internal.os;
 
+import static org.mockito.ArgumentMatchers.anyDouble;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.net.NetworkStats;
 import android.os.BatteryStats;
 import android.os.BatteryUsageStats;
 import android.os.BatteryUsageStatsQuery;
@@ -32,6 +35,7 @@
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
+import org.mockito.stubbing.Answer;
 
 public class BatteryUsageStatsRule implements TestRule {
     private final PowerProfile mPowerProfile;
@@ -53,6 +57,14 @@
 
     public BatteryUsageStatsRule setAveragePower(String key, double value) {
         when(mPowerProfile.getAveragePower(key)).thenReturn(value);
+        when(mPowerProfile.getAveragePowerOrDefault(eq(key), anyDouble())).thenReturn(value);
+        return this;
+    }
+
+    public BatteryUsageStatsRule setAveragePowerUnspecified(String key) {
+        when(mPowerProfile.getAveragePower(key)).thenReturn(0.0);
+        when(mPowerProfile.getAveragePowerOrDefault(eq(key), anyDouble()))
+                .thenAnswer((Answer<Double>) invocation -> (Double) invocation.getArguments()[1]);
         return this;
     }
 
@@ -64,6 +76,10 @@
         return this;
     }
 
+    public void setNetworkStats(NetworkStats networkStats) {
+        mBatteryStats.setNetworkStats(networkStats);
+    }
+
     @Override
     public Statement apply(Statement base, Description description) {
         return new Statement() {
@@ -76,6 +92,7 @@
     }
 
     private void noteOnBattery() {
+        mBatteryStats.setOnBatteryInternal(true);
         mBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0);
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
new file mode 100644
index 0000000..4230066
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.net.ConnectivityManager;
+import android.net.NetworkStats;
+import android.os.BatteryConsumer;
+import android.os.Process;
+import android.os.SystemBatteryConsumer;
+import android.os.UidBatteryConsumer;
+import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.ModemActivityInfo;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MobileRadioPowerCalculatorTest {
+    private static final double PRECISION = 0.00001;
+    private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
+
+    @Rule
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+            .setAveragePowerUnspecified(PowerProfile.POWER_RADIO_ACTIVE)
+            .setAveragePowerUnspecified(PowerProfile.POWER_RADIO_ON)
+            .setAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE, 360.0)
+            .setAveragePower(PowerProfile.POWER_RADIO_SCANNING, 720.0)
+            .setAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX, 1440.0)
+            .setAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX,
+                    new double[] {720.0, 1080.0, 1440.0, 1800.0, 2160.0});
+
+    @Test
+    public void testCounterBasedModel() {
+        BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+
+        // Scan for a cell
+        stats.notePhoneStateLocked(ServiceState.STATE_OUT_OF_SERVICE,
+                TelephonyManager.SIM_STATE_READY,
+                2000, 2000);
+
+        // Found a cell
+        stats.notePhoneStateLocked(ServiceState.STATE_IN_SERVICE, TelephonyManager.SIM_STATE_READY,
+                5000, 5000);
+
+        // Note cell signal strength
+        SignalStrength signalStrength = mock(SignalStrength.class);
+        when(signalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_MODERATE);
+        stats.notePhoneSignalStrengthLocked(signalStrength, 5000, 5000);
+
+        stats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
+                8_000_000_000L, APP_UID, 8000, 8000);
+
+        // Note established network
+        stats.noteNetworkInterfaceType("cellular", ConnectivityManager.TYPE_MOBILE);
+
+        // Note application network activity
+        NetworkStats networkStats = new NetworkStats(10000, 1)
+                .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100);
+        mStatsRule.setNetworkStats(networkStats);
+
+        ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
+                new int[] {100, 200, 300, 400, 500}, 600);
+        stats.noteModemControllerActivity(mai, 10000, 10000);
+
+        mStatsRule.setTime(12_000_000, 12_000_000);
+
+        MobileRadioPowerCalculator calculator =
+                new MobileRadioPowerCalculator(mStatsRule.getPowerProfile());
+
+        mStatsRule.apply(calculator);
+
+        SystemBatteryConsumer consumer =
+                mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO);
+        assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
+                .isWithin(PRECISION).of(1.44440);
+
+        UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+        assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
+                .isWithin(PRECISION).of(0.8);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index fc23721..bf74c8d 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.os;
 
-import android.location.GnssSignalQuality;
+import android.net.NetworkStats;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.SparseIntArray;
@@ -38,26 +38,14 @@
 public class MockBatteryStatsImpl extends BatteryStatsImpl {
     public BatteryStatsImpl.Clocks clocks;
     public boolean mForceOnBattery;
+    private NetworkStats mNetworkStats;
 
     MockBatteryStatsImpl(Clocks clocks) {
         super(clocks);
         this.clocks = mClocks;
-        mScreenOnTimer = new BatteryStatsImpl.StopwatchTimer(clocks, null, -1, null,
-                mOnBatteryTimeBase);
-        mScreenDozeTimer = new BatteryStatsImpl.StopwatchTimer(clocks, null, -1, null,
-                mOnBatteryTimeBase);
-        for (int i = 0; i < mScreenBrightnessTimer.length; i++) {
-            mScreenBrightnessTimer[i] = new BatteryStatsImpl.StopwatchTimer(clocks, null, -1, null,
-                    mOnBatteryTimeBase);
-        }
-        mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase);
-        mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, 1);
-        setExternalStatsSyncLocked(new DummyExternalStatsSync());
+        initTimersAndCounters();
 
-        for (int i = 0; i < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; i++) {
-            mGpsSignalQualityTimer[i] = new StopwatchTimer(clocks, null, -1000 - i, null,
-                    mOnBatteryTimeBase);
-        }
+        setExternalStatsSyncLocked(new DummyExternalStatsSync());
 
         final boolean[] supportedBuckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS];
         Arrays.fill(supportedBuckets, true);
@@ -106,6 +94,16 @@
         return getUidStatsLocked(uid).mOnBatteryScreenOffBackgroundTimeBase;
     }
 
+    public MockBatteryStatsImpl setNetworkStats(NetworkStats networkStats) {
+        mNetworkStats = networkStats;
+        return this;
+    }
+
+    @Override
+    protected NetworkStats readNetworkStatsLocked(String[] ifaces) {
+        return mNetworkStats;
+    }
+
     public MockBatteryStatsImpl setPowerProfile(PowerProfile powerProfile) {
         mPowerProfile = powerProfile;
         return this;
diff --git a/core/tests/overlaytests/device/Android.bp b/core/tests/overlaytests/device/Android.bp
index 12a2b08..f86ac9c 100644
--- a/core/tests/overlaytests/device/Android.bp
+++ b/core/tests/overlaytests/device/Android.bp
@@ -16,7 +16,11 @@
     name: "OverlayDeviceTests",
     srcs: ["src/**/*.java"],
     platform_apis: true,
-    static_libs: ["androidx.test.rules"],
+    certificate: "platform",
+    static_libs: [
+        "androidx.test.rules",
+        "testng",
+    ],
     test_suites: ["device-tests"],
     data: [
         ":OverlayDeviceTests_AppOverlayOne",
diff --git a/core/tests/overlaytests/device/AndroidManifest.xml b/core/tests/overlaytests/device/AndroidManifest.xml
index 4881636..a69911f 100644
--- a/core/tests/overlaytests/device/AndroidManifest.xml
+++ b/core/tests/overlaytests/device/AndroidManifest.xml
@@ -19,6 +19,8 @@
 
     <uses-sdk android:minSdkVersion="21" />
 
+    <uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" />
+
     <application>
         <uses-library android:name="android.test.runner"/>
     </application>
diff --git a/core/tests/overlaytests/device/AndroidTest.xml b/core/tests/overlaytests/device/AndroidTest.xml
index 6507839..ebbdda5 100644
--- a/core/tests/overlaytests/device/AndroidTest.xml
+++ b/core/tests/overlaytests/device/AndroidTest.xml
@@ -19,9 +19,20 @@
     <option name="test-suite-tag" value="apct" />
     <option name="test-suite-tag" value="apct-instrumentation" />
 
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="remount-system" value="true" />
+        <option name="push" value="OverlayDeviceTests.apk->/system/app/OverlayDeviceTests.apk" />
+    </target_preparer>
+
+    <!-- Reboot to have the test APK scanned by PM and reboot after to remove the test APK. -->
+    <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer">
+      <option name="pre-reboot" value="true" />
+      <option name="post-reboot" value="true" />
+    </target_preparer>
+
     <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
         <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="OverlayDeviceTests.apk" />
         <option name="test-file-name" value="OverlayDeviceTests_AppOverlayOne.apk" />
         <option name="test-file-name" value="OverlayDeviceTests_AppOverlayTwo.apk" />
         <option name="test-file-name" value="OverlayDeviceTests_FrameworkOverlay.apk" />
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
index 390bb76..76c01a7 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
@@ -18,60 +18,76 @@
 
 import static java.util.concurrent.TimeUnit.SECONDS;
 
-import android.app.UiAutomation;
-import android.content.res.Resources;
-import android.os.ParcelFileDescriptor;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.om.OverlayManager;
+import android.content.om.OverlayManagerTransaction;
+import android.os.UserHandle;
 
 import androidx.test.InstrumentationRegistry;
 
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
 import java.util.concurrent.Executor;
 import java.util.concurrent.FutureTask;
 
 class LocalOverlayManager {
     private static final long TIMEOUT = 30;
 
-    public static void setEnabledAndWait(Executor executor, final String packageName,
-            boolean enable) throws Exception {
-        final String pattern = (enable ? "[x]" : "[ ]") + " " + packageName;
-        if (executeShellCommand("cmd overlay list").contains(pattern)) {
-            // nothing to do, overlay already in the requested state
-            return;
+    public static void toggleOverlaysAndWait(@NonNull final String[] overlaysToEnable,
+            @NonNull final String[] overlaysToDisable) throws Exception {
+        final int userId = UserHandle.myUserId();
+        OverlayManagerTransaction.Builder builder = new OverlayManagerTransaction.Builder();
+        for (String pkg : overlaysToEnable) {
+            builder.setEnabled(pkg, true, userId);
         }
+        for (String pkg : overlaysToDisable) {
+            builder.setEnabled(pkg, false, userId);
+        }
+        OverlayManagerTransaction transaction = builder.build();
 
-        final Resources res = InstrumentationRegistry.getContext().getResources();
-        final String[] oldApkPaths = res.getAssets().getApkPaths();
+        final Context ctx = InstrumentationRegistry.getTargetContext();
         FutureTask<Boolean> task = new FutureTask<>(() -> {
             while (true) {
-                if (!Arrays.equals(oldApkPaths, res.getAssets().getApkPaths())) {
+                final String[] paths = ctx.getResources().getAssets().getApkPaths();
+                if (arrayTailContains(paths, overlaysToEnable)
+                        && arrayDoesNotContain(paths, overlaysToDisable)) {
                     return true;
                 }
                 Thread.sleep(10);
             }
         });
+
+        OverlayManager om = ctx.getSystemService(OverlayManager.class);
+        om.commit(transaction);
+
+        Executor executor = (cmd) -> new Thread(cmd).start();
         executor.execute(task);
-        executeShellCommand("cmd overlay " + (enable ? "enable " : "disable ") + packageName);
         task.get(TIMEOUT, SECONDS);
     }
 
-    private static String executeShellCommand(final String command)
-            throws Exception {
-        final UiAutomation uiAutomation =
-                InstrumentationRegistry.getInstrumentation().getUiAutomation();
-        final ParcelFileDescriptor pfd = uiAutomation.executeShellCommand(command);
-        try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
-            final BufferedReader reader = new BufferedReader(
-                    new InputStreamReader(in, StandardCharsets.UTF_8));
-            StringBuilder str = new StringBuilder();
-            String line;
-            while ((line = reader.readLine()) != null) {
-                str.append(line);
-            }
-            return str.toString();
+    private static boolean arrayTailContains(@NonNull final String[] array,
+            @NonNull final String[] substrings) {
+        if (array.length < substrings.length) {
+            return false;
         }
+        for (int i = 0; i < substrings.length; i++) {
+            String a = array[array.length - substrings.length + i];
+            String s = substrings[i];
+            if (!a.contains(s)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static boolean arrayDoesNotContain(@NonNull final String[] array,
+            @NonNull final String[] substrings) {
+        for (String s : substrings) {
+            for (String a : array) {
+                if (a.contains(s)) {
+                    return false;
+                }
+            }
+        }
+        return true;
     }
 }
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java
new file mode 100644
index 0000000..0b4f5e2
--- /dev/null
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2019 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.overlaytest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.content.om.OverlayInfo;
+import android.content.om.OverlayManager;
+import android.content.om.OverlayManagerTransaction;
+import android.content.res.Resources;
+import android.os.UserHandle;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+
+@RunWith(JUnit4.class)
+@MediumTest
+public class TransactionTest {
+    static final String APP_OVERLAY_ONE_PKG = "com.android.overlaytest.app_overlay_one";
+    static final String APP_OVERLAY_TWO_PKG = "com.android.overlaytest.app_overlay_two";
+
+    private Context mContext;
+    private Resources mResources;
+    private OverlayManager mOverlayManager;
+    private int mUserId;
+    private UserHandle mUserHandle;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getContext();
+        mResources = mContext.getResources();
+        mOverlayManager = mContext.getSystemService(OverlayManager.class);
+        mUserId = UserHandle.myUserId();
+        mUserHandle = UserHandle.of(mUserId);
+
+        LocalOverlayManager.toggleOverlaysAndWait(
+                new String[]{},
+                new String[]{APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
+    }
+
+    @Test
+    public void testValidTransaction() throws Exception {
+        assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
+        assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
+
+        OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
+                .setEnabled(APP_OVERLAY_ONE_PKG, true)
+                .setEnabled(APP_OVERLAY_TWO_PKG, true)
+                .build();
+        mOverlayManager.commit(t);
+
+        assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
+        assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, true, mUserId);
+        List<OverlayInfo> ois =
+                mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
+        assertEquals(ois.size(), 2);
+        assertEquals(ois.get(0).packageName, APP_OVERLAY_ONE_PKG);
+        assertEquals(ois.get(1).packageName, APP_OVERLAY_TWO_PKG);
+
+        OverlayManagerTransaction t2 = new OverlayManagerTransaction.Builder()
+                .setEnabled(APP_OVERLAY_TWO_PKG, true)
+                .setEnabled(APP_OVERLAY_ONE_PKG, true)
+                .build();
+        mOverlayManager.commit(t2);
+
+        assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
+        assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, true, mUserId);
+        List<OverlayInfo> ois2 =
+                mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
+        assertEquals(ois2.size(), 2);
+        assertEquals(ois2.get(0).packageName, APP_OVERLAY_TWO_PKG);
+        assertEquals(ois2.get(1).packageName, APP_OVERLAY_ONE_PKG);
+
+        OverlayManagerTransaction t3 = new OverlayManagerTransaction.Builder()
+                .setEnabled(APP_OVERLAY_TWO_PKG, false)
+                .build();
+        mOverlayManager.commit(t3);
+
+        assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
+        assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
+        List<OverlayInfo> ois3 =
+                mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
+        assertEquals(ois3.size(), 2);
+        assertEquals(ois3.get(0).packageName, APP_OVERLAY_TWO_PKG);
+        assertEquals(ois3.get(1).packageName, APP_OVERLAY_ONE_PKG);
+    }
+
+    @Test
+    public void testInvalidRequestHasNoEffect() {
+        assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
+        assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
+
+        OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
+                .setEnabled(APP_OVERLAY_ONE_PKG, true)
+                .setEnabled("does-not-exist", true)
+                .setEnabled(APP_OVERLAY_TWO_PKG, true)
+                .build();
+        assertThrows(SecurityException.class, () -> mOverlayManager.commit(t));
+
+        assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
+        assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
+    }
+
+    private void assertOverlayIsEnabled(final String packageName, boolean enabled, int userId) {
+        final OverlayInfo oi = mOverlayManager.getOverlayInfo(packageName, UserHandle.of(userId));
+        assertNotNull(oi);
+        assertEquals(oi.isEnabled(), enabled);
+    }
+}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
index d28c47d..420f755 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
@@ -22,8 +22,6 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.util.concurrent.Executor;
-
 @RunWith(JUnit4.class)
 @MediumTest
 public class WithMultipleOverlaysTest extends OverlayBaseTest {
@@ -33,9 +31,8 @@
 
     @BeforeClass
     public static void enableOverlay() throws Exception {
-        Executor executor = (cmd) -> new Thread(cmd).start();
-        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
-        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, true);
-        LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
+        LocalOverlayManager.toggleOverlaysAndWait(
+                new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG},
+                new String[]{});
     }
 }
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
index 6566ad3..a86255e 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
@@ -22,8 +22,6 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.util.concurrent.Executor;
-
 @RunWith(JUnit4.class)
 @MediumTest
 public class WithOverlayTest extends OverlayBaseTest {
@@ -32,10 +30,9 @@
     }
 
     @BeforeClass
-    public static void enableOverlay() throws Exception {
-        Executor executor = (cmd) -> new Thread(cmd).start();
-        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
-        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
-        LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
+    public static void enableOverlays() throws Exception {
+        LocalOverlayManager.toggleOverlaysAndWait(
+                new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG},
+                new String[]{APP_OVERLAY_TWO_PKG});
     }
 }
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
index 48cfeab..51c4118 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
@@ -22,8 +22,6 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.util.concurrent.Executor;
-
 @RunWith(JUnit4.class)
 @MediumTest
 public class WithoutOverlayTest extends OverlayBaseTest {
@@ -33,9 +31,8 @@
 
     @BeforeClass
     public static void disableOverlays() throws Exception {
-        Executor executor = (cmd) -> new Thread(cmd).start();
-        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, false);
-        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
-        LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, false);
+        LocalOverlayManager.toggleOverlaysAndWait(
+                new String[]{},
+                new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
     }
 }
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
index da3aa00..847b491 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
@@ -15,6 +15,6 @@
 android_test {
     name: "OverlayDeviceTests_AppOverlayOne",
     sdk_version: "current",
-
+    certificate: "platform",
     aaptflags: ["--no-resource-removal"],
 }
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
index 215b66da3..7d5f82a 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
@@ -15,6 +15,6 @@
 android_test {
     name: "OverlayDeviceTests_AppOverlayTwo",
     sdk_version: "current",
-
+    certificate: "platform",
     aaptflags: ["--no-resource-removal"],
 }
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index 7f20b3b..41a6fea 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -122,13 +122,6 @@
 }
 
 prebuilt_etc {
-    name: "allowed_privapp_com.android.car.companiondevicesupport",
-    sub_dir: "permissions",
-    src: "com.android.car.companiondevicesupport.xml",
-    filename_from_src: true,
-}
-
-prebuilt_etc {
     name: "allowed_privapp_com.google.android.car.kitchensink",
     sub_dir: "permissions",
     src: "com.google.android.car.kitchensink.xml",
diff --git a/data/etc/car/com.android.car.companiondevicesupport.xml b/data/etc/car/com.android.car.companiondevicesupport.xml
deleted file mode 100644
index 2067bab..0000000
--- a/data/etc/car/com.android.car.companiondevicesupport.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<permissions>
-    <privapp-permissions package="com.android.car.companiondevicesupport">
-      <permission name="android.permission.INTERACT_ACROSS_USERS"/>
-      <permission name="android.permission.MANAGE_USERS"/>
-      <permission name="android.permission.PROVIDE_TRUST_AGENT"/>
-      <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
-    </privapp-permissions>
-</permissions>
diff --git a/data/etc/com.android.launcher3.xml b/data/etc/com.android.launcher3.xml
index 17d614e..99c38db 100644
--- a/data/etc/com.android.launcher3.xml
+++ b/data/etc/com.android.launcher3.xml
@@ -20,5 +20,6 @@
         <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
         <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+        <permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
     </privapp-permissions>
 </permissions>
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index 6255584..ff8d96d 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -17,9 +17,8 @@
 <!--
 This XML file declares which system packages should be initially installed for new users based on
 their user type. All system packages on the device should ideally have an entry in an xml file
-(keyed by its manifest name), except auto-generated rro packages. Auto-generated RRO packages 
-(package name ends with ".auto_generated_rro_product__" or ".auto_generated_rro_vendor__")
-will be installed for new users according to corresponding overlay target packages.
+(keyed by its manifest name), except for static overlays which are instead treated automatically
+according to the entry for their corresponding overlay target package.
 
 Base user-types (every user will be at least one of these types) are:
   SYSTEM    (user 0)
diff --git a/graphics/java/android/graphics/fonts/FontFileUtil.java b/graphics/java/android/graphics/fonts/FontFileUtil.java
index 2896c46..af49619 100644
--- a/graphics/java/android/graphics/fonts/FontFileUtil.java
+++ b/graphics/java/android/graphics/fonts/FontFileUtil.java
@@ -166,6 +166,23 @@
         return nGetFontPostScriptName(buffer, index);
     }
 
+    /**
+     * Analyze name OpenType table and return true if the font has PostScript Type 1 glyphs.
+     *
+     * IllegalArgumentException will be thrown for invalid font data.
+     * -1 will be returned if the byte buffer is not a OpenType font data.
+     * 0 will be returned if the font file doesn't have PostScript Type 1 glyphs, i.e. ttf file.
+     * 1 will be returned if the font file has PostScript Type 1 glyphs, i.e. otf file.
+     *
+     * @param buffer a buffer of OpenType font
+     * @param index a font index
+     * @return a post script name or null if it is invalid or not found.
+     */
+    public static int isPostScriptType1Font(@NonNull ByteBuffer buffer,
+            @IntRange(from = 0) int index) {
+        return nIsPostScriptType1Font(buffer, index);
+    }
+
     @FastNative
     private static native long nGetFontRevision(@NonNull ByteBuffer buffer,
             @IntRange(from = 0) int index);
@@ -173,4 +190,8 @@
     @FastNative
     private static native String nGetFontPostScriptName(@NonNull ByteBuffer buffer,
             @IntRange(from = 0) int index);
+
+    @FastNative
+    private static native int nIsPostScriptType1Font(@NonNull ByteBuffer buffer,
+            @IntRange(from = 0) int index);
 }
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index 54167b4..6aa50db 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -50,9 +50,11 @@
     private static final String DEFAULT_FAMILY = "sans-serif";
 
     private static final String FONTS_XML = "/system/etc/fonts.xml";
-    private static final String SYSTEM_FONT_DIR = "/system/fonts/";
+    /** @hide */
+    public static final String SYSTEM_FONT_DIR = "/system/fonts/";
     private static final String OEM_XML = "/product/etc/fonts_customization.xml";
-    private static final String OEM_FONT_DIR = "/product/fonts/";
+    /** @hide */
+    public static final String OEM_FONT_DIR = "/product/fonts/";
 
     private SystemFonts() {}  // Do not instansiate.
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
index b43203d..0958a07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
@@ -100,7 +100,7 @@
         mSplitScreenOptional.ifPresent(SplitScreen::onOrganizerRegistered);
 
         // Bind the splitscreen impl to the drag drop controller
-        mDragAndDropController.initialize(mLegacySplitScreenOptional);
+        mDragAndDropController.initialize(mSplitScreenOptional);
 
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
             mTransitions.register(mShellTaskOrganizer);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index e17a943..c27c929 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -135,7 +135,7 @@
 
                 final int position = mSplitLayout.getDividePosition() + touchPos - mStartPos;
                 final DividerSnapAlgorithm.SnapTarget snapTarget =
-                        mSplitLayout.findSnapTarget(position, velocity);
+                        mSplitLayout.findSnapTarget(position, velocity, false /* hardDismiss */);
                 mSplitLayout.snapToTarget(position, snapTarget);
                 break;
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index d77def5..60231df 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -188,11 +188,11 @@
     public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
         switch (snapTarget.flag) {
             case FLAG_DISMISS_START:
-                mLayoutChangeListener.onSnappedToDismiss(false /* snappedToEnd */);
+                mLayoutChangeListener.onSnappedToDismiss(false /* bottomOrRight */);
                 mSplitWindowManager.setResizingSplits(false);
                 break;
             case FLAG_DISMISS_END:
-                mLayoutChangeListener.onSnappedToDismiss(true /* snappedToEnd */);
+                mLayoutChangeListener.onSnappedToDismiss(true /* bottomOrRight */);
                 mSplitWindowManager.setResizingSplits(false);
                 break;
             default:
@@ -207,9 +207,11 @@
 
     /**
      * Returns {@link DividerSnapAlgorithm.SnapTarget} which matches passing position and velocity.
+     * If hardDismiss is set to {@code true}, it will be harder to reach dismiss target.
      */
-    public DividerSnapAlgorithm.SnapTarget findSnapTarget(int position, float velocity) {
-        return mDividerSnapAlgorithm.calculateSnapTarget(position, velocity);
+    public DividerSnapAlgorithm.SnapTarget findSnapTarget(int position, float velocity,
+            boolean hardDismiss) {
+        return mDividerSnapAlgorithm.calculateSnapTarget(position, velocity, hardDismiss);
     }
 
     private DividerSnapAlgorithm getSnapAlgorithm(Resources resources, Rect rootBounds) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index c8b4e10..c8938ad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -52,7 +52,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.util.Optional;
 
@@ -66,7 +66,7 @@
 
     private final Context mContext;
     private final DisplayController mDisplayController;
-    private LegacySplitScreen mLegacySplitScreen;
+    private SplitScreen mSplitScreen;
 
     private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>();
     private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
@@ -76,8 +76,8 @@
         mDisplayController = displayController;
     }
 
-    public void initialize(Optional<LegacySplitScreen> splitscreen) {
-        mLegacySplitScreen = splitscreen.orElse(null);
+    public void initialize(Optional<SplitScreen> splitscreen) {
+        mSplitScreen = splitscreen.orElse(null);
         mDisplayController.addDisplayWindowListener(this);
     }
 
@@ -104,7 +104,7 @@
                 R.layout.global_drop_target, null);
         rootView.setOnDragListener(this);
         rootView.setVisibility(View.INVISIBLE);
-        DragLayout dragLayout = new DragLayout(context, mLegacySplitScreen);
+        DragLayout dragLayout = new DragLayout(context, mSplitScreen);
         rootView.addView(dragLayout,
                 new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
         try {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 4043d0b..800150c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -34,6 +34,8 @@
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
+import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_TOP_OR_LEFT;
 
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
@@ -59,13 +61,12 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.function.Consumer;
 
 /**
  * The policy for handling drag and drop operations to shell.
@@ -77,22 +78,22 @@
     private final Context mContext;
     private final ActivityTaskManager mActivityTaskManager;
     private final Starter mStarter;
-    private final LegacySplitScreen mLegacySplitScreen;
+    private final SplitScreen mSplitScreen;
     private final ArrayList<DragAndDropPolicy.Target> mTargets = new ArrayList<>();
 
     private DragSession mSession;
 
-    public DragAndDropPolicy(Context context, LegacySplitScreen legacySplitScreen) {
-        this(context, ActivityTaskManager.getInstance(), legacySplitScreen,
-                new DefaultStarter(context, legacySplitScreen));
+    public DragAndDropPolicy(Context context, SplitScreen splitScreen) {
+        this(context, ActivityTaskManager.getInstance(), splitScreen,
+                new DefaultStarter(context, splitScreen));
     }
 
     @VisibleForTesting
     DragAndDropPolicy(Context context, ActivityTaskManager activityTaskManager,
-            LegacySplitScreen legacySplitScreen, Starter starter) {
+            SplitScreen splitScreen, Starter starter) {
         mContext = context;
         mActivityTaskManager = activityTaskManager;
-        mLegacySplitScreen = legacySplitScreen;
+        mSplitScreen = splitScreen;
         mStarter = starter;
     }
 
@@ -122,64 +123,54 @@
         final int ih = h - insets.top - insets.bottom;
         final int l = insets.left;
         final int t = insets.top;
-        final boolean isVerticalSplit = mSession.isPhone && !mSession.displayLayout.isLandscape();
-        if (mSession.dragItemSupportsSplitscreen
-                && mSession.runningTaskActType == ACTIVITY_TYPE_STANDARD
-                && mSession.runningTaskWinMode == WINDOWING_MODE_FULLSCREEN
-                && mSession.runningTaskIsResizeable) {
-            // Allow splitting when there is a fullscreen standard activity running
-            if (isVerticalSplit) {
-                // TODO(b/169894807): For now, only allow splitting to the right/bottom until we
-                //                    have split pairs
-                mTargets.add(new Target(TYPE_FULLSCREEN,
-                        new Rect(l, t, l + iw, t + ih / 2),
-                        new Rect(l, t, l + iw, t + ih),
-                        new Rect(0, 0, w, h)));
-                mTargets.add(new Target(TYPE_SPLIT_BOTTOM,
-                        new Rect(l, t + ih / 2, l + iw, t + ih),
-                        new Rect(l, t + ih / 2, l + iw, t + ih),
-                        new Rect(0, h / 2, w, h)));
-            } else {
-                mTargets.add(new Target(TYPE_FULLSCREEN,
-                        new Rect(l, t, l + iw / 2, t + ih),
-                        new Rect(l, t, l + iw, t + ih),
-                        new Rect(0, 0, w, h)));
-                mTargets.add(new Target(TYPE_SPLIT_RIGHT,
-                        new Rect(l + iw / 2, t, l + iw, t + ih),
-                        new Rect(l + iw / 2, t, l + iw, t + ih),
-                        new Rect(w / 2, 0, w, h)));
-            }
-        } else if (mSession.dragItemSupportsSplitscreen
-                && mLegacySplitScreen != null
-                && mLegacySplitScreen.isDividerVisible()) {
+        final Rect displayRegion = new Rect(l, t, l + iw, t + ih);
+        final Rect fullscreenDrawRegion = new Rect(displayRegion);
+        final Rect fullscreenHitRegion = new Rect(displayRegion);
+        final boolean inLandscape = mSession.displayLayout.isLandscape();
+        final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
+        // We allow splitting if we are already in split-screen or the running task is a standard
+        // task in fullscreen mode.
+        final boolean allowSplit = inSplitScreen
+                || (mSession.runningTaskActType == ACTIVITY_TYPE_STANDARD
+                        && mSession.runningTaskWinMode == WINDOWING_MODE_FULLSCREEN);
+        if (allowSplit) {
             // Already split, allow replacing existing split task
-            // TODO(b/169894807): For now, only allow replacing the non-primary task until we have
-            //                    split pairs
-            final Rect secondarySplitRawBounds =
-                    mLegacySplitScreen.getDividerView().getNonMinimizedSplitScreenSecondaryBounds();
-            final Rect secondarySplitBounds = new Rect(secondarySplitRawBounds);
-            secondarySplitBounds.intersect(new Rect(l, t, l + iw, t + ih));
-            if (isVerticalSplit) {
-                mTargets.add(new Target(TYPE_FULLSCREEN,
-                        new Rect(l, t, l + iw, secondarySplitRawBounds.top),
-                        new Rect(l, t, l + iw, t + ih),
-                        new Rect(0, 0, w, secondarySplitRawBounds.top)));
+            final Rect topOrLeftBounds = new Rect();
+            final Rect bottomOrRightBounds = new Rect();
+            mSplitScreen.getStageBounds(topOrLeftBounds, bottomOrRightBounds);
+            topOrLeftBounds.intersect(displayRegion);
+            bottomOrRightBounds.intersect(displayRegion);
+
+            if (inLandscape) {
+                final Rect leftHitRegion = new Rect();
+                final Rect leftDrawRegion = topOrLeftBounds;
+                final Rect rightHitRegion = new Rect();
+                final Rect rightDrawRegion = bottomOrRightBounds;
+
+                displayRegion.splitVertically(leftHitRegion, fullscreenHitRegion, rightHitRegion);
+
+                mTargets.add(
+                        new Target(TYPE_FULLSCREEN, fullscreenHitRegion, fullscreenDrawRegion));
+                mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, leftDrawRegion));
+                mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, rightDrawRegion));
+
             } else {
-                mTargets.add(new Target(TYPE_FULLSCREEN,
-                        new Rect(l, t, secondarySplitRawBounds.left, t + ih),
-                        new Rect(l, t, l + iw, t + ih),
-                        new Rect(0, 0, w, h)));
+                final Rect topHitRegion = new Rect();
+                final Rect topDrawRegion = topOrLeftBounds;
+                final Rect bottomHitRegion = new Rect();
+                final Rect bottomDrawRegion = bottomOrRightBounds;
+
+                displayRegion.splitHorizontally(
+                        topHitRegion, fullscreenHitRegion, bottomHitRegion);
+
+                mTargets.add(
+                        new Target(TYPE_FULLSCREEN, fullscreenHitRegion, fullscreenDrawRegion));
+                mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topDrawRegion));
+                mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomDrawRegion));
             }
-            mTargets.add(new Target(isVerticalSplit ? TYPE_SPLIT_BOTTOM : TYPE_SPLIT_RIGHT,
-                    new Rect(secondarySplitBounds),
-                    new Rect(secondarySplitBounds),
-                    new Rect(secondarySplitBounds)));
         } else {
-            // Otherwise only show the fullscreen target
-            mTargets.add(new Target(TYPE_FULLSCREEN,
-                    new Rect(l, t, l + iw, t + ih),
-                    new Rect(l, t, l + iw, t + ih),
-                    new Rect(0, 0, w, h)));
+            // Split-screen not allowed, so only show the fullscreen target
+            mTargets.add(new Target(TYPE_FULLSCREEN, fullscreenHitRegion, fullscreenDrawRegion));
         }
         return mTargets;
     }
@@ -208,58 +199,34 @@
         final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
         final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
         final Intent dragData = mSession.dragData;
+        final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
+        final boolean leftOrTop = target.type == TYPE_SPLIT_TOP || target.type == TYPE_SPLIT_LEFT;
+        final Bundle opts = dragData.hasExtra(EXTRA_ACTIVITY_OPTIONS)
+                ? dragData.getBundleExtra(EXTRA_ACTIVITY_OPTIONS)
+                : new Bundle();
 
-        boolean deferAppLaunchUntilSplit = false;
         if (target.type == TYPE_FULLSCREEN) {
-            if (mLegacySplitScreen != null && mLegacySplitScreen.isDividerVisible()) {
-                // If in split, remove split and launch fullscreen
-                mStarter.exitSplitScreen(mSession.runningTaskId);
-            } else {
-                // Not in split, fall through to launch
+            // Exit split stages if needed
+            mStarter.exitSplitScreen();
+        } else if (mSplitScreen != null) {
+            // Update launch options for the split side we are targeting.
+            final int position = leftOrTop
+                    ? SIDE_STAGE_POSITION_TOP_OR_LEFT : SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+            if (!inSplitScreen) {
+                // Update the side stage position to match where we want to launch.
+                mSplitScreen.setSideStagePosition(position);
             }
-        } else {
-            if (mLegacySplitScreen != null && mLegacySplitScreen.isDividerVisible()) {
-                // Split is already visible, just replace the task
-                // TODO(b/169894807): Since we only allow replacing the non-primary target above
-                //                    just fall through and start the activity
-            } else {
-                // Not in split, enter split now
-                mStarter.enterSplitScreen(mSession.runningTaskId,
-                        target.type == TYPE_SPLIT_LEFT || target.type == TYPE_SPLIT_TOP);
-                deferAppLaunchUntilSplit = true;
-            }
+            mSplitScreen.updateActivityOptions(opts, position);
         }
 
-        final Runnable startAppRunnable = () -> {
-            Bundle opts = dragData.hasExtra(EXTRA_ACTIVITY_OPTIONS)
-                    ? dragData.getBundleExtra(EXTRA_ACTIVITY_OPTIONS)
-                    : null;
-            if (isTask) {
-                mStarter.startTask(dragData.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID), opts);
-            } else if (isShortcut) {
-                mStarter.startShortcut(dragData.getStringExtra(EXTRA_PACKAGE_NAME),
-                        dragData.getStringExtra(EXTRA_SHORTCUT_ID),
-                        opts, dragData.getParcelableExtra(EXTRA_USER));
-            } else {
-                mStarter.startIntent(dragData.getParcelableExtra(EXTRA_PENDING_INTENT), opts);
-            }
-        };
-        if (deferAppLaunchUntilSplit) {
-            // TODO(b/169894807): The enterSplitScreen() call above will trigger the current task
-            // into split, and we should wait for home and other tasks to be moved to
-            // split-secondary before trying to launch the new secondary task.  This can be removed
-            // once we have app-pairs.
-            mLegacySplitScreen.registerInSplitScreenListener(new Consumer<Boolean>() {
-                @Override
-                public void accept(Boolean inSplit) {
-                    if (inSplit) {
-                        startAppRunnable.run();
-                        mLegacySplitScreen.unregisterInSplitScreenListener(this);
-                    }
-                }
-            });
+        if (isTask) {
+            mStarter.startTask(dragData.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID), opts);
+        } else if (isShortcut) {
+            mStarter.startShortcut(dragData.getStringExtra(EXTRA_PACKAGE_NAME),
+                    dragData.getStringExtra(EXTRA_SHORTCUT_ID),
+                    opts, dragData.getParcelableExtra(EXTRA_USER));
         } else {
-            startAppRunnable.run();
+            mStarter.startIntent(dragData.getParcelableExtra(EXTRA_PENDING_INTENT), opts);
         }
     }
 
@@ -323,7 +290,7 @@
                 UserHandle user);
         void startIntent(PendingIntent intent, Bundle activityOptions);
         void enterSplitScreen(int taskId, boolean leftOrTop);
-        void exitSplitScreen(int taskId);
+        void exitSplitScreen();
     }
 
     /**
@@ -332,17 +299,17 @@
      */
     private static class DefaultStarter implements Starter {
         private final Context mContext;
-        private final LegacySplitScreen mLegacySplitScreen;
+        private final SplitScreen mSplitScreen;
 
-        public DefaultStarter(Context context, LegacySplitScreen legacySplitScreen) {
+        public DefaultStarter(Context context, SplitScreen splitScreen) {
             mContext = context;
-            mLegacySplitScreen = legacySplitScreen;
+            mSplitScreen = splitScreen;
         }
 
         @Override
         public void startTask(int taskId, Bundle activityOptions) {
             try {
-                ActivityTaskManager.getService().startActivityFromRecents(taskId, null);
+                ActivityTaskManager.getService().startActivityFromRecents(taskId, activityOptions);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to launch task", e);
             }
@@ -372,12 +339,14 @@
 
         @Override
         public void enterSplitScreen(int taskId, boolean leftOrTop) {
-            mLegacySplitScreen.splitPrimaryTask();
+            mSplitScreen.moveToSideStage(taskId,
+                    leftOrTop ? SIDE_STAGE_POSITION_TOP_OR_LEFT
+                            : SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT);
         }
 
         @Override
-        public void exitSplitScreen(int taskId) {
-            mLegacySplitScreen.dismissSplitToPrimaryTask();
+        public void exitSplitScreen() {
+            mSplitScreen.exitSplitScreen();
         }
     }
 
@@ -406,19 +375,16 @@
         final Rect hitRegion;
         // The approximate visual region for where the task will start
         final Rect drawRegion;
-        // The
-        final Rect dropTargetBounds;
 
-        public Target(@Type int t, Rect hit, Rect draw, Rect drop) {
+        public Target(@Type int t, Rect hit, Rect draw) {
             type = t;
             hitRegion = hit;
             drawRegion = draw;
-            dropTargetBounds = drop;
         }
 
         @Override
         public String toString() {
-            return "Target {hit=" + hitRegion + " drop=" + dropTargetBounds + "}";
+            return "Target {hit=" + hitRegion + " draw=" + drawRegion + "}";
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index a56fe8d..82c4e44 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -42,7 +42,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.util.ArrayList;
 
@@ -61,7 +61,7 @@
     private boolean mIsShowing;
     private boolean mHasDropped;
 
-    public DragLayout(Context context, LegacySplitScreen splitscreen) {
+    public DragLayout(Context context, SplitScreen splitscreen) {
         super(context);
         mPolicy = new DragAndDropPolicy(context, splitscreen);
         mDisplayMargin = context.getResources().getDimensionPixelSize(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
index 552eba4..7f98965 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
@@ -38,12 +38,6 @@
 
     private boolean mIsActive = false;
 
-    private static final int[] CONTROLLED_ACTIVITY_TYPES = {ACTIVITY_TYPE_STANDARD};
-    private static final int[] CONTROLLED_WINDOWING_MODES =
-            {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED};
-    private static final int[] CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE =
-            {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED, WINDOWING_MODE_MULTI_WINDOW};
-
     MainStage(ShellTaskOrganizer taskOrganizer, int displayId,
             StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue) {
         super(taskOrganizer, displayId, callbacks, syncQueue);
@@ -92,7 +86,7 @@
                         null /* newParent */,
                         CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE,
                         CONTROLLED_ACTIVITY_TYPES,
-                        true /* onTop */)
+                        false /* onTop */)
                 .reorder(rootToken, false /* onTop */);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index 5645c19..cd44e4b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.splitscreen;
 
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
 import android.app.ActivityManager;
 import android.graphics.Rect;
 import android.window.WindowContainerToken;
@@ -48,6 +50,17 @@
                 .reorder(rootToken, true);
     }
 
+    boolean removeAllTasks(WindowContainerTransaction wct) {
+        if (mChildrenTaskInfo.size() == 0) return false;
+        wct.reparentTasks(
+                mRootTaskInfo.token,
+                null /* newParent */,
+                CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE,
+                CONTROLLED_ACTIVITY_TYPES,
+                false /* onTop */);
+        return true;
+    }
+
     boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) {
         final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId);
         if (task == null) return false;
@@ -57,4 +70,12 @@
                 .reparent(task.token, newParent, false /* onTop */);
         return true;
     }
+
+    int getTopVisibleTaskId() {
+        for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
+            final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.valueAt(i);
+            if (task.isVisible) return task.taskId;
+        }
+        return INVALID_TASK_ID;
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 08c2856..7c1b9d8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -18,6 +18,8 @@
 
 import android.annotation.IntDef;
 import android.app.ActivityManager;
+import android.graphics.Rect;
+import android.os.Bundle;
 
 import androidx.annotation.NonNull;
 
@@ -61,6 +63,12 @@
     void setSideStagePosition(@SideStagePosition int sideStagePosition);
     /** Hides the side-stage if it is currently visible. */
     void setSideStageVisibility(boolean visible);
+    /** Removes the split-screen stages. */
+    void exitSplitScreen();
+    /** Gets the stage bounds. */
+    void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds);
+    /** Updates the launch activity options for the split position we want to launch it in. */
+    void updateActivityOptions(Bundle opts, @SideStagePosition int position);
     /** Dumps current status of split-screen. */
     void dump(@NonNull PrintWriter pw, String prefix);
     /** Called when the shell organizer has been registered. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 55cfea5..27d3b81 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -20,6 +20,8 @@
 
 import android.app.ActivityManager;
 import android.content.Context;
+import android.graphics.Rect;
+import android.os.Bundle;
 
 import androidx.annotation.NonNull;
 
@@ -69,7 +71,10 @@
     @Override
     public boolean moveToSideStage(int taskId, @SideStagePosition int sideStagePosition) {
         final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
-        return task != null && moveToSideStage(task, sideStagePosition);
+        if (task == null) {
+            throw new IllegalArgumentException("Unknown taskId" + taskId);
+        }
+        return moveToSideStage(task, sideStagePosition);
     }
 
     @Override
@@ -94,6 +99,21 @@
     }
 
     @Override
+    public void exitSplitScreen() {
+        mStageCoordinator.exitSplitScreen();
+    }
+
+    @Override
+    public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
+        mStageCoordinator.getStageBounds(outTopOrLeftBounds, outBottomOrRightBounds);
+    }
+
+    @Override
+    public void updateActivityOptions(Bundle opts, @SideStagePosition int position) {
+        mStageCoordinator.updateActivityOptions(opts, position);
+    }
+
+    @Override
     public void dump(@NonNull PrintWriter pw, String prefix) {
         pw.println(prefix + TAG);
         if (mStageCoordinator != null) {
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 5c1d18e..dd41957 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
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.splitscreen;
 
+import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 
@@ -25,6 +27,7 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.view.SurfaceControl;
 import android.window.DisplayAreaInfo;
 import android.window.WindowContainerTransaction;
@@ -142,6 +145,31 @@
         mTaskOrganizer.applyTransaction(wct);
     }
 
+    void exitSplitScreen() {
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        mSideStage.removeAllTasks(wct);
+        mMainStage.deactivate(wct);
+        mTaskOrganizer.applyTransaction(wct);
+    }
+
+    void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
+        outTopOrLeftBounds.set(mSplitLayout.getBounds1());
+        outBottomOrRightBounds.set(mSplitLayout.getBounds2());
+    }
+
+    void updateActivityOptions(Bundle opts, @SplitScreen.SideStagePosition int position) {
+        final StageTaskListener stage = position == mSideStagePosition ? mSideStage : mMainStage;
+        opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
+
+        if (!mMainStage.isActive()) {
+            // Activate the main stage in anticipation of an app launch.
+            final WindowContainerTransaction wct = new WindowContainerTransaction();
+            mMainStage.activate(getMainStageBounds(), wct);
+            mSideStage.setBounds(getSideStageBounds(), wct);
+            mTaskOrganizer.applyTransaction(wct);
+        }
+    }
+
     private void onStageRootTaskAppeared(StageListenerImpl stageListener) {
         if (mMainStageListener.mHasRootTask && mSideStageListener.mHasRootTask) {
             final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -175,6 +203,12 @@
             }
         }
 
+        if (!mainStageVisible && !sideStageVisible) {
+            // Exit split-screen if both stage are not visible.
+            // TODO: This is only a temporary request from UX and is likely to be removed soon...
+            exitSplitScreen();
+        }
+
         if (mainStageVisible) {
             final WindowContainerTransaction wct = new WindowContainerTransaction();
             if (sideStageVisible) {
@@ -252,10 +286,27 @@
     }
 
     @Override
-    public void onSnappedToDismiss(boolean snappedToEnd) {
-        // TODO: What to do...what to do...
+    public void onSnappedToDismiss(boolean bottomOrRight) {
+        if (mSideStagePosition == SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT && bottomOrRight) {
+            // Main stage was fully expanded...Just side side-stage.
+            setSideStageVisibility(false);
+        } else {
+            // Side stage was fully expanded...Move its top task to the main stage
+            // and hide side-stage.
+            // TODO: Would UX prefer the side-stage go into fullscreen mode here?
+            final int taskId = mSideStage.getTopVisibleTaskId();
+            if (taskId == INVALID_TASK_ID) {
+                throw new IllegalStateException("Side stage doesn't have visible task? "
+                        + mSideStage);
+            }
+            final WindowContainerTransaction wct = new WindowContainerTransaction();
+            mSideStage.removeTask(taskId, mMainStage.mRootTaskInfo.getToken(), wct);
+            mSideStage.setVisibility(false, wct);
+            mTaskOrganizer.applyTransaction(wct);
+        }
+
+        // Reset divider position.
         mSplitLayout.resetDividerPosition();
-        onBoundsChanged(mSplitLayout);
     }
 
     @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 efd42ce..1aa7552 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
@@ -16,7 +16,10 @@
 
 package com.android.wm.shell.splitscreen;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
 import android.annotation.CallSuper;
 import android.app.ActivityManager;
@@ -45,6 +48,12 @@
 class StageTaskListener implements ShellTaskOrganizer.TaskListener {
     private static final String TAG = StageTaskListener.class.getSimpleName();
 
+    protected static final int[] CONTROLLED_ACTIVITY_TYPES = {ACTIVITY_TYPE_STANDARD};
+    protected static final int[] CONTROLLED_WINDOWING_MODES =
+            {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED};
+    protected static final int[] CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE =
+            {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED, WINDOWING_MODE_MULTI_WINDOW};
+
     /** Callback interface for listening to changes in a split-screen stage. */
     public interface StageListenerCallbacks {
         void onRootTaskAppeared();
@@ -69,7 +78,7 @@
     @Override
     @CallSuper
     public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
-        if (!taskInfo.hasParentTask()) {
+        if (mRootTaskInfo == null && !taskInfo.hasParentTask()) {
             mRootLeash = leash;
             mRootTaskInfo = taskInfo;
             mCallbacks.onRootTaskAppeared();
@@ -78,7 +87,8 @@
             mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
             updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */);
         } else {
-            throw new IllegalArgumentException("Unknown task: " + taskInfo);
+            throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+                    + "\n mRootTaskInfo: " + mRootTaskInfo);
         }
         sendStatusChanged();
     }
@@ -93,7 +103,8 @@
             updateChildTaskSurface(
                     taskInfo, mChildrenLeashes.get(taskInfo.taskId), false /* firstAppeared */);
         } else {
-            throw new IllegalArgumentException("Unknown task: " + taskInfo);
+            throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+                    + "\n mRootTaskInfo: " + mRootTaskInfo);
         }
         sendStatusChanged();
     }
@@ -110,7 +121,8 @@
             mChildrenLeashes.remove(taskId);
             sendStatusChanged();
         } else {
-            throw new IllegalArgumentException("Unknown task: " + taskInfo);
+            throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+                    + "\n mRootTaskInfo: " + mRootTaskInfo);
         }
     }
 
@@ -143,6 +155,8 @@
     @Override
     @CallSuper
     public void dump(@NonNull PrintWriter pw, String prefix) {
-
+        final String innerPrefix = prefix + "  ";
+        final String childPrefix = innerPrefix + "  ";
+        pw.println(prefix + this);
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 912418d..79bdaf4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -26,7 +26,9 @@
 
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_FULLSCREEN;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM;
+import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
+import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
 
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
@@ -35,7 +37,6 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
@@ -52,7 +53,6 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Insets;
-import android.graphics.Rect;
 import android.os.RemoteException;
 import android.view.DisplayInfo;
 
@@ -61,20 +61,17 @@
 
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.draganddrop.DragAndDropPolicy.Target;
-import com.android.wm.shell.legacysplitscreen.DividerView;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
-import java.util.function.Consumer;
 
 /**
  * Tests for the drag and drop policy.
@@ -90,12 +87,13 @@
     private ActivityTaskManager mActivityTaskManager;
 
     @Mock
-    private LegacySplitScreen mLegacySplitScreen;
+    private SplitScreen mSplitScreen;
 
     @Mock
     private DragAndDropPolicy.Starter mStarter;
 
-    private DisplayLayout mDisplayLayout;
+    private DisplayLayout mLandscapeDisplayLayout;
+    private DisplayLayout mPortraitDisplayLayout;
     private Insets mInsets;
     private DragAndDropPolicy mPolicy;
 
@@ -116,25 +114,19 @@
         Resources res = mock(Resources.class);
         Configuration config = new Configuration();
         doReturn(config).when(res).getConfiguration();
+        doReturn(res).when(mContext).getResources();
         DisplayInfo info = new DisplayInfo();
-        info.logicalWidth = 100;
+        info.logicalWidth = 200;
         info.logicalHeight = 100;
-        mDisplayLayout = new DisplayLayout(info, res, false, false);
+        mLandscapeDisplayLayout = new DisplayLayout(info, res, false, false);
+        DisplayInfo info2 = new DisplayInfo();
+        info.logicalWidth = 100;
+        info.logicalHeight = 200;
+        mPortraitDisplayLayout = new DisplayLayout(info2, res, false, false);
         mInsets = Insets.of(0, 0, 0, 0);
 
-        DividerView divider = mock(DividerView.class);
-        doReturn(divider).when(mLegacySplitScreen).getDividerView();
-        doReturn(new Rect(50, 0, 100, 100)).when(divider)
-                .getNonMinimizedSplitScreenSecondaryBounds();
-
-        doAnswer((Answer<Void>) invocation -> {
-            Consumer<Boolean> callback = invocation.getArgument(0);
-            callback.accept(true);
-            return null;
-        }).when(mLegacySplitScreen).registerInSplitScreenListener(any());
-
         mPolicy = new DragAndDropPolicy(
-                mContext, mActivityTaskManager, mLegacySplitScreen, mStarter);
+                mContext, mActivityTaskManager, mSplitScreen, mStarter);
         mActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
         mNonResizeableActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
         setClipDataResizeable(mNonResizeableActivityClipData, false);
@@ -149,7 +141,6 @@
         mSplitPrimaryAppTask = createTaskInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
                 ACTIVITY_TYPE_STANDARD);
 
-        setIsPhone(false);
         setInSplitScreen(false);
         setRunningTask(mFullscreenAppTask);
     }
@@ -199,22 +190,14 @@
                 : ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
     }
 
-    private void setIsPhone(boolean isPhone) {
-        Resources res = mock(Resources.class);
-        Configuration config = mock(Configuration.class);
-        config.smallestScreenWidthDp = isPhone ? 400 : 800;
-        doReturn(config).when(res).getConfiguration();
-        doReturn(res).when(mContext).getResources();
-    }
-
     private void setInSplitScreen(boolean inSplitscreen) {
-        doReturn(inSplitscreen).when(mLegacySplitScreen).isDividerVisible();
+        doReturn(inSplitscreen).when(mSplitScreen).isSplitScreenVisible();
     }
 
     @Test
-    public void testDragAppOverFullscreenHome_expectOnlyFullscreenTarget() throws RemoteException {
+    public void testDragAppOverFullscreenHome_expectOnlyFullscreenTarget() {
         setRunningTask(mHomeTask);
-        mPolicy.start(mDisplayLayout, mActivityClipData);
+        mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
         ArrayList<Target> targets = assertExactTargetTypes(
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
 
@@ -223,77 +206,66 @@
     }
 
     @Test
-    public void testDragAppOverFullscreenApp_expectSplitScreenAndFullscreenTargets()
-            throws RemoteException {
+    public void testDragAppOverFullscreenApp_expectSplitScreenAndFullscreenTargets() {
         setRunningTask(mFullscreenAppTask);
-        mPolicy.start(mDisplayLayout, mActivityClipData);
-        // TODO(b/169894807): For now, only allow splitting to the right/bottom until we have split
-        //                    pairs
+        mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
         ArrayList<Target> targets = assertExactTargetTypes(
-                mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_RIGHT);
+                mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
+        verify(mStarter).exitSplitScreen();
         verify(mStarter).startIntent(any(), any());
         reset(mStarter);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
-        verify(mStarter).enterSplitScreen(anyInt(), eq(false));
         verify(mStarter).startIntent(any(), any());
     }
 
     @Test
-    public void testDragAppOverFullscreenAppPhone_expectVerticalSplitScreenAndFullscreenTargets()
-            throws RemoteException {
-        setIsPhone(true);
+    public void testDragAppOverFullscreenAppPhone_expectVerticalSplitScreenAndFullscreenTargets() {
         setRunningTask(mFullscreenAppTask);
-        mPolicy.start(mDisplayLayout, mActivityClipData);
-        // TODO(b/169894807): For now, only allow splitting to the right/bottom until we have split
-        //                    pairs
+        mPolicy.start(mPortraitDisplayLayout, mActivityClipData);
         ArrayList<Target> targets = assertExactTargetTypes(
-                mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_BOTTOM);
+                mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
+        verify(mStarter).exitSplitScreen();
         verify(mStarter).startIntent(any(), any());
         reset(mStarter);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
-        verify(mStarter).enterSplitScreen(anyInt(), eq(false));
         verify(mStarter).startIntent(any(), any());
     }
 
     @Test
-    public void testDragAppOverFullscreenNonResizeableApp_expectOnlyFullscreenTargets()
-            throws RemoteException {
+    public void testDragAppOverFullscreenNonResizeableApp_expectOnlyFullscreenTargets() {
         setRunningTask(mNonResizeableFullscreenAppTask);
-        mPolicy.start(mDisplayLayout, mActivityClipData);
+        mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
         ArrayList<Target> targets = assertExactTargetTypes(
-                mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
+                mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
         verify(mStarter).startIntent(any(), any());
     }
 
     @Test
-    public void testDragNonResizeableAppOverFullscreenApp_expectOnlyFullscreenTargets()
-            throws RemoteException {
+    public void testDragNonResizeableAppOverFullscreenApp_expectOnlyFullscreenTargets() {
         setRunningTask(mFullscreenAppTask);
-        mPolicy.start(mDisplayLayout, mNonResizeableActivityClipData);
+        mPolicy.start(mLandscapeDisplayLayout, mNonResizeableActivityClipData);
         ArrayList<Target> targets = assertExactTargetTypes(
-                mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
+                mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
         verify(mStarter).startIntent(any(), any());
     }
 
     @Test
-    public void testDragAppOverSplitApp_expectFullscreenAndSplitTargets() throws RemoteException {
+    public void testDragAppOverSplitApp_expectFullscreenAndSplitTargets() {
         setInSplitScreen(true);
         setRunningTask(mSplitPrimaryAppTask);
-        mPolicy.start(mDisplayLayout, mActivityClipData);
-        // TODO(b/169894807): For now, only allow splitting to the right/bottom until we have split
-        //                    pairs
+        mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
         ArrayList<Target> targets = assertExactTargetTypes(
-                mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_RIGHT);
+                mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
         verify(mStarter).startIntent(any(), any());
@@ -305,16 +277,12 @@
     }
 
     @Test
-    public void testDragAppOverSplitAppPhone_expectFullscreenAndVerticalSplitTargets()
-            throws RemoteException {
-        setIsPhone(true);
+    public void testDragAppOverSplitAppPhone_expectFullscreenAndVerticalSplitTargets() {
         setInSplitScreen(true);
         setRunningTask(mSplitPrimaryAppTask);
-        mPolicy.start(mDisplayLayout, mActivityClipData);
-        // TODO(b/169894807): For now, only allow splitting to the right/bottom until we have split
-        //                    pairs
+        mPolicy.start(mPortraitDisplayLayout, mActivityClipData);
         ArrayList<Target> targets = assertExactTargetTypes(
-                mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_BOTTOM);
+                mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
         verify(mStarter).startIntent(any(), any());
@@ -326,9 +294,9 @@
     }
 
     @Test
-    public void testTargetHitRects() throws RemoteException {
+    public void testTargetHitRects() {
         setRunningTask(mFullscreenAppTask);
-        mPolicy.start(mDisplayLayout, mActivityClipData);
+        mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
         ArrayList<Target> targets = mPolicy.getTargets(mInsets);
         for (Target t : targets) {
             assertTrue(mPolicy.getTargetAtLocation(t.hitRegion.left, t.hitRegion.top) == t);
diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp
index 6c6c5c9..8a10599 100644
--- a/libs/androidfw/LocaleDataTables.cpp
+++ b/libs/androidfw/LocaleDataTables.cpp
@@ -64,42 +64,43 @@
     /* 60 */ {'N', 'k', 'o', 'o'},
     /* 61 */ {'N', 's', 'h', 'u'},
     /* 62 */ {'O', 'g', 'a', 'm'},
-    /* 63 */ {'O', 'r', 'k', 'h'},
-    /* 64 */ {'O', 'r', 'y', 'a'},
-    /* 65 */ {'O', 's', 'g', 'e'},
-    /* 66 */ {'P', 'a', 'u', 'c'},
-    /* 67 */ {'P', 'h', 'l', 'i'},
-    /* 68 */ {'P', 'h', 'n', 'x'},
-    /* 69 */ {'P', 'l', 'r', 'd'},
-    /* 70 */ {'P', 'r', 't', 'i'},
-    /* 71 */ {'R', 'u', 'n', 'r'},
-    /* 72 */ {'S', 'a', 'm', 'r'},
-    /* 73 */ {'S', 'a', 'r', 'b'},
-    /* 74 */ {'S', 'a', 'u', 'r'},
-    /* 75 */ {'S', 'g', 'n', 'w'},
-    /* 76 */ {'S', 'i', 'n', 'h'},
-    /* 77 */ {'S', 'o', 'g', 'd'},
-    /* 78 */ {'S', 'o', 'r', 'a'},
-    /* 79 */ {'S', 'o', 'y', 'o'},
-    /* 80 */ {'S', 'y', 'r', 'c'},
-    /* 81 */ {'T', 'a', 'l', 'e'},
-    /* 82 */ {'T', 'a', 'l', 'u'},
-    /* 83 */ {'T', 'a', 'm', 'l'},
-    /* 84 */ {'T', 'a', 'n', 'g'},
-    /* 85 */ {'T', 'a', 'v', 't'},
-    /* 86 */ {'T', 'e', 'l', 'u'},
-    /* 87 */ {'T', 'f', 'n', 'g'},
-    /* 88 */ {'T', 'h', 'a', 'a'},
-    /* 89 */ {'T', 'h', 'a', 'i'},
-    /* 90 */ {'T', 'i', 'b', 't'},
-    /* 91 */ {'U', 'g', 'a', 'r'},
-    /* 92 */ {'V', 'a', 'i', 'i'},
-    /* 93 */ {'W', 'c', 'h', 'o'},
-    /* 94 */ {'X', 'p', 'e', 'o'},
-    /* 95 */ {'X', 's', 'u', 'x'},
-    /* 96 */ {'Y', 'i', 'i', 'i'},
-    /* 97 */ {'~', '~', '~', 'A'},
-    /* 98 */ {'~', '~', '~', 'B'},
+    /* 63 */ {'O', 'l', 'c', 'k'},
+    /* 64 */ {'O', 'r', 'k', 'h'},
+    /* 65 */ {'O', 'r', 'y', 'a'},
+    /* 66 */ {'O', 's', 'g', 'e'},
+    /* 67 */ {'P', 'a', 'u', 'c'},
+    /* 68 */ {'P', 'h', 'l', 'i'},
+    /* 69 */ {'P', 'h', 'n', 'x'},
+    /* 70 */ {'P', 'l', 'r', 'd'},
+    /* 71 */ {'P', 'r', 't', 'i'},
+    /* 72 */ {'R', 'u', 'n', 'r'},
+    /* 73 */ {'S', 'a', 'm', 'r'},
+    /* 74 */ {'S', 'a', 'r', 'b'},
+    /* 75 */ {'S', 'a', 'u', 'r'},
+    /* 76 */ {'S', 'g', 'n', 'w'},
+    /* 77 */ {'S', 'i', 'n', 'h'},
+    /* 78 */ {'S', 'o', 'g', 'd'},
+    /* 79 */ {'S', 'o', 'r', 'a'},
+    /* 80 */ {'S', 'o', 'y', 'o'},
+    /* 81 */ {'S', 'y', 'r', 'c'},
+    /* 82 */ {'T', 'a', 'l', 'e'},
+    /* 83 */ {'T', 'a', 'l', 'u'},
+    /* 84 */ {'T', 'a', 'm', 'l'},
+    /* 85 */ {'T', 'a', 'n', 'g'},
+    /* 86 */ {'T', 'a', 'v', 't'},
+    /* 87 */ {'T', 'e', 'l', 'u'},
+    /* 88 */ {'T', 'f', 'n', 'g'},
+    /* 89 */ {'T', 'h', 'a', 'a'},
+    /* 90 */ {'T', 'h', 'a', 'i'},
+    /* 91 */ {'T', 'i', 'b', 't'},
+    /* 92 */ {'U', 'g', 'a', 'r'},
+    /* 93 */ {'V', 'a', 'i', 'i'},
+    /* 94 */ {'W', 'c', 'h', 'o'},
+    /* 95 */ {'X', 'p', 'e', 'o'},
+    /* 96 */ {'X', 's', 'u', 'x'},
+    /* 97 */ {'Y', 'i', 'i', 'i'},
+    /* 98 */ {'~', '~', '~', 'A'},
+    /* 99 */ {'~', '~', '~', 'B'},
 };
 
 
@@ -120,7 +121,7 @@
     {0x80600000u, 46u}, // ada -> Latn
     {0x90600000u, 46u}, // ade -> Latn
     {0xA4600000u, 46u}, // adj -> Latn
-    {0xBC600000u, 90u}, // adp -> Tibt
+    {0xBC600000u, 91u}, // adp -> Tibt
     {0xE0600000u, 17u}, // ady -> Cyrl
     {0xE4600000u, 46u}, // adz -> Latn
     {0x61650000u,  4u}, // ae -> Avst
@@ -138,7 +139,7 @@
     {0xB8E00000u,  0u}, // aho -> Ahom
     {0x99200000u, 46u}, // ajg -> Latn
     {0x616B0000u, 46u}, // ak -> Latn
-    {0xA9400000u, 95u}, // akk -> Xsux
+    {0xA9400000u, 96u}, // akk -> Xsux
     {0x81600000u, 46u}, // ala -> Latn
     {0xA1600000u, 46u}, // ali -> Latn
     {0xB5600000u, 46u}, // aln -> Latn
@@ -163,7 +164,7 @@
     {0xC9E00000u, 46u}, // aps -> Latn
     {0xE5E00000u, 46u}, // apz -> Latn
     {0x61720000u,  1u}, // ar -> Arab
-    {0x61725842u, 98u}, // ar-XB -> ~~~B
+    {0x61725842u, 99u}, // ar-XB -> ~~~B
     {0x8A200000u,  2u}, // arc -> Armi
     {0x9E200000u, 46u}, // arh -> Latn
     {0xB6200000u, 46u}, // arn -> Latn
@@ -174,7 +175,7 @@
     {0xE6200000u,  1u}, // arz -> Arab
     {0x61730000u,  7u}, // as -> Beng
     {0x82400000u, 46u}, // asa -> Latn
-    {0x92400000u, 75u}, // ase -> Sgnw
+    {0x92400000u, 76u}, // ase -> Sgnw
     {0x9A400000u, 46u}, // asg -> Latn
     {0xBA400000u, 46u}, // aso -> Latn
     {0xCE400000u, 46u}, // ast -> Latn
@@ -231,7 +232,7 @@
     {0xDC810000u, 46u}, // bex -> Latn
     {0xE4810000u, 46u}, // bez -> Latn
     {0x8CA10000u, 46u}, // bfd -> Latn
-    {0xC0A10000u, 83u}, // bfq -> Taml
+    {0xC0A10000u, 84u}, // bfq -> Taml
     {0xCCA10000u,  1u}, // bft -> Arab
     {0xE0A10000u, 18u}, // bfy -> Deva
     {0x62670000u, 17u}, // bg -> Cyrl
@@ -265,7 +266,7 @@
     {0xC1410000u, 46u}, // bkq -> Latn
     {0xD1410000u, 46u}, // bku -> Latn
     {0xD5410000u, 46u}, // bkv -> Latn
-    {0xCD610000u, 85u}, // blt -> Tavt
+    {0xCD610000u, 86u}, // blt -> Tavt
     {0x626D0000u, 46u}, // bm -> Latn
     {0x9D810000u, 46u}, // bmh -> Latn
     {0xA9810000u, 46u}, // bmk -> Latn
@@ -275,7 +276,7 @@
     {0x99A10000u, 46u}, // bng -> Latn
     {0xB1A10000u, 46u}, // bnm -> Latn
     {0xBDA10000u, 46u}, // bnp -> Latn
-    {0x626F0000u, 90u}, // bo -> Tibt
+    {0x626F0000u, 91u}, // bo -> Tibt
     {0xA5C10000u, 46u}, // boj -> Latn
     {0xB1C10000u, 46u}, // bom -> Latn
     {0xB5C10000u, 46u}, // bon -> Latn
@@ -322,6 +323,7 @@
     {0x9F210000u, 46u}, // bzh -> Latn
     {0xDB210000u, 46u}, // bzw -> Latn
     {0x63610000u, 46u}, // ca -> Latn
+    {0x8C020000u, 46u}, // cad -> Latn
     {0xB4020000u, 46u}, // can -> Latn
     {0xA4220000u, 46u}, // cbj -> Latn
     {0x9C420000u, 46u}, // cch -> Latn
@@ -346,7 +348,7 @@
     {0xE1420000u, 46u}, // cky -> Latn
     {0x81620000u, 46u}, // cla -> Latn
     {0x91820000u, 46u}, // cme -> Latn
-    {0x99820000u, 79u}, // cmg -> Soyo
+    {0x99820000u, 80u}, // cmg -> Soyo
     {0x636F0000u, 46u}, // co -> Latn
     {0xBDC20000u, 15u}, // cop -> Copt
     {0xC9E20000u, 46u}, // cps -> Latn
@@ -360,7 +362,7 @@
     {0x63730000u, 46u}, // cs -> Latn
     {0x86420000u, 46u}, // csb -> Latn
     {0xDA420000u, 10u}, // csw -> Cans
-    {0x8E620000u, 66u}, // ctd -> Pauc
+    {0x8E620000u, 67u}, // ctd -> Pauc
     {0x63750000u, 17u}, // cu -> Cyrl
     {0x63760000u, 17u}, // cv -> Cyrl
     {0x63790000u, 46u}, // cy -> Latn
@@ -389,7 +391,7 @@
     {0x91230000u, 46u}, // dje -> Latn
     {0xA5A30000u, 46u}, // dnj -> Latn
     {0x85C30000u, 46u}, // dob -> Latn
-    {0xA1C30000u,  1u}, // doi -> Arab
+    {0xA1C30000u, 18u}, // doi -> Deva
     {0xBDC30000u, 46u}, // dop -> Latn
     {0xD9C30000u, 46u}, // dow -> Latn
     {0x9E230000u, 56u}, // drh -> Mong
@@ -404,12 +406,12 @@
     {0x8A830000u, 46u}, // duc -> Latn
     {0x8E830000u, 46u}, // dud -> Latn
     {0x9A830000u, 46u}, // dug -> Latn
-    {0x64760000u, 88u}, // dv -> Thaa
+    {0x64760000u, 89u}, // dv -> Thaa
     {0x82A30000u, 46u}, // dva -> Latn
     {0xDAC30000u, 46u}, // dww -> Latn
     {0xBB030000u, 46u}, // dyo -> Latn
     {0xD3030000u, 46u}, // dyu -> Latn
-    {0x647A0000u, 90u}, // dz -> Tibt
+    {0x647A0000u, 91u}, // dz -> Tibt
     {0x9B230000u, 46u}, // dzg -> Latn
     {0xD0240000u, 46u}, // ebu -> Latn
     {0x65650000u, 46u}, // ee -> Latn
@@ -422,7 +424,7 @@
     {0x81840000u, 46u}, // ema -> Latn
     {0xA1840000u, 46u}, // emi -> Latn
     {0x656E0000u, 46u}, // en -> Latn
-    {0x656E5841u, 97u}, // en-XA -> ~~~A
+    {0x656E5841u, 98u}, // en-XA -> ~~~A
     {0xB5A40000u, 46u}, // enn -> Latn
     {0xC1A40000u, 46u}, // enq -> Latn
     {0x656F0000u, 46u}, // eo -> Latn
@@ -438,6 +440,7 @@
     {0x65750000u, 46u}, // eu -> Latn
     {0xBAC40000u, 46u}, // ewo -> Latn
     {0xCEE40000u, 46u}, // ext -> Latn
+    {0x83240000u, 46u}, // eza -> Latn
     {0x66610000u,  1u}, // fa -> Arab
     {0x80050000u, 46u}, // faa -> Latn
     {0x84050000u, 46u}, // fab -> Latn
@@ -521,7 +524,7 @@
     {0x95C60000u, 20u}, // gof -> Ethi
     {0xA1C60000u, 46u}, // goi -> Latn
     {0xB1C60000u, 18u}, // gom -> Deva
-    {0xB5C60000u, 86u}, // gon -> Telu
+    {0xB5C60000u, 87u}, // gon -> Telu
     {0xC5C60000u, 46u}, // gor -> Latn
     {0xC9C60000u, 46u}, // gos -> Latn
     {0xCDC60000u, 24u}, // got -> Goth
@@ -566,7 +569,7 @@
     {0xAD070000u, 46u}, // hil -> Latn
     {0x81670000u, 46u}, // hla -> Latn
     {0xD1670000u, 32u}, // hlu -> Hluw
-    {0x8D870000u, 69u}, // hmd -> Plrd
+    {0x8D870000u, 70u}, // hmd -> Plrd
     {0xCD870000u, 46u}, // hmt -> Latn
     {0x8DA70000u,  1u}, // hnd -> Arab
     {0x91A70000u, 18u}, // hne -> Deva
@@ -601,7 +604,7 @@
     {0x69670000u, 46u}, // ig -> Latn
     {0x84C80000u, 46u}, // igb -> Latn
     {0x90C80000u, 46u}, // ige -> Latn
-    {0x69690000u, 96u}, // ii -> Yiii
+    {0x69690000u, 97u}, // ii -> Yiii
     {0xA5280000u, 46u}, // ijj -> Latn
     {0x696B0000u, 46u}, // ik -> Latn
     {0xA9480000u, 46u}, // ikk -> Latn
@@ -626,6 +629,7 @@
     {0x6A610000u, 36u}, // ja -> Jpan
     {0x84090000u, 46u}, // jab -> Latn
     {0xB0090000u, 46u}, // jam -> Latn
+    {0xC4090000u, 46u}, // jar -> Latn
     {0xB8290000u, 46u}, // jbo -> Latn
     {0xD0290000u, 46u}, // jbu -> Latn
     {0xB4890000u, 46u}, // jen -> Latn
@@ -661,7 +665,7 @@
     {0x906A0000u, 46u}, // kde -> Latn
     {0x9C6A0000u,  1u}, // kdh -> Arab
     {0xAC6A0000u, 46u}, // kdl -> Latn
-    {0xCC6A0000u, 89u}, // kdt -> Thai
+    {0xCC6A0000u, 90u}, // kdt -> Thai
     {0x808A0000u, 46u}, // kea -> Latn
     {0xB48A0000u, 46u}, // ken -> Latn
     {0xE48A0000u, 46u}, // kez -> Latn
@@ -673,7 +677,7 @@
     {0x94CA0000u, 46u}, // kgf -> Latn
     {0xBCCA0000u, 46u}, // kgp -> Latn
     {0x80EA0000u, 46u}, // kha -> Latn
-    {0x84EA0000u, 82u}, // khb -> Talu
+    {0x84EA0000u, 83u}, // khb -> Talu
     {0xB4EA0000u, 18u}, // khn -> Deva
     {0xC0EA0000u, 46u}, // khq -> Latn
     {0xC8EA0000u, 46u}, // khs -> Latn
@@ -766,7 +770,8 @@
     {0x82EA0000u, 46u}, // kxa -> Latn
     {0x8AEA0000u, 20u}, // kxc -> Ethi
     {0x92EA0000u, 46u}, // kxe -> Latn
-    {0xB2EA0000u, 89u}, // kxm -> Thai
+    {0xAEEA0000u, 18u}, // kxl -> Deva
+    {0xB2EA0000u, 90u}, // kxm -> Thai
     {0xBEEA0000u,  1u}, // kxp -> Arab
     {0xDAEA0000u, 46u}, // kxw -> Latn
     {0xE6EA0000u, 46u}, // kxz -> Latn
@@ -775,6 +780,7 @@
     {0x6B795452u, 46u}, // ky-TR -> Latn
     {0x930A0000u, 46u}, // kye -> Latn
     {0xDF0A0000u, 46u}, // kyx -> Latn
+    {0x9F2A0000u,  1u}, // kzh -> Arab
     {0xA72A0000u, 46u}, // kzj -> Latn
     {0xC72A0000u, 46u}, // kzr -> Latn
     {0xCF2A0000u, 46u}, // kzt -> Latn
@@ -790,7 +796,7 @@
     {0xD02B0000u, 46u}, // lbu -> Latn
     {0xD82B0000u, 46u}, // lbw -> Latn
     {0xB04B0000u, 46u}, // lcm -> Latn
-    {0xBC4B0000u, 89u}, // lcp -> Thai
+    {0xBC4B0000u, 90u}, // lcp -> Thai
     {0x846B0000u, 46u}, // ldb -> Latn
     {0x8C8B0000u, 46u}, // led -> Latn
     {0x908B0000u, 46u}, // lee -> Latn
@@ -814,7 +820,7 @@
     {0xCD4B0000u, 46u}, // lkt -> Latn
     {0x916B0000u, 46u}, // lle -> Latn
     {0xB56B0000u, 46u}, // lln -> Latn
-    {0xB58B0000u, 86u}, // lmn -> Telu
+    {0xB58B0000u, 87u}, // lmn -> Telu
     {0xB98B0000u, 46u}, // lmo -> Latn
     {0xBD8B0000u, 46u}, // lmp -> Latn
     {0x6C6E0000u, 46u}, // ln -> Latn
@@ -836,7 +842,7 @@
     {0xE28B0000u, 46u}, // luy -> Latn
     {0xE68B0000u,  1u}, // luz -> Arab
     {0x6C760000u, 46u}, // lv -> Latn
-    {0xAECB0000u, 89u}, // lwl -> Thai
+    {0xAECB0000u, 90u}, // lwl -> Thai
     {0x9F2B0000u, 28u}, // lzh -> Hans
     {0xE72B0000u, 46u}, // lzz -> Latn
     {0x8C0C0000u, 46u}, // mad -> Latn
@@ -927,7 +933,6 @@
     {0xBA2C0000u, 57u}, // mro -> Mroo
     {0x6D730000u, 46u}, // ms -> Latn
     {0x6D734343u,  1u}, // ms-CC -> Arab
-    {0x6D734944u,  1u}, // ms-ID -> Arab
     {0x6D740000u, 46u}, // mt -> Latn
     {0x8A6C0000u, 46u}, // mtc -> Latn
     {0x966C0000u, 46u}, // mtf -> Latn
@@ -1006,11 +1011,11 @@
     {0x9DAD0000u, 46u}, // nnh -> Latn
     {0xA9AD0000u, 46u}, // nnk -> Latn
     {0xB1AD0000u, 46u}, // nnm -> Latn
-    {0xBDAD0000u, 93u}, // nnp -> Wcho
+    {0xBDAD0000u, 94u}, // nnp -> Wcho
     {0x6E6F0000u, 46u}, // no -> Latn
     {0x8DCD0000u, 44u}, // nod -> Lana
     {0x91CD0000u, 18u}, // noe -> Deva
-    {0xB5CD0000u, 71u}, // non -> Runr
+    {0xB5CD0000u, 72u}, // non -> Runr
     {0xBDCD0000u, 46u}, // nop -> Latn
     {0xD1CD0000u, 46u}, // nou -> Latn
     {0xBA0D0000u, 60u}, // nqo -> Nkoo
@@ -1044,18 +1049,18 @@
     {0xB5AE0000u, 46u}, // onn -> Latn
     {0xC9AE0000u, 46u}, // ons -> Latn
     {0xB1EE0000u, 46u}, // opm -> Latn
-    {0x6F720000u, 64u}, // or -> Orya
+    {0x6F720000u, 65u}, // or -> Orya
     {0xBA2E0000u, 46u}, // oro -> Latn
     {0xD22E0000u,  1u}, // oru -> Arab
     {0x6F730000u, 17u}, // os -> Cyrl
-    {0x824E0000u, 65u}, // osa -> Osge
+    {0x824E0000u, 66u}, // osa -> Osge
     {0x826E0000u,  1u}, // ota -> Arab
-    {0xAA6E0000u, 63u}, // otk -> Orkh
+    {0xAA6E0000u, 64u}, // otk -> Orkh
     {0xB32E0000u, 46u}, // ozm -> Latn
     {0x70610000u, 27u}, // pa -> Guru
     {0x7061504Bu,  1u}, // pa-PK -> Arab
     {0x980F0000u, 46u}, // pag -> Latn
-    {0xAC0F0000u, 67u}, // pal -> Phli
+    {0xAC0F0000u, 68u}, // pal -> Phli
     {0xB00F0000u, 46u}, // pam -> Latn
     {0xBC0F0000u, 46u}, // pap -> Latn
     {0xD00F0000u, 46u}, // pau -> Latn
@@ -1065,11 +1070,11 @@
     {0x886F0000u, 46u}, // pdc -> Latn
     {0xCC6F0000u, 46u}, // pdt -> Latn
     {0x8C8F0000u, 46u}, // ped -> Latn
-    {0xB88F0000u, 94u}, // peo -> Xpeo
+    {0xB88F0000u, 95u}, // peo -> Xpeo
     {0xDC8F0000u, 46u}, // pex -> Latn
     {0xACAF0000u, 46u}, // pfl -> Latn
     {0xACEF0000u,  1u}, // phl -> Arab
-    {0xB4EF0000u, 68u}, // phn -> Phnx
+    {0xB4EF0000u, 69u}, // phn -> Phnx
     {0xAD0F0000u, 46u}, // pil -> Latn
     {0xBD0F0000u, 46u}, // pip -> Latn
     {0x814F0000u,  8u}, // pka -> Brah
@@ -1105,7 +1110,7 @@
     {0xB4D10000u, 46u}, // rgn -> Latn
     {0x98F10000u,  1u}, // rhg -> Arab
     {0x81110000u, 46u}, // ria -> Latn
-    {0x95110000u, 87u}, // rif -> Tfng
+    {0x95110000u, 88u}, // rif -> Tfng
     {0x95114E4Cu, 46u}, // rif-NL -> Latn
     {0xC9310000u, 18u}, // rjs -> Deva
     {0xCD510000u,  7u}, // rkt -> Beng
@@ -1135,9 +1140,9 @@
     {0x9C120000u, 17u}, // sah -> Cyrl
     {0xC0120000u, 46u}, // saq -> Latn
     {0xC8120000u, 46u}, // sas -> Latn
-    {0xCC120000u, 46u}, // sat -> Latn
+    {0xCC120000u, 63u}, // sat -> Olck
     {0xD4120000u, 46u}, // sav -> Latn
-    {0xE4120000u, 74u}, // saz -> Saur
+    {0xE4120000u, 75u}, // saz -> Saur
     {0x80320000u, 46u}, // sba -> Latn
     {0x90320000u, 46u}, // sbe -> Latn
     {0xBC320000u, 46u}, // sbp -> Latn
@@ -1161,11 +1166,11 @@
     {0xD8D20000u, 20u}, // sgw -> Ethi
     {0xE4D20000u, 46u}, // sgz -> Latn
     {0x73680000u, 46u}, // sh -> Latn
-    {0xA0F20000u, 87u}, // shi -> Tfng
+    {0xA0F20000u, 88u}, // shi -> Tfng
     {0xA8F20000u, 46u}, // shk -> Latn
     {0xB4F20000u, 58u}, // shn -> Mymr
     {0xD0F20000u,  1u}, // shu -> Arab
-    {0x73690000u, 76u}, // si -> Sinh
+    {0x73690000u, 77u}, // si -> Sinh
     {0x8D120000u, 46u}, // sid -> Latn
     {0x99120000u, 46u}, // sig -> Latn
     {0xAD120000u, 46u}, // sil -> Latn
@@ -1184,7 +1189,7 @@
     {0x81920000u, 46u}, // sma -> Latn
     {0xA5920000u, 46u}, // smj -> Latn
     {0xB5920000u, 46u}, // smn -> Latn
-    {0xBD920000u, 72u}, // smp -> Samr
+    {0xBD920000u, 73u}, // smp -> Samr
     {0xC1920000u, 46u}, // smq -> Latn
     {0xC9920000u, 46u}, // sms -> Latn
     {0x736E0000u, 46u}, // sn -> Latn
@@ -1194,10 +1199,10 @@
     {0xDDB20000u, 46u}, // snx -> Latn
     {0xE1B20000u, 46u}, // sny -> Latn
     {0x736F0000u, 46u}, // so -> Latn
-    {0x99D20000u, 77u}, // sog -> Sogd
+    {0x99D20000u, 78u}, // sog -> Sogd
     {0xA9D20000u, 46u}, // sok -> Latn
     {0xC1D20000u, 46u}, // soq -> Latn
-    {0xD1D20000u, 89u}, // sou -> Thai
+    {0xD1D20000u, 90u}, // sou -> Thai
     {0xE1D20000u, 46u}, // soy -> Latn
     {0x8DF20000u, 46u}, // spd -> Latn
     {0xADF20000u, 46u}, // spl -> Latn
@@ -1208,7 +1213,7 @@
     {0x7372524Fu, 46u}, // sr-RO -> Latn
     {0x73725255u, 46u}, // sr-RU -> Latn
     {0x73725452u, 46u}, // sr-TR -> Latn
-    {0x86320000u, 78u}, // srb -> Sora
+    {0x86320000u, 79u}, // srb -> Sora
     {0xB6320000u, 46u}, // srn -> Latn
     {0xC6320000u, 46u}, // srr -> Latn
     {0xDE320000u, 18u}, // srx -> Deva
@@ -1235,9 +1240,9 @@
     {0xB6F20000u, 46u}, // sxn -> Latn
     {0xDAF20000u, 46u}, // sxw -> Latn
     {0xAF120000u,  7u}, // syl -> Beng
-    {0xC7120000u, 80u}, // syr -> Syrc
+    {0xC7120000u, 81u}, // syr -> Syrc
     {0xAF320000u, 46u}, // szl -> Latn
-    {0x74610000u, 83u}, // ta -> Taml
+    {0x74610000u, 84u}, // ta -> Taml
     {0xA4130000u, 18u}, // taj -> Deva
     {0xAC130000u, 46u}, // tal -> Latn
     {0xB4130000u, 46u}, // tan -> Latn
@@ -1251,11 +1256,11 @@
     {0xE4330000u, 46u}, // tbz -> Latn
     {0xA0530000u, 46u}, // tci -> Latn
     {0xE0530000u, 42u}, // tcy -> Knda
-    {0x8C730000u, 81u}, // tdd -> Tale
+    {0x8C730000u, 82u}, // tdd -> Tale
     {0x98730000u, 18u}, // tdg -> Deva
     {0x9C730000u, 18u}, // tdh -> Deva
     {0xD0730000u, 46u}, // tdu -> Latn
-    {0x74650000u, 86u}, // te -> Telu
+    {0x74650000u, 87u}, // te -> Telu
     {0x8C930000u, 46u}, // ted -> Latn
     {0xB0930000u, 46u}, // tem -> Latn
     {0xB8930000u, 46u}, // teo -> Latn
@@ -1266,7 +1271,7 @@
     {0x88D30000u, 46u}, // tgc -> Latn
     {0xB8D30000u, 46u}, // tgo -> Latn
     {0xD0D30000u, 46u}, // tgu -> Latn
-    {0x74680000u, 89u}, // th -> Thai
+    {0x74680000u, 90u}, // th -> Thai
     {0xACF30000u, 18u}, // thl -> Deva
     {0xC0F30000u, 18u}, // thq -> Deva
     {0xC4F30000u, 18u}, // thr -> Deva
@@ -1305,14 +1310,14 @@
     {0x8E530000u, 25u}, // tsd -> Grek
     {0x96530000u, 18u}, // tsf -> Deva
     {0x9A530000u, 46u}, // tsg -> Latn
-    {0xA6530000u, 90u}, // tsj -> Tibt
+    {0xA6530000u, 91u}, // tsj -> Tibt
     {0xDA530000u, 46u}, // tsw -> Latn
     {0x74740000u, 17u}, // tt -> Cyrl
     {0x8E730000u, 46u}, // ttd -> Latn
     {0x92730000u, 46u}, // tte -> Latn
     {0xA6730000u, 46u}, // ttj -> Latn
     {0xC6730000u, 46u}, // ttr -> Latn
-    {0xCA730000u, 89u}, // tts -> Thai
+    {0xCA730000u, 90u}, // tts -> Thai
     {0xCE730000u, 46u}, // ttt -> Latn
     {0x9E930000u, 46u}, // tuh -> Latn
     {0xAE930000u, 46u}, // tul -> Latn
@@ -1323,7 +1328,7 @@
     {0xD2B30000u, 46u}, // tvu -> Latn
     {0x9ED30000u, 46u}, // twh -> Latn
     {0xC2D30000u, 46u}, // twq -> Latn
-    {0x9AF30000u, 84u}, // txg -> Tang
+    {0x9AF30000u, 85u}, // txg -> Tang
     {0x74790000u, 46u}, // ty -> Latn
     {0x83130000u, 46u}, // tya -> Latn
     {0xD7130000u, 17u}, // tyv -> Cyrl
@@ -1333,7 +1338,7 @@
     {0x75670000u,  1u}, // ug -> Arab
     {0x75674B5Au, 17u}, // ug-KZ -> Cyrl
     {0x75674D4Eu, 17u}, // ug-MN -> Cyrl
-    {0x80D40000u, 91u}, // uga -> Ugar
+    {0x80D40000u, 92u}, // uga -> Ugar
     {0x756B0000u, 17u}, // uk -> Cyrl
     {0xA1740000u, 46u}, // uli -> Latn
     {0x85940000u, 46u}, // umb -> Latn
@@ -1346,6 +1351,7 @@
     {0xCE340000u, 46u}, // urt -> Latn
     {0xDA340000u, 46u}, // urw -> Latn
     {0x82540000u, 46u}, // usa -> Latn
+    {0x9E740000u, 46u}, // uth -> Latn
     {0xC6740000u, 46u}, // utr -> Latn
     {0x9EB40000u, 46u}, // uvh -> Latn
     {0xAEB40000u, 46u}, // uvl -> Latn
@@ -1353,7 +1359,7 @@
     {0x757A4146u,  1u}, // uz-AF -> Arab
     {0x757A434Eu, 17u}, // uz-CN -> Cyrl
     {0x98150000u, 46u}, // vag -> Latn
-    {0xA0150000u, 92u}, // vai -> Vaii
+    {0xA0150000u, 93u}, // vai -> Vaii
     {0xB4150000u, 46u}, // van -> Latn
     {0x76650000u, 46u}, // ve -> Latn
     {0x88950000u, 46u}, // vec -> Latn
@@ -1376,7 +1382,7 @@
     {0xB4160000u, 46u}, // wan -> Latn
     {0xC4160000u, 46u}, // war -> Latn
     {0xBC360000u, 46u}, // wbp -> Latn
-    {0xC0360000u, 86u}, // wbq -> Telu
+    {0xC0360000u, 87u}, // wbq -> Telu
     {0xC4360000u, 18u}, // wbr -> Deva
     {0xA0560000u, 46u}, // wci -> Latn
     {0xC4960000u, 46u}, // wer -> Latn
@@ -1418,9 +1424,9 @@
     {0xC5B70000u, 18u}, // xnr -> Deva
     {0x99D70000u, 46u}, // xog -> Latn
     {0xB5D70000u, 46u}, // xon -> Latn
-    {0xC5F70000u, 70u}, // xpr -> Prti
+    {0xC5F70000u, 71u}, // xpr -> Prti
     {0x86370000u, 46u}, // xrb -> Latn
-    {0x82570000u, 73u}, // xsa -> Sarb
+    {0x82570000u, 74u}, // xsa -> Sarb
     {0xA2570000u, 46u}, // xsi -> Latn
     {0xB2570000u, 46u}, // xsm -> Latn
     {0xC6570000u, 18u}, // xsr -> Deva
@@ -1461,7 +1467,7 @@
     {0x98190000u, 46u}, // zag -> Latn
     {0xA4790000u,  1u}, // zdj -> Arab
     {0x80990000u, 46u}, // zea -> Latn
-    {0x9CD90000u, 87u}, // zgh -> Tfng
+    {0x9CD90000u, 88u}, // zgh -> Tfng
     {0x7A680000u, 28u}, // zh -> Hans
     {0x7A684155u, 29u}, // zh-AU -> Hant
     {0x7A68424Eu, 29u}, // zh-BN -> Hant
@@ -1470,7 +1476,6 @@
     {0x7A68484Bu, 29u}, // zh-HK -> Hant
     {0x7A684944u, 29u}, // zh-ID -> Hant
     {0x7A684D4Fu, 29u}, // zh-MO -> Hant
-    {0x7A684D59u, 29u}, // zh-MY -> Hant
     {0x7A685041u, 29u}, // zh-PA -> Hant
     {0x7A685046u, 29u}, // zh-PF -> Hant
     {0x7A685048u, 29u}, // zh-PH -> Hant
@@ -1592,6 +1597,7 @@
     0xD701434D4C61746ELLU, // byv_Latn_CM
     0x93214D4C4C61746ELLU, // bze_Latn_ML
     0x636145534C61746ELLU, // ca_Latn_ES
+    0x8C0255534C61746ELLU, // cad_Latn_US
     0x9C424E474C61746ELLU, // cch_Latn_NG
     0xBC42424443616B6DLLU, // ccp_Cakm_BD
     0x636552554379726CLLU, // ce_Cyrl_RU
@@ -1627,6 +1633,7 @@
     0x637652554379726CLLU, // cv_Cyrl_RU
     0x637947424C61746ELLU, // cy_Latn_GB
     0x6461444B4C61746ELLU, // da_Latn_DK
+    0x940343494C61746ELLU, // daf_Latn_CI
     0xA80355534C61746ELLU, // dak_Latn_US
     0xC40352554379726CLLU, // dar_Cyrl_RU
     0xD4034B454C61746ELLU, // dav_Latn_KE
@@ -1636,7 +1643,7 @@
     0xC4C343414C61746ELLU, // dgr_Latn_CA
     0x91234E454C61746ELLU, // dje_Latn_NE
     0xA5A343494C61746ELLU, // dnj_Latn_CI
-    0xA1C3494E41726162LLU, // doi_Arab_IN
+    0xA1C3494E44657661LLU, // doi_Deva_IN
     0x9E23434E4D6F6E67LLU, // drh_Mong_CN
     0x864344454C61746ELLU, // dsb_Latn_DE
     0xB2634D4C4C61746ELLU, // dtm_Latn_ML
@@ -1839,6 +1846,7 @@
     0xC6AA49444C61746ELLU, // kvr_Latn_ID
     0xDEAA504B41726162LLU, // kvx_Arab_PK
     0x6B7747424C61746ELLU, // kw_Latn_GB
+    0xAEEA494E44657661LLU, // kxl_Deva_IN
     0xB2EA544854686169LLU, // kxm_Thai_TH
     0xBEEA504B41726162LLU, // kxp_Arab_PK
     0x6B79434E41726162LLU, // ky_Arab_CN
@@ -2047,7 +2055,7 @@
     0x9C1252554379726CLLU, // sah_Cyrl_RU
     0xC0124B454C61746ELLU, // saq_Latn_KE
     0xC81249444C61746ELLU, // sas_Latn_ID
-    0xCC12494E4C61746ELLU, // sat_Latn_IN
+    0xCC12494E4F6C636BLLU, // sat_Olck_IN
     0xD412534E4C61746ELLU, // sav_Latn_SN
     0xE412494E53617572LLU, // saz_Saur_IN
     0xBC32545A4C61746ELLU, // sbp_Latn_TZ
@@ -2149,6 +2157,7 @@
     0x747254524C61746ELLU, // tr_Latn_TR
     0xD23354524C61746ELLU, // tru_Latn_TR
     0xD63354574C61746ELLU, // trv_Latn_TW
+    0xDA33504B41726162LLU, // trw_Arab_PK
     0x74735A414C61746ELLU, // ts_Latn_ZA
     0x8E5347524772656BLLU, // tsd_Grek_GR
     0x96534E5044657661LLU, // tsf_Deva_NP
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 943423f..b944310 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -277,6 +277,28 @@
     }
     return env->NewStringUTF(psName->c_str());
 }
+
+static jint FontFileUtil_isPostScriptType1Font(JNIEnv* env, jobject, jobject buffer, jint index) {
+    NPE_CHECK_RETURN_ZERO(env, buffer);
+    const void* fontPtr = env->GetDirectBufferAddress(buffer);
+    if (fontPtr == nullptr) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", "Not a direct buffer");
+        return -1;
+    }
+    jlong fontSize = env->GetDirectBufferCapacity(buffer);
+    if (fontSize <= 0) {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                          "buffer size must not be zero or negative");
+        return -1;
+    }
+    minikin::FontFileParser parser(fontPtr, fontSize, index);
+    std::optional<bool> isType1 = parser.isPostScriptType1Font();
+    if (!isType1.has_value()) {
+        return -1;  // not an OpenType font. HarfBuzz failed to parse it.
+    }
+    return isType1.value();
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static const JNINativeMethod gFontBuilderMethods[] = {
@@ -304,6 +326,8 @@
     { "nGetFontRevision", "(Ljava/nio/ByteBuffer;I)J", (void*) FontFileUtil_getFontRevision },
     { "nGetFontPostScriptName", "(Ljava/nio/ByteBuffer;I)Ljava/lang/String;",
         (void*) FontFileUtil_getFontPostScriptName },
+    { "nIsPostScriptType1Font", "(Ljava/nio/ByteBuffer;I)I",
+        (void*) FontFileUtil_isPostScriptType1Font },
 };
 
 int register_android_graphics_fonts_Font(JNIEnv* env) {
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 2703ee3..49f9d66 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -21,8 +21,10 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
+import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.media.metrics.PlaybackComponent;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.Looper;
@@ -35,8 +37,8 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -49,7 +51,6 @@
 import java.util.function.Consumer;
 import java.util.function.Function;
 
-
 /**
  * MediaDrm can be used to obtain keys for decrypting protected media streams, in
  * conjunction with {@link android.media.MediaCrypto}.  The MediaDrm APIs
@@ -963,14 +964,30 @@
      * a session
      */
     @NonNull
-    public native byte[] openSession(@SecurityLevel int level) throws
+    public byte[] openSession(@SecurityLevel int level) throws
+            NotProvisionedException, ResourceBusyException {
+        byte[] sessionId = openSessionNative(level);
+        mPlaybackComponentMap.put(ByteBuffer.wrap(sessionId), new PlaybackComponentImpl(sessionId));
+        return sessionId;
+    }
+
+    @NonNull
+    private native byte[] openSessionNative(int level) throws
             NotProvisionedException, ResourceBusyException;
 
     /**
      * Close a session on the MediaDrm object that was previously opened
      * with {@link #openSession}.
      */
-    public native void closeSession(@NonNull byte[] sessionId);
+    public void closeSession(@NonNull byte[] sessionId) {
+        closeSessionNative(sessionId);
+        mPlaybackComponentMap.remove(ByteBuffer.wrap(sessionId));
+    }
+
+    private native void closeSessionNative(@NonNull byte[] sessionId);
+
+    private final Map<ByteBuffer, PlaybackComponent> mPlaybackComponentMap
+            = new ConcurrentHashMap<>();
 
     /**
      * This key request type species that the keys will be for online use, they will
@@ -2056,6 +2073,7 @@
         mCloseGuard.close();
         if (mClosed.compareAndSet(false, true)) {
             native_release();
+            mPlaybackComponentMap.clear();
         }
     }
 
@@ -2430,4 +2448,49 @@
         public static final String EVENT_SESSION_RECLAIMED_COUNT
             = "drm.mediadrm.event.SESSION_RECLAIMED.count";
     }
+
+    /**
+     * Obtain a {@link PlaybackComponent} associated with a DRM session.
+     * Call {@link PlaybackComponent#setPlaybackId(String)} on the returned object
+     * to associate a playback session with the DRM session.
+     *
+     * @param sessionId a DRM session ID obtained from {@link #openSession()}
+     * @return a {@link PlaybackComponent} associated with the session,
+     * or {@code null} if the session is closed or does not exist.
+     * @see PlaybackComponent
+     * @hide
+     */
+    @TestApi
+    @Nullable
+    public PlaybackComponent getPlaybackComponent(@NonNull byte[] sessionId) {
+        if (sessionId == null) {
+            throw new IllegalArgumentException("sessionId is null");
+        }
+        return mPlaybackComponentMap.get(ByteBuffer.wrap(sessionId));
+    }
+
+    private native void setPlaybackId(byte[] sessionId, String playbackId);
+
+    private final class PlaybackComponentImpl implements PlaybackComponent {
+        private final byte[] mSessionId;
+        private String mPlaybackId = "";
+
+        public PlaybackComponentImpl(byte[] sessionId) {
+            mSessionId = sessionId;
+        }
+
+        @Override
+        public void setPlaybackId(@NonNull String playbackId) {
+            if (playbackId == null) {
+                throw new IllegalArgumentException("playbackId is null");
+            }
+            MediaDrm.this.setPlaybackId(mSessionId, playbackId);
+            mPlaybackId = playbackId;
+        }
+
+        @Override
+        @NonNull public String getPlaybackId() {
+            return mPlaybackId;
+        }
+    }
 }
diff --git a/media/java/android/media/metrics/PlaybackComponent.java b/media/java/android/media/metrics/PlaybackComponent.java
index 625dd0a..94e55b4 100644
--- a/media/java/android/media/metrics/PlaybackComponent.java
+++ b/media/java/android/media/metrics/PlaybackComponent.java
@@ -17,11 +17,13 @@
 package android.media.metrics;
 
 import android.annotation.NonNull;
+import android.annotation.TestApi;
 
 /**
  * Interface for playback related components used by playback metrics.
  * @hide
  */
+@TestApi
 public interface PlaybackComponent {
 
     /**
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index babb16b..0e8719e 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -1978,6 +1978,24 @@
     return drm->requiresSecureDecoder(mimeType.c_str(), securityLevel);
 }
 
+static void android_media_MediaDrm_setPlaybackId(
+        JNIEnv *env, jobject thiz, jbyteArray jsessionId,
+        jstring jplaybackId) {
+    sp<IDrm> drm = GetDrm(env, thiz);
+    if (!CheckSession(env, drm, jsessionId)) {
+        return;
+    }
+
+    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
+
+    String8 playbackId;
+    if (jplaybackId != NULL) {
+        playbackId = JStringToString8(env, jplaybackId);
+    }
+    status_t err = drm->setPlaybackId(sessionId, playbackId.c_str());
+    throwExceptionAsNecessary(env, err, "Failed to set playbackId");
+}
+
 static const JNINativeMethod gMethods[] = {
     { "native_release", "()V", (void *)android_media_MediaDrm_native_release },
 
@@ -1992,10 +2010,10 @@
     { "isCryptoSchemeSupportedNative", "([BLjava/lang/String;I)Z",
       (void *)android_media_MediaDrm_isCryptoSchemeSupportedNative },
 
-    { "openSession", "(I)[B",
+    { "openSessionNative", "(I)[B",
       (void *)android_media_MediaDrm_openSession },
 
-    { "closeSession", "([B)V",
+    { "closeSessionNative", "([B)V",
       (void *)android_media_MediaDrm_closeSession },
 
     { "getKeyRequest", "([B[BLjava/lang/String;ILjava/util/HashMap;)"
@@ -2102,6 +2120,9 @@
 
     { "requiresSecureDecoder", "(Ljava/lang/String;I)Z",
       (void *)android_media_MediaDrm_requiresSecureDecoder },
+
+    { "setPlaybackId", "([BLjava/lang/String;)V",
+      (void *)android_media_MediaDrm_setPlaybackId },
 };
 
 int register_android_media_Drm(JNIEnv *env) {
diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml
index ead68e3..b643eb2 100644
--- a/packages/CompanionDeviceManager/res/values-de/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-de/strings.xml
@@ -19,11 +19,9 @@
     <string name="app_label" msgid="4470785958457506021">"Begleitgerät-Manager"</string>
     <string name="chooser_title" msgid="2262294130493605839">"Gerät (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) auswählen, das von &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; verwaltet werden soll"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"Gerät"</string>
-    <!-- no translation found for profile_name_watch (576290739483672360) -->
-    <skip />
+    <string name="profile_name_watch" msgid="576290739483672360">"Smartwatch"</string>
     <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; zum Verwalten deines Geräts (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) festlegen – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <!-- no translation found for profile_summary (2009764182871566255) -->
-    <skip />
+    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> ist erforderlich, um dein Gerät (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) zu verwalten. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
     <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
     <string name="consent_no" msgid="1335543792857823917">"Nein danke"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml
index e99a3cd..7e41042 100644
--- a/packages/CompanionDeviceManager/res/values-gu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml
@@ -19,11 +19,9 @@
     <string name="app_label" msgid="4470785958457506021">"કમ્પેનિયન ડિવાઇસ મેનેજર"</string>
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; દ્વારા મેનેજ કરવા માટે કોઈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> પસંદ કરો"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ડિવાઇસ"</string>
-    <!-- no translation found for profile_name_watch (576290739483672360) -->
-    <skip />
+    <string name="profile_name_watch" msgid="576290739483672360">"સ્માર્ટવૉચ"</string>
     <string name="confirmation_title" msgid="4751119145078041732">"તમારા <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;ને મેનેજ કરવા માટે &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; સેટ કરો"</string>
-    <!-- no translation found for profile_summary (2009764182871566255) -->
-    <skip />
+    <string name="profile_summary" msgid="2009764182871566255">"તમારા <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ને મેનેજ કરવા માટે <xliff:g id="APP_NAME">%1$s</xliff:g> જરૂરી છે. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
     <string name="consent_yes" msgid="4055438216605487056">"હા"</string>
     <string name="consent_no" msgid="1335543792857823917">"ના, આભાર"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml
index 33950eb..8663e56 100644
--- a/packages/CompanionDeviceManager/res/values-iw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml
@@ -19,11 +19,9 @@
     <string name="app_label" msgid="4470785958457506021">"ניהול מכשיר מותאם"</string>
     <string name="chooser_title" msgid="2262294130493605839">"‏בחירה של <xliff:g id="PROFILE_NAME">%1$s</xliff:g> לניהול באמצעות &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"מכשיר"</string>
-    <!-- no translation found for profile_name_watch (576290739483672360) -->
-    <skip />
+    <string name="profile_name_watch" msgid="576290739483672360">"שעון"</string>
     <string name="confirmation_title" msgid="4751119145078041732">"‏הגדרה של &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לניהול <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <!-- no translation found for profile_summary (2009764182871566255) -->
-    <skip />
+    <string name="profile_summary" msgid="2009764182871566255">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> נדרשת לניהול של <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
     <string name="consent_yes" msgid="4055438216605487056">"כן"</string>
     <string name="consent_no" msgid="1335543792857823917">"לא תודה"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml
index b695d9d..ca17336 100644
--- a/packages/CompanionDeviceManager/res/values-ja/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"コンパニオン デバイス マネージャ"</string>
-    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; の管理対象となる <xliff:g id="PROFILE_NAME">%1$s</xliff:g> の選択"</string>
+    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; の管理対象となる<xliff:g id="PROFILE_NAME">%1$s</xliff:g>の選択"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"デバイス"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"ウォッチ"</string>
     <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; で <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; を管理するよう設定する"</string>
diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml
index f4ae18f..0225166 100644
--- a/packages/CompanionDeviceManager/res/values-kn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml
@@ -19,11 +19,9 @@
     <string name="app_label" msgid="4470785958457506021">"ಕಂಪ್ಯಾನಿಯನ್ ಸಾಧನ ನಿರ್ವಾಹಕರು"</string>
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ಮೂಲಕ ನಿರ್ವಹಿಸಬೇಕಾದ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ಸಾಧನ"</string>
-    <!-- no translation found for profile_name_watch (576290739483672360) -->
-    <skip />
+    <string name="profile_name_watch" msgid="576290739483672360">"ವೀಕ್ಷಿಸಿ"</string>
     <string name="confirmation_title" msgid="4751119145078041732">"ನಿಮ್ಮ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ನಿರ್ವಹಿಸಲು, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ನಿರ್ವಹಿಸಿ"</string>
-    <!-- no translation found for profile_summary (2009764182871566255) -->
-    <skip />
+    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> ಅನ್ನು ನಿರ್ವಹಿಸಲು, <xliff:g id="APP_NAME">%1$s</xliff:g> ಅಗತ್ಯವಿದೆ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
     <string name="consent_yes" msgid="4055438216605487056">"ಹೌದು"</string>
     <string name="consent_no" msgid="1335543792857823917">"ಬೇಡ, ಧನ್ಯವಾದಗಳು"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml
index 68f9109..144698b 100644
--- a/packages/CompanionDeviceManager/res/values-mr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml
@@ -19,11 +19,9 @@
     <string name="app_label" msgid="4470785958457506021">"सहयोगी डिव्हाइस व्यवस्थापक"</string>
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; द्वारे व्यवस्थापित करण्यासाठी <xliff:g id="PROFILE_NAME">%1$s</xliff:g> निवडा"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"डिव्हाइस"</string>
-    <!-- no translation found for profile_name_watch (576290739483672360) -->
-    <skip />
+    <string name="profile_name_watch" msgid="576290739483672360">"पाहा"</string>
     <string name="confirmation_title" msgid="4751119145078041732">"तुमची <xliff:g id="PROFILE_NAME">%2$s</xliff:g> व्यवस्थापित करण्यासाठी &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; सेट करा - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <!-- no translation found for profile_summary (2009764182871566255) -->
-    <skip />
+    <string name="profile_summary" msgid="2009764182871566255">"तुमची <xliff:g id="PROFILE_NAME">%2$s</xliff:g> व्यवस्थापित करण्यासाठी <xliff:g id="APP_NAME">%1$s</xliff:g> आवश्यक आहे. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
     <string name="consent_yes" msgid="4055438216605487056">"होय"</string>
     <string name="consent_no" msgid="1335543792857823917">"नाही, नको"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml
index 1188922..7bea2c9 100644
--- a/packages/CompanionDeviceManager/res/values-ms/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml
@@ -19,11 +19,9 @@
     <string name="app_label" msgid="4470785958457506021">"Pengurus Peranti Rakan"</string>
     <string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk diurus oleh &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"peranti"</string>
-    <!-- no translation found for profile_name_watch (576290739483672360) -->
-    <skip />
+    <string name="profile_name_watch" msgid="576290739483672360">"jam tangan"</string>
     <string name="confirmation_title" msgid="4751119145078041732">"Tetapkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengurus <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; anda"</string>
-    <!-- no translation found for profile_summary (2009764182871566255) -->
-    <skip />
+    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> diperlukan untuk mengurus <xliff:g id="PROFILE_NAME">%2$s</xliff:g> anda. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
     <string name="consent_yes" msgid="4055438216605487056">"Ya"</string>
     <string name="consent_no" msgid="1335543792857823917">"Tidak perlu"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml
index d1aa50b..c8c680f 100644
--- a/packages/CompanionDeviceManager/res/values-or/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-or/strings.xml
@@ -19,11 +19,9 @@
     <string name="app_label" msgid="4470785958457506021">"ସହଯୋଗୀ ଡିଭାଇସ୍ ପରିଚାଳକ"</string>
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ଦ୍ୱାରା ପରିଚାଳିତ ହେବା ପାଇଁ ଏକ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>କୁ ବାଛନ୍ତୁ"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ଡିଭାଇସ୍"</string>
-    <!-- no translation found for profile_name_watch (576290739483672360) -->
-    <skip />
+    <string name="profile_name_watch" msgid="576290739483672360">"ୱାଚ୍"</string>
     <string name="confirmation_title" msgid="4751119145078041732">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ସେଟ୍ କରନ୍ତୁ - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <!-- no translation found for profile_summary (2009764182871566255) -->
-    <skip />
+    <string name="profile_summary" msgid="2009764182871566255">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଆବଶ୍ୟକ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
     <string name="consent_yes" msgid="4055438216605487056">"ହଁ"</string>
     <string name="consent_no" msgid="1335543792857823917">"ନା, ଧନ୍ୟବାଦ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml
index ff211f2..0da9410 100644
--- a/packages/CompanionDeviceManager/res/values-pa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml
@@ -19,11 +19,9 @@
     <string name="app_label" msgid="4470785958457506021">"ਸੰਬੰਧੀ ਡੀਵਾਈਸ ਪ੍ਰਬੰਧਕ"</string>
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ ਕੀਤੇ ਜਾਣ ਲਈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ਚੁਣੋ"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ਡੀਵਾਈਸ"</string>
-    <!-- no translation found for profile_name_watch (576290739483672360) -->
-    <skip />
+    <string name="profile_name_watch" msgid="576290739483672360">"ਸਮਾਰਟ-ਵਾਚ"</string>
     <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡਾ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਸੈੱਟ ਕਰੋ"</string>
-    <!-- no translation found for profile_summary (2009764182871566255) -->
-    <skip />
+    <string name="profile_summary" msgid="2009764182871566255">"ਤੁਹਾਡੇ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ <xliff:g id="APP_NAME">%1$s</xliff:g> ਦੀ ਲੋੜ ਹੈ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
     <string name="consent_yes" msgid="4055438216605487056">"ਹਾਂ"</string>
     <string name="consent_no" msgid="1335543792857823917">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml
index 4c308e8..6fa759c 100644
--- a/packages/CompanionDeviceManager/res/values-sq/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml
@@ -19,11 +19,9 @@
     <string name="app_label" msgid="4470785958457506021">"Menaxheri i pajisjes shoqëruese"</string>
     <string name="chooser_title" msgid="2262294130493605839">"Zgjidh një profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> që do të menaxhohet nga &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"pajisja"</string>
-    <!-- no translation found for profile_name_watch (576290739483672360) -->
-    <skip />
+    <string name="profile_name_watch" msgid="576290739483672360">"ora inteligjente"</string>
     <string name="confirmation_title" msgid="4751119145078041732">"Cakto &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; që të menaxhojë profilin tënd <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <!-- no translation found for profile_summary (2009764182871566255) -->
-    <skip />
+    <string name="profile_summary" msgid="2009764182871566255">"Nevojitet <xliff:g id="APP_NAME">%1$s</xliff:g> për të menaxhuar profilin tënd të <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
     <string name="consent_yes" msgid="4055438216605487056">"Po"</string>
     <string name="consent_no" msgid="1335543792857823917">"Jo, faleminderit"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml
index 967b7f9..dce1815 100644
--- a/packages/CompanionDeviceManager/res/values-ur/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -19,11 +19,9 @@
     <string name="app_label" msgid="4470785958457506021">"ساتھی آلہ مینیجر"</string>
     <string name="chooser_title" msgid="2262294130493605839">"‏&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; کے ذریعے نظم کئے جانے کیلئے <xliff:g id="PROFILE_NAME">%1$s</xliff:g> کو منتخب کریں"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"آلہ"</string>
-    <!-- no translation found for profile_name_watch (576290739483672360) -->
-    <skip />
+    <string name="profile_name_watch" msgid="576290739483672360">"دیکھیں"</string>
     <string name="confirmation_title" msgid="4751119145078041732">"‏اپنے <xliff:g id="PROFILE_NAME">%2$s</xliff:g> کا نظم کرنے کے لیے &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کو سیٹ کریں - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <!-- no translation found for profile_summary (2009764182871566255) -->
-    <skip />
+    <string name="profile_summary" msgid="2009764182871566255">"آپ کے <xliff:g id="PROFILE_NAME">%2$s</xliff:g> کا نظم کرنے کے لیے <xliff:g id="APP_NAME">%1$s</xliff:g> کی ضرورت ہے۔ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
     <string name="consent_yes" msgid="4055438216605487056">"ہاں"</string>
     <string name="consent_no" msgid="1335543792857823917">"نہیں شکریہ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml
index 4cce2e8..2ca27b5 100644
--- a/packages/CompanionDeviceManager/res/values-uz/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml
@@ -19,9 +19,9 @@
     <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; boshqaradigan <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qurilmasini tanlang"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"qurilma"</string>
-    <string name="profile_name_watch" msgid="576290739483672360">"tomosha qilish"</string>
+    <string name="profile_name_watch" msgid="576290739483672360">"soat"</string>
     <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; qurilmalarini boshqarish uchun &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasini sozlang"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="PROFILE_NAME">%2$s</xliff:g> qurilmasini boshqarish uchun kerak. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
+    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> qurilmasini boshqarish uchun <xliff:g id="APP_NAME">%1$s</xliff:g> zarur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
     <string name="consent_yes" msgid="4055438216605487056">"Ha"</string>
     <string name="consent_no" msgid="1335543792857823917">"Kerak emas"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-eu/strings.xml b/packages/InputDevices/res/values-eu/strings.xml
index eb95466..0346d74 100644
--- a/packages/InputDevices/res/values-eu/strings.xml
+++ b/packages/InputDevices/res/values-eu/strings.xml
@@ -44,7 +44,7 @@
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituaniera"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espainiera (Latinoamerika)"</string>
     <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letoniera"</string>
-    <string name="keyboard_layout_persian" msgid="3920643161015888527">"Pertsiera"</string>
+    <string name="keyboard_layout_persian" msgid="3920643161015888527">"Persiarra"</string>
     <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbaijandarra"</string>
     <string name="keyboard_layout_polish" msgid="1121588624094925325">"Poloniarra"</string>
     <string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bielorrusiera"</string>
diff --git a/packages/PackageInstaller/res/values-ca/strings.xml b/packages/PackageInstaller/res/values-ca/strings.xml
index 1833329..b25b37b 100644
--- a/packages/PackageInstaller/res/values-ca/strings.xml
+++ b/packages/PackageInstaller/res/values-ca/strings.xml
@@ -80,9 +80,9 @@
     <string name="wear_not_allowed_dlg_text" msgid="704615521550939237">"Les accions d\'instal·lar o de desinstal·lar no s\'admeten a Wear."</string>
     <string name="message_staging" msgid="8032722385658438567">"S\'està preparant la instal·lació de l\'aplicació…"</string>
     <string name="app_name_unknown" msgid="6881210203354323926">"Desconeguda"</string>
-    <string name="untrusted_external_source_warning" product="tablet" msgid="6539403649459942547">"Per seguretat, la tauleta no pot instal·lar aplicacions desconegudes d\'aquesta font."</string>
-    <string name="untrusted_external_source_warning" product="tv" msgid="1206648674551321364">"Per seguretat, el televisor no pot instal·lar aplicacions desconegudes d\'aquesta font."</string>
-    <string name="untrusted_external_source_warning" product="default" msgid="7279739265754475165">"Per seguretat, el telèfon no pot instal·lar aplicacions desconegudes d\'aquesta font."</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="6539403649459942547">"Per la teva seguretat, la tauleta no pot instal·lar aplicacions desconegudes d\'aquesta font."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="1206648674551321364">"Per la teva seguretat, el televisor no pot instal·lar aplicacions desconegudes d\'aquesta font."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="7279739265754475165">"Per la teva seguretat, el telèfon no pot instal·lar aplicacions desconegudes d\'aquesta font."</string>
     <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"El telèfon i les dades personals són més vulnerables als atacs d\'aplicacions desconegudes. En instal·lar aquesta aplicació, acceptes que ets responsable de qualsevol dany que es produeixi al telèfon o de la pèrdua de dades que pugui resultar del seu ús."</string>
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"La tauleta i les dades personals són més vulnerables als atacs d\'aplicacions desconegudes. En instal·lar aquesta aplicació, acceptes que ets responsable de qualsevol dany que es produeixi a la tauleta o de la pèrdua de dades que pugui resultar del seu ús."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"El televisor i les dades personals són més vulnerables als atacs d\'aplicacions desconegudes. En instal·lar aquesta aplicació, acceptes que ets responsable de qualsevol dany que es produeixi al televisor o de la pèrdua de dades que pugui resultar del seu ús."</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
index 02d1c2e..64cb0f1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
@@ -210,7 +210,8 @@
     }
 
     private void checkIfAllSubsystemsRestartsAreDone() {
-        if (!mWifiRestartInProgress && !mTelephonyRestartInProgress) {
+        if (!mWifiRestartInProgress && !mTelephonyRestartInProgress
+                && mCurrentRecoveryCallback != null) {
             mCurrentRecoveryCallback.onSubsystemRestartOperationEnd();
             mCurrentRecoveryCallback = null;
         }
@@ -283,8 +284,10 @@
                     stopTrackingTelephonyRestart();
                     mWifiRestartInProgress = false;
                     mTelephonyRestartInProgress = false;
-                    mCurrentRecoveryCallback.onSubsystemRestartOperationEnd();
-                    mCurrentRecoveryCallback = null;
+                    if (mCurrentRecoveryCallback != null) {
+                        mCurrentRecoveryCallback.onSubsystemRestartOperationEnd();
+                        mCurrentRecoveryCallback = null;
+                    }
                 }, RESTART_TIMEOUT_MS);
             }
         });
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_keyboard_tab_36dp.xml b/packages/SystemUI/res-keyguard/drawable/ic_keyboard_tab_36dp.xml
index 21c9051..b844515 100644
--- a/packages/SystemUI/res-keyguard/drawable/ic_keyboard_tab_36dp.xml
+++ b/packages/SystemUI/res-keyguard/drawable/ic_keyboard_tab_36dp.xml
@@ -13,8 +13,13 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-<vector android:height="36sp" android:viewportHeight="36"
-        android:viewportWidth="36" android:width="36sp" xmlns:android="http://schemas.android.com/apk/res/android">
-    <path android:fillColor="?android:attr/colorAccent" android:pathData="M18,18m-18,0a18,18 0,1 1,36 0a18,18 0,1 1,-36 0"/>
-    <path android:fillColor="?android:attr/textColorPrimaryInverse" android:pathData="M17.59,13.41L21.17,17H7v2h14.17l-3.59,3.59L19,24l6,-6l-6,-6L17.59,13.41zM26,12v12h2V12H26z"/>
-</vector>
\ No newline at end of file
+<vector
+     xmlns:android="http://schemas.android.com/apk/res/android"
+     android:height="36sp"
+     android:viewportHeight="36"
+     android:viewportWidth="36"
+     android:width="36sp">
+  <path android:fillColor="?android:attr/colorBackground"
+        android:pathData="M17.59,13.41L21.17,17H7v2h14.17l-3.59,3.59L19,24l6,-6l-6,-6L17.59,
+                          13.41zM26,12v12h2V12H26z"/>
+</vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/num_pad_key_background.xml b/packages/SystemUI/res-keyguard/drawable/num_pad_key_background.xml
new file mode 100644
index 0000000..b7a9fafd
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/num_pad_key_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* 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.
+*/
+-->
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android">
+  <solid android:color="?android:attr/colorBackground" />
+  <corners android:radius="10dp" />
+</shape>
diff --git a/packages/SystemUI/res-keyguard/drawable/ripple_drawable_pin.xml b/packages/SystemUI/res-keyguard/drawable/ripple_drawable_pin.xml
deleted file mode 100644
index 51c442a..0000000
--- a/packages/SystemUI/res-keyguard/drawable/ripple_drawable_pin.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2018 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
-  -->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-        android:color="?android:attr/colorControlHighlight"
-        android:radius="40dp"/>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_num_pad_key.xml b/packages/SystemUI/res-keyguard/layout/keyguard_num_pad_key.xml
index 72591d46..411fea5 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_num_pad_key.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_num_pad_key.xml
@@ -17,15 +17,18 @@
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
     <TextView
         android:id="@+id/digit_text"
-        style="@style/Widget.TextView.NumPadKey"
+        style="@style/Widget.TextView.NumPadKey.Digit"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         />
+    <!-- b/172360102: Setting visibility to gone for now, in order to see if there are many
+         issues with removing the alpha characters. -->
     <TextView
         android:id="@+id/klondike_text"
         style="@style/Widget.TextView.NumPadKey.Klondike"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:visibility="gone"
         />
 </merge>
 
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index 87c98d2..aa14645a 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -24,7 +24,6 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         androidprv:layout_maxWidth="@dimen/keyguard_security_width"
-        androidprv:layout_maxHeight="@dimen/keyguard_security_max_height"
         android:orientation="vertical"
         >
     <LinearLayout
@@ -34,37 +33,34 @@
             android:orientation="vertical"
             android:layout_weight="1"
             android:layoutDirection="ltr"
+            android:layout_marginBottom="8dp"
             >
-        <com.android.keyguard.AlphaOptimizedRelativeLayout
-                android:id="@+id/row0"
-                android:layout_width="match_parent"
-                android:layout_height="0dp"
-                android:layout_weight="1"
-                android:paddingBottom="16dp"
-                >
+      <Space
+          android:layout_width="match_parent"
+          android:layout_height="0dp"
+          android:layout_weight="1"
+          />
+      <com.android.keyguard.AlphaOptimizedRelativeLayout
+          android:id="@+id/row0"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:paddingBottom="16dp"
+          >
             <com.android.keyguard.PasswordTextView
                     android:id="@+id/pinEntry"
                     android:layout_width="@dimen/keyguard_security_width"
-                    android:layout_height="match_parent"
+                    android:layout_height="@dimen/keyguard_password_height"
                     style="@style/Widget.TextView.Password"
                     android:layout_centerHorizontal="true"
                     android:layout_marginRight="72dp"
                     androidprv:scaledTextSize="@integer/scaled_password_text_size"
                     android:contentDescription="@string/keyguard_accessibility_pin_area"
                     />
-            <View
-                    android:id="@+id/divider"
-                    android:layout_width="match_parent"
-                    android:layout_height="1dp"
-                    android:layout_alignParentBottom="true"
-                    android:background="@drawable/pin_divider"
-                    />
         </com.android.keyguard.AlphaOptimizedRelativeLayout>
         <LinearLayout
                 android:id="@+id/row1"
                 android:layout_width="match_parent"
-                android:layout_height="0dp"
-                android:layout_weight="1"
+                android:layout_height="wrap_content"
                 android:orientation="horizontal"
                 >
             <com.android.keyguard.NumPadKey
@@ -95,8 +91,7 @@
         <LinearLayout
                 android:id="@+id/row2"
                 android:layout_width="match_parent"
-                android:layout_height="0dp"
-                android:layout_weight="1"
+                android:layout_height="wrap_content"
                 android:orientation="horizontal"
                 >
             <com.android.keyguard.NumPadKey
@@ -127,9 +122,8 @@
         <LinearLayout
                 android:id="@+id/row3"
                 android:layout_width="match_parent"
-                android:layout_height="0dp"
+                android:layout_height="wrap_content"
                 android:orientation="horizontal"
-                android:layout_weight="1"
                 >
             <com.android.keyguard.NumPadKey
                     android:id="@+id/key7"
@@ -159,18 +153,16 @@
         <LinearLayout
                 android:id="@+id/row4"
                 android:layout_width="match_parent"
-                android:layout_height="0dp"
-                android:layout_weight="1"
+                android:layout_height="wrap_content"
                 android:orientation="horizontal"
                 >
-            <com.android.keyguard.AlphaOptimizedImageButton
+            <com.android.keyguard.NumPadButton
                     android:id="@+id/delete_button"
                     android:layout_width="0px"
                     android:layout_height="match_parent"
                     android:layout_weight="1"
-                    android:background="@drawable/ripple_drawable_pin"
                     android:contentDescription="@string/keyboardview_keycode_delete"
-                    style="@style/Keyguard.ImageButton.NumPadDelete"
+                    style="@style/NumPadKey.Delete"
                     />
             <com.android.keyguard.NumPadKey
                     android:id="@+id/key0"
@@ -180,13 +172,12 @@
                     androidprv:textView="@+id/pinEntry"
                     androidprv:digit="0"
                     />
-            <com.android.keyguard.AlphaOptimizedImageButton
+            <com.android.keyguard.NumPadButton
                     android:id="@+id/key_enter"
                     android:layout_width="0px"
                     android:layout_height="match_parent"
                     android:layout_weight="1"
-                    style="@style/Keyguard.ImageButton.NumPadEnter"
-                    android:background="@drawable/ripple_drawable_pin"
+                    style="@style/NumPadKey.Enter"
                     android:contentDescription="@string/keyboardview_keycode_enter"
                     />
         </LinearLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
index 912d7bb..64ccefd 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
@@ -25,9 +25,14 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         androidprv:layout_maxWidth="@dimen/keyguard_security_width"
-        androidprv:layout_maxHeight="@dimen/keyguard_security_max_height"
         android:gravity="center_horizontal">
 
+  <Space
+      android:layout_width="match_parent"
+      android:layout_height="0dp"
+      android:layout_weight="1"
+      />
+
     <ImageView
             android:id="@+id/keyguard_sim"
             android:layout_width="match_parent"
@@ -37,10 +42,9 @@
 
     <LinearLayout
             android:layout_width="match_parent"
-            android:layout_height="0dp"
+            android:layout_height="wrap_content"
             android:orientation="vertical"
             android:gravity="center"
-            android:layout_weight="1"
             android:layoutDirection="ltr"
             >
         <include layout="@layout/keyguard_esim_area"
@@ -52,32 +56,23 @@
         <RelativeLayout
                 android:id="@+id/row0"
                 android:layout_width="match_parent"
-                android:layout_height="0dp"
-                android:layout_weight="1"
+                android:layout_height="wrap_content"
                 android:paddingBottom="16dp"
                 >
             <com.android.keyguard.PasswordTextView
                     android:id="@+id/simPinEntry"
                     android:layout_width="@dimen/keyguard_security_width"
-                    android:layout_height="match_parent"
+                    android:layout_height="@dimen/keyguard_password_height"
                     android:gravity="center"
                     android:layout_centerHorizontal="true"
                     android:layout_marginRight="72dp"
                     androidprv:scaledTextSize="@integer/scaled_password_text_size"
                     android:contentDescription="@string/keyguard_accessibility_sim_pin_area"
                     />
-            <View
-                    android:id="@+id/divider"
-                    android:layout_width="match_parent"
-                    android:layout_height="1dp"
-                    android:layout_alignParentBottom="true"
-                    android:background="@drawable/pin_divider"
-                    />
         </RelativeLayout>
         <LinearLayout
                 android:layout_width="match_parent"
-                android:layout_height="0dp"
-                android:layout_weight="1"
+                android:layout_height="wrap_content"
                 android:orientation="horizontal"
                 >
             <com.android.keyguard.NumPadKey
@@ -107,8 +102,7 @@
         </LinearLayout>
         <LinearLayout
                 android:layout_width="match_parent"
-                android:layout_height="0dp"
-                android:layout_weight="1"
+                android:layout_height="wrap_content"
                 android:orientation="horizontal"
                 >
             <com.android.keyguard.NumPadKey
@@ -138,9 +132,8 @@
         </LinearLayout>
         <LinearLayout
                 android:layout_width="match_parent"
-                android:layout_height="0dp"
+                android:layout_height="wrap_content"
                 android:orientation="horizontal"
-                android:layout_weight="1"
                 >
             <com.android.keyguard.NumPadKey
                     android:id="@+id/key7"
@@ -169,18 +162,16 @@
         </LinearLayout>
         <LinearLayout
                 android:layout_width="match_parent"
-                android:layout_height="0dp"
-                android:layout_weight="1"
+                android:layout_height="wrap_content"
                 android:orientation="horizontal"
                 >
-            <com.android.keyguard.AlphaOptimizedImageButton
+            <com.android.keyguard.NumPadButton
                     android:id="@+id/delete_button"
                     android:layout_width="0px"
                     android:layout_height="match_parent"
                     android:layout_weight="1"
-                    android:background="@drawable/ripple_drawable_pin"
                     android:contentDescription="@string/keyboardview_keycode_delete"
-                    style="@style/Keyguard.ImageButton.NumPadDelete"
+                    style="@style/NumPadKey.Delete"
                     />
             <com.android.keyguard.NumPadKey
                     android:id="@+id/key0"
@@ -190,13 +181,12 @@
                     androidprv:textView="@+id/simPinEntry"
                     androidprv:digit="0"
                     />
-            <com.android.keyguard.AlphaOptimizedImageButton
+            <com.android.keyguard.NumPadButton
                     android:id="@+id/key_enter"
                     android:layout_width="0px"
                     android:layout_height="match_parent"
                     android:layout_weight="1"
-                    style="@style/Keyguard.ImageButton.NumPadEnter"
-                    android:background="@drawable/ripple_drawable_pin"
+                    style="@style/NumPadKey.Enter"
                     android:contentDescription="@string/keyboardview_keycode_enter"
                     />
         </LinearLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
index 81b4964..dc77bd3 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
@@ -26,9 +26,14 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         androidprv:layout_maxWidth="@dimen/keyguard_security_width"
-        androidprv:layout_maxHeight="@dimen/keyguard_security_max_height"
         android:gravity="center_horizontal">
 
+  <Space
+      android:layout_width="match_parent"
+      android:layout_height="0dp"
+      android:layout_weight="1"
+      />
+
     <ImageView
             android:id="@+id/keyguard_sim"
             android:layout_width="match_parent"
@@ -38,7 +43,7 @@
 
     <LinearLayout
             android:layout_width="match_parent"
-            android:layout_height="0dp"
+            android:layout_height="wrap_content"
             android:orientation="vertical"
             android:gravity="center"
             android:layout_weight="1"
@@ -53,32 +58,23 @@
         <RelativeLayout
                 android:id="@+id/row0"
                 android:layout_width="match_parent"
-                android:layout_height="0dp"
-                android:layout_weight="1"
+                android:layout_height="wrap_content"
                 android:paddingBottom="16dp"
                 >
             <com.android.keyguard.PasswordTextView
                     android:id="@+id/pukEntry"
                     android:layout_width="@dimen/keyguard_security_width"
-                    android:layout_height="match_parent"
+                    android:layout_height="@dimen/keyguard_password_height"
                     android:gravity="center"
                     android:layout_centerHorizontal="true"
                     android:layout_marginRight="72dp"
                     androidprv:scaledTextSize="@integer/scaled_password_text_size"
                     android:contentDescription="@string/keyguard_accessibility_sim_puk_area"
                     />
-            <View
-                    android:id="@+id/divider"
-                    android:layout_width="match_parent"
-                    android:layout_height="1dp"
-                    android:layout_alignParentBottom="true"
-                    android:background="@drawable/pin_divider"
-                    />
         </RelativeLayout>
         <LinearLayout
                 android:layout_width="match_parent"
-                android:layout_height="0dp"
-                android:layout_weight="1"
+                android:layout_height="wrap_content"
                 android:orientation="horizontal"
                 >
             <com.android.keyguard.NumPadKey
@@ -108,8 +104,7 @@
         </LinearLayout>
         <LinearLayout
                 android:layout_width="match_parent"
-                android:layout_height="0dp"
-                android:layout_weight="1"
+                android:layout_height="wrap_content"
                 android:orientation="horizontal"
                 >
             <com.android.keyguard.NumPadKey
@@ -139,9 +134,8 @@
         </LinearLayout>
         <LinearLayout
                 android:layout_width="match_parent"
-                android:layout_height="0dp"
+                android:layout_height="wrap_content"
                 android:orientation="horizontal"
-                android:layout_weight="1"
                 >
             <com.android.keyguard.NumPadKey
                     android:id="@+id/key7"
@@ -170,18 +164,16 @@
         </LinearLayout>
         <LinearLayout
                 android:layout_width="match_parent"
-                android:layout_height="0dp"
-                android:layout_weight="1"
+                android:layout_height="wrap_content"
                 android:orientation="horizontal"
                 >
-            <com.android.keyguard.AlphaOptimizedImageButton
+            <com.android.keyguard.NumPadButton
                     android:id="@+id/delete_button"
                     android:layout_width="0px"
                     android:layout_height="match_parent"
                     android:layout_weight="1"
-                    android:background="@drawable/ripple_drawable_pin"
                     android:contentDescription="@string/keyboardview_keycode_delete"
-                    style="@style/Keyguard.ImageButton.NumPadDelete"
+                    style="@style/NumPadKey.Delete"
                     />
             <com.android.keyguard.NumPadKey
                     android:id="@+id/key0"
@@ -191,13 +183,12 @@
                     androidprv:textView="@+id/pukEntry"
                     androidprv:digit="0"
                     />
-            <com.android.keyguard.AlphaOptimizedImageButton
+            <com.android.keyguard.NumPadButton
                     android:id="@+id/key_enter"
                     android:layout_width="0px"
                     android:layout_height="match_parent"
                     android:layout_weight="1"
-                    style="@style/Keyguard.ImageButton.NumPadEnter"
-                    android:background="@drawable/ripple_drawable_pin"
+                    style="@style/NumPadKey.Enter"
                     android:contentDescription="@string/keyboardview_keycode_enter"
                     />
         </LinearLayout>
diff --git a/packages/SystemUI/res-keyguard/values/attrs.xml b/packages/SystemUI/res-keyguard/values/attrs.xml
index bfcc56c..eb7a1f7 100644
--- a/packages/SystemUI/res-keyguard/values/attrs.xml
+++ b/packages/SystemUI/res-keyguard/values/attrs.xml
@@ -40,6 +40,8 @@
 
     <attr name="passwordStyle" format="reference" />
 
+    <attr name="numPadKeyStyle" format="reference" />
+
     <declare-styleable name="AnimatableClockView">
         <attr name="dozeWeight" format="integer" />
         <attr name="lockScreenWeight" format="integer" />
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index f9389ce..aa87107 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -33,6 +33,9 @@
          (includes 2x keyguard_security_view_top_margin) -->
     <dimen name="keyguard_security_max_height">450dp</dimen>
 
+    <!-- pin/password field max height -->
+    <dimen name="keyguard_password_height">80dp</dimen>
+
     <!-- Margin around the various security views -->
     <dimen name="keyguard_security_view_top_margin">8dp</dimen>
     <dimen name="keyguard_security_view_lateral_margin">36dp</dimen>
@@ -81,4 +84,7 @@
 
     <!-- The translation for disappearing security views after having solved them. -->
     <dimen name="disappear_y_translation">-32dp</dimen>
+
+    <!-- Spacing around each button used for PIN view -->
+    <dimen name="num_pad_key_margin">2dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 71a1cc2..2e99dea 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -30,7 +30,13 @@
         <item name="android:paddingLeft">12dp</item>
         <item name="android:paddingRight">12dp</item>
     </style>
-    <style name="Widget.TextView.NumPadKey" parent="@android:style/Widget.DeviceDefault.TextView">
+    <style name="NumPadKey" parent="Theme.SystemUI">
+      <item name="android:colorControlNormal">?android:attr/colorBackground</item>
+      <item name="android:colorControlHighlight">?android:attr/colorAccent</item>
+      <item name="android:background">@drawable/num_pad_key_background</item>
+    </style>
+    <style name="Widget.TextView.NumPadKey.Digit"
+           parent="@android:style/Widget.DeviceDefault.TextView">
         <item name="android:singleLine">true</item>
         <item name="android:gravity">center_horizontal|center_vertical</item>
         <item name="android:background">@null</item>
@@ -38,26 +44,25 @@
         <item name="android:textColor">?android:attr/textColorPrimary</item>
         <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
         <item name="android:paddingBottom">-16dp</item>
-        <item name="android:colorControlHighlight">?android:attr/textColorPrimary</item>
     </style>
     <style name="Widget.TextView.Password" parent="@android:style/Widget.TextView">
         <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
         <item name="android:gravity">center</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
-    <style name="Keyguard.ImageButton.NumPadDelete" parent="@android:style/Widget.DeviceDefault.ImageButton">
+    <style name="NumPadKey.Delete">
         <item name="android:src">@drawable/ic_backspace_black_24dp</item>
-        <item name="android:paddingBottom">11sp</item>
         <item name="android:tint">?android:attr/textColorSecondary</item>
         <item name="android:tintMode">src_in</item>
-        <item name="android:src">@drawable/ic_backspace_black_24dp</item>
     </style>
-    <style name="Keyguard.ImageButton.NumPadEnter" parent="@android:style/Widget.DeviceDefault.ImageButton">
-        <item name="android:src">@drawable/ic_keyboard_tab_36dp</item>
-        <item name="android:paddingBottom">11sp</item>
+    <style name="NumPadKey.Enter">
+      <item name="android:colorControlNormal">?android:attr/textColorSecondary</item>
+      <item name="android:src">@drawable/ic_keyboard_tab_36dp</item>
     </style>
-    <style name="Widget.TextView.NumPadKey.Klondike">
+    <style name="Widget.TextView.NumPadKey.Klondike"
+           parent="@android:style/Widget.DeviceDefault.TextView">
         <item name="android:textSize">12sp</item>
+        <item name="android:background">@null</item>
         <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
         <item name="android:textColor">?android:attr/textColorSecondary</item>
         <item name="android:paddingBottom">0dp</item>
diff --git a/packages/SystemUI/res-keyguard/drawable/pin_divider.xml b/packages/SystemUI/res/drawable/qs_background_primary.xml
similarity index 64%
rename from packages/SystemUI/res-keyguard/drawable/pin_divider.xml
rename to packages/SystemUI/res/drawable/qs_background_primary.xml
index 39104b5..0a3afc5 100644
--- a/packages/SystemUI/res-keyguard/drawable/pin_divider.xml
+++ b/packages/SystemUI/res/drawable/qs_background_primary.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2017 The Android Open Source Project
+  ~ Copyright (C) 2021 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -12,9 +12,11 @@
   ~ distributed under the License is distributed on an "AS IS" BASIS,
   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
+  ~ limitations under the License.
   -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="@color/pin_divider_color" />
-</shape>
\ No newline at end of file
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+    <shape>
+        <solid android:color="?android:attr/colorBackground"/>
+        <corners android:radius="@dimen/notification_corner_radius" />
+    </shape>
+</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 387f2f2..77f1743 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -25,7 +25,8 @@
     <View
         android:id="@+id/quick_settings_background"
         android:layout_width="match_parent"
-        android:layout_height="0dp" />
+        android:layout_height="0dp"
+        android:background="@drawable/qs_background_primary" />
 
     <com.android.systemui.qs.NonInterceptingScrollView
         android:id="@+id/expanded_qs_scroll_view"
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index f984100..1630244 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -43,12 +43,6 @@
                    android:visibility="invisible" />
     </com.android.systemui.statusbar.BackDropView>
 
-    <com.android.systemui.statusbar.LightRevealScrim
-        android:id="@+id/light_reveal_scrim"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:visibility="gone" />
-
     <com.android.systemui.statusbar.ScrimView
         android:id="@+id/scrim_behind"
         android:layout_width="match_parent"
@@ -57,6 +51,12 @@
         sysui:ignoreRightInset="true"
         />
 
+    <com.android.systemui.statusbar.LightRevealScrim
+            android:id="@+id/light_reveal_scrim"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="gone" />
+
     <include layout="@layout/status_bar_expanded"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index ba39d1e..e70930a 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nuwe gebruiker"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Vliegtuigveilig"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Netwerke is beskikbaar"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Netwerke is nie beskikbaar nie"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nie gekoppel nie"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Geen netwerk nie"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi af"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index a193bbb..a5ca6e7 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"አዲስ ተጠቃሚ"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"በይነመረብ"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"አውሮፕላን-ደህንነቱ የተጠበቀ"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"አውታረ መረቦች ይገኛሉ"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"አውታረ መረቦች አይገኙም"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"አልተገናኘም"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ምንም አውታረ መረብ የለም"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ጠፍቷል"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index a634281dc..97fcbd6d 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -362,12 +362,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"مستخدم جديد"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"الإنترنت"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"آمنة في الطائرة"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"الشبكات متوفرة"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"الشبكات غير متوفرة"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ليست متصلة"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"لا تتوفر شبكة"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"‏إيقاف Wi-Fi"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 1151644..6ad6b08 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"নতুন ব্যৱহাৰকাৰী"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ৱাই-ফাই"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"ইণ্টাৰনেট"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"এয়াৰপ্লে’নত ব্যৱহাৰৰ বাবে সুৰক্ষিত"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"নেটৱৰ্ক উপলব্ধ"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"নেটৱৰ্ক উপলব্ধ নহয়"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"সংযোগ হৈ থকা নাই"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"নেটৱৰ্ক নাই"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ৱাই-ফাই অফ"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 146add2f..a9441db 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Yeni istifadəçi"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"İnternet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Təyyarə üçün güvənli şəbəkə"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Şəbəkələr əlçatandır"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Şəbəkələr əlçatan deyil"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Bağlantı yoxdur"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Şəbəkə yoxdur"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi sönülüdür"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 77b101c..aebd1e2 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -359,12 +359,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novi korisnik"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Bezbedno za avion"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Mreže su dostupne"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Mreže nisu dostupne"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Veza nije uspostavljena"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nema mreže"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi je isključen"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 5977d04..f115f3f 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -360,12 +360,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Новы карыстальнік"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Інтэрнэт"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Бяспечныя ў самалёце"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Сеткі даступныя"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Сеткі недаступныя"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Няма падключэння"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Няма сеткi"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi адключаны"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 53eeac8..ae94f58 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Нов потребител"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Безопасно в самолети"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Налице са мрежи"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Няма достъпни мрежи"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Няма връзка"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Няма мрежа"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi е изключен"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 7717c65..7ed6937 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -359,7 +359,7 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novi korisnik"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Sigurno za rad u zrakoplovu"</string>
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Sigurno za korištenje u avionu"</string>
     <string name="quick_settings_networks_available" msgid="1875138606855420438">"Mreže su dostupne"</string>
     <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Mreže nisu dostupne"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nije povezano"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index f648bc6..80c4d28 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Usuari nou"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Mode d\'avió"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Xarxes disponibles"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Xarxes no disponibles"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Desconnectat"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No hi ha cap xarxa"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desconnectada"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 4d14d2b..e266b20 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -360,12 +360,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nový uživatel"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Použitelné v letadle"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Dostupné sítě"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Nedostupné sítě"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nepřipojeno"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Žádná síť"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi vypnuta"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 95b1a55..ba8b1e8 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Ny bruger"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Kan bruges i fly"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Tilgængelige netværk"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Ingen tilgængelige netværk"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ikke forbundet"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Intet netværk"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi slået fra"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index e6acd87..7b69260 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Neuer Nutzer"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WLAN"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Flugsicher"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Netzwerke verfügbar"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Netzwerke nicht verfügbar"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nicht verbunden"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Kein Netz"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WLAN aus"</string>
@@ -938,7 +935,7 @@
     <string name="mobile_carrier_text_format" msgid="8912204177152950766">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>, <xliff:g id="MOBILE_DATA_TYPE">%2$s</xliff:g>"</string>
     <string name="wifi_is_off" msgid="5389597396308001471">"WLAN ist deaktiviert"</string>
     <string name="bt_is_off" msgid="7436344904889461591">"Bluetooth ist deaktiviert"</string>
-    <string name="dnd_is_off" msgid="3185706903793094463">"\"Bitte nicht stören\" ist deaktiviert"</string>
+    <string name="dnd_is_off" msgid="3185706903793094463">"„Bitte nicht stören“ ist deaktiviert"</string>
     <string name="qs_dnd_prompt_auto_rule" msgid="3535469468310002616">"\"Bitte nicht stören\" wurde von einer automatischen Regel aktiviert (<xliff:g id="ID_1">%s</xliff:g>)."</string>
     <string name="qs_dnd_prompt_app" msgid="4027984447935396820">"\"Bitte nicht stören\" wurde von einer App aktiviert (<xliff:g id="ID_1">%s</xliff:g>)."</string>
     <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"\"Bitte nicht stören\" wurde von einer automatischen Regel oder einer App aktiviert."</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 185af57..1fa6b11 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -344,10 +344,8 @@
     <string name="quick_settings_ime_label" msgid="3351174938144332051">"Input Method"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
     <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Location Off"</string>
-    <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
-    <skip />
-    <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
-    <skip />
+    <string name="quick_settings_camera_label" msgid="1367149596242401934">"Block camera"</string>
+    <string name="quick_settings_mic_label" msgid="8245831073612564953">"Mute microphone"</string>
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media device"</string>
     <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
     <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Emergency Calls Only"</string>
@@ -358,12 +356,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Aeroplane-safe"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Networks available"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Networks unavailable"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 3517f87..1c1bd89 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -344,10 +344,8 @@
     <string name="quick_settings_ime_label" msgid="3351174938144332051">"Input Method"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
     <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Location Off"</string>
-    <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
-    <skip />
-    <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
-    <skip />
+    <string name="quick_settings_camera_label" msgid="1367149596242401934">"Block camera"</string>
+    <string name="quick_settings_mic_label" msgid="8245831073612564953">"Mute microphone"</string>
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media device"</string>
     <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
     <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Emergency Calls Only"</string>
@@ -358,12 +356,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Aeroplane-safe"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Networks available"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Networks unavailable"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 185af57..1fa6b11 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -344,10 +344,8 @@
     <string name="quick_settings_ime_label" msgid="3351174938144332051">"Input Method"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
     <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Location Off"</string>
-    <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
-    <skip />
-    <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
-    <skip />
+    <string name="quick_settings_camera_label" msgid="1367149596242401934">"Block camera"</string>
+    <string name="quick_settings_mic_label" msgid="8245831073612564953">"Mute microphone"</string>
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media device"</string>
     <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
     <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Emergency Calls Only"</string>
@@ -358,12 +356,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Aeroplane-safe"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Networks available"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Networks unavailable"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 185af57..1fa6b11 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -344,10 +344,8 @@
     <string name="quick_settings_ime_label" msgid="3351174938144332051">"Input Method"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
     <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Location Off"</string>
-    <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
-    <skip />
-    <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
-    <skip />
+    <string name="quick_settings_camera_label" msgid="1367149596242401934">"Block camera"</string>
+    <string name="quick_settings_mic_label" msgid="8245831073612564953">"Mute microphone"</string>
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media device"</string>
     <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
     <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Emergency Calls Only"</string>
@@ -358,12 +356,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Aeroplane-safe"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Networks available"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Networks unavailable"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 306116c..791d75a 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -344,10 +344,8 @@
     <string name="quick_settings_ime_label" msgid="3351174938144332051">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎‎‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‎‎‏‎‎‎‏‎‎‏‏‎Input Method‎‏‎‎‏‎"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‏‎‎‎‏‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‏‏‎‎‎‏‏‎‏‎‎‏‏‏‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎Location‎‏‎‎‏‎"</string>
     <string name="quick_settings_location_off_label" msgid="7923929131443915919">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‏‏‏‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‏‏‏‏‎Location Off‎‏‎‎‏‎"</string>
-    <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
-    <skip />
-    <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
-    <skip />
+    <string name="quick_settings_camera_label" msgid="1367149596242401934">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‎‎‏‏‏‎‏‏‎‏‏‎‏‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎‎‏‏‏‎‎Block Camera‎‏‎‎‏‎"</string>
+    <string name="quick_settings_mic_label" msgid="8245831073612564953">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‎‏‏‎‎‎‏‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎‎‎‎‏‎‎‏‏‏‎‏‏‎‎‏‎Mute Microphone‎‏‎‎‏‎"</string>
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‏‎‎‎‏‎‎‏‏‏‎‏‎‏‎Media device‎‏‎‎‏‎"</string>
     <string name="quick_settings_rssi_label" msgid="3397615415140356701">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎‏‏‎‎‎‎‎‏‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‏‎‎‏‎‏‏‏‎‏‎RSSI‎‏‎‎‏‎"</string>
     <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‎‏‎‏‎‎‎‏‎‎‏‎‎‏‏‏‎‏‎‏‏‎‏‎‎‏‎‏‏‏‎‏‎‏‏‏‏‎‏‏‏‎‏‎‏‎‎‎‏‏‎‎Emergency Calls Only‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 27c424b..9d7a4be 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -344,10 +344,8 @@
     <string name="quick_settings_ime_label" msgid="3351174938144332051">"Método de introducción"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicación"</string>
     <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Ubicación desactivada"</string>
-    <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
-    <skip />
-    <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
-    <skip />
+    <string name="quick_settings_camera_label" msgid="1367149596242401934">"Bloquear cámara"</string>
+    <string name="quick_settings_mic_label" msgid="8245831073612564953">"Silenciar micrófono"</string>
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo multimedia"</string>
     <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
     <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Solo emergencia"</string>
@@ -358,12 +356,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Usuario nuevo"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Seguro para aviones"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Redes disponibles"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Redes no disponible"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Sin conexión"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sin red"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desactivada"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index ab5d0d2..fcec044 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nuevo usuario"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Modo avión"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Redes disponibles"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Redes no disponibles"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"No conectado"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No hay red."</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desactivado"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 6c7e19a..f8745ee 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Uus kasutaja"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Lennukikindel"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Võrgud on saadaval"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Võrgud pole saadaval"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ühendus puudub"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Võrku pole"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi-ühendus on väljas"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index da7733c..d6569f8 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Erabiltzaile berria"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wifia"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Hegaldietarako segurua"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Erabilgarri daude sareak"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Ez dago sarerik erabilgarri"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Konektatu gabe"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ez dago sarerik"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi konexioa desaktibatuta"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 28708b9..c186b6e 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Uusi käyttäjä"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Lentokoneturvallinen"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Verkkoja käytettävissä"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Ei verkkoja käytettävissä"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ei yhteyttä"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ei verkkoa"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi-yhteys pois käytöstä"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 50a3cfb..5b55fbd 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nouvel utilisateur"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Acceptés dans les avions"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Réseaux disponibles"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Réseaux non disponibles"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non connecté"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Aucun réseau"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi désactivé"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 4c6a53e..70505f8 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo usuario"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wifi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Redes seguras para os avións"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Redes dispoñibles"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Non hai redes dispoñibles"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non conectada"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Non hai rede"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wifi desactivada"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index add1ef5..11c35db 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"નવો વપરાશકર્તા"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"વાઇ-ફાઇ"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"ઇન્ટરનેટ"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"એરપ્લેન મોડમાં ઉપયોગ માટે સુરક્ષિત"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"નેટવર્ક ઉપલબ્ધ છે"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"નેટવર્ક અનુપલબ્ધ છે"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"કનેક્ટ થયેલ નથી"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"કોઈ નેટવર્ક નથી"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"વાઇ-ફાઇ બંધ"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index b4d61bb..0a34f6f 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -360,12 +360,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"नया उपयोगकर्ता"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"वाई-फ़ाई"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"इंटरनेट"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"हवाई जहाज़ मोड पर, काम करने वाले सुरक्षित नेटवर्क"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"नेटवर्क उपलब्ध हैं"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"नेटवर्क उपलब्ध नहीं हैं"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"कनेक्ट नहीं है"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"कोई नेटवर्क नहीं"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"वाई-फ़ाई  बंद"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 958e25f6..f62de80 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Új felhasználó"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Repülőgépen használható"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Használhatók hálózatok"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Nem használhatók hálózatok"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nincs kapcsolat"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nincs hálózat"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi kikapcsolva"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 4fd1abe..49f3eb2 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -358,7 +358,7 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Նոր օգտատեր"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Ինտերնետ"</string>
-    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Ցանցեր, որոնք անվտանգ են ինքնաթիռում"</string>
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Ինքնաթիռում անվտանգ"</string>
     <string name="quick_settings_networks_available" msgid="1875138606855420438">"Հասանելի ցանցեր"</string>
     <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Անհասանելի ցանցեր"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Միացված չէ"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 4136ead..29b9797 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Pengguna baru"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Aman di pesawat"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Jaringan tersedia"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Jaringan tidak tersedia"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Tidak Terhubung"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tidak Ada Jaringan"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Mati"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 15c07e4..9cd2379 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nýr notandi"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Öruggt í flugi"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Net í boði"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Net er ekki tiltækt"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Engin tenging"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ekkert net"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Slökkt á Wi-Fi"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 299824b..b08406f 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nuovo utente"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Utilizzabili in aereo"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Reti disponibili"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Reti non disponibili"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non connessa"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nessuna rete"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi disattivato"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index e8cc8bf..368d9ee 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -360,12 +360,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"משתמש חדש"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"אינטרנט"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"רשתות בטוחות לטיסה"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"רשתות זמינות"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"אין רשתות זמינות"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"אין חיבור"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"אין רשת"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"‏Wi-Fi כבוי"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 459b520..c3dfec9 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"新しいユーザー"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"インターネット"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"機内モードで利用可能"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"ネットワークが利用できます"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"ネットワークは利用できません"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"接続されていません"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ネットワークなし"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi OFF"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 65f3032..205ec76 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Жаңа пайдаланушы"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Ұшақта пайдалануға болатын"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Желілер қолжетімді."</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Желілер қолжетімді емес."</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Жалғанбаған"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Желі жоқ"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi өшірулі"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index a4f6e0a..1a4b525 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"អ្នកប្រើ​ថ្មី"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"អ៊ីនធឺណិត"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"សុវត្ថិភាព​ពេលជិះ​យន្តហោះ"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"បណ្ដាញ​ដែលអាច​ប្រើបាន"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"​មិនអាចប្រើ​បណ្តាញ​បានទេ"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"មិន​បាន​តភ្ជាប់"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"គ្មាន​បណ្ដាញ"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"វ៉ាយហ្វាយ​បានបិទ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index e02ed5a..db5b319 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ಹೊಸ ಬಳಕೆದಾರರು"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ವೈ-ಫೈ"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"ಇಂಟರ್ನೆಟ್"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್‌ನಲ್ಲಿ ಬಳಸಲು ಸುರಕ್ಷಿತವಾಗಿದೆ"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"ನೆಟ್‌ವರ್ಕ್‌ಗಳು ಲಭ್ಯವಿವೆ"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"ನೆಟ್‌ವರ್ಕ್‌ಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ಸಂಪರ್ಕಗೊಂಡಿಲ್ಲ"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ನೆಟ್‌ವರ್ಕ್ ಇಲ್ಲ"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ವೈ-ಫೈ ಆಫ್"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index e9d33fa..9c0b27c 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"신규 사용자"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"인터넷"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"항공 안전"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"네트워크 사용 가능"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"네트워크 사용 불가"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"연결되어 있지 않음"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"네트워크가 연결되지 않음"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi 꺼짐"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index f4191d4..b7ee71f 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -360,12 +360,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Жаңы колдонуучу"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Учак режимине ылайыктуу"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Тармактар жеткиликтүү"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Тармактар жеткиликсиз"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Байланышкан жок"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Желе жок"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi өчүк"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 8d9051d..74e1ef7 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -360,12 +360,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Naujas naudotojas"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internetas"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Saugu naudotis lėktuvuose"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Tinklai pasiekiami"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Tinklai nepasiekiami"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Neprisijungta"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tinklo nėra"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"„Wi-Fi“ išjungta"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 1cde532..1de72c5 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -359,12 +359,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Jauns lietotājs"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internets"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Var izmantot lidojuma režīmā"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Tīkli ir pieejami"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Tīkli nav pieejami"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nav izveidots savienojums"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nav tīkla"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ir izslēgts"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index a006bc9..f17e096 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Нов корисник"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Безбедно за во авион"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Мрежите се достапни"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Мрежите се недостапни"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Не е поврзано"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Нема мрежа"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi е исклучено"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 4d2898f..996562e 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"പുതിയ ഉപയോക്താവ്"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"വൈഫൈ"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"ഇന്റർനെറ്റ്"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"വിമാന-സുരക്ഷിതം"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"നെറ്റ്‌വർക്കുകൾ ലഭ്യമാണ്"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"നെറ്റ്‌വർക്കുകൾ ലഭ്യമല്ല"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"കണ‌ക്റ്റ് ചെയ്‌തിട്ടില്ല"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"നെറ്റ്‌വർക്ക് ഒന്നുമില്ല"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"വൈഫൈ ഓഫുചെയ്യുക"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 8845b07..efbbb80 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Шинэ хэрэглэгч"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернэт"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Аюулгүй нислэг"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Сүлжээ боломжтой"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Сүлжээ боломжгүй"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Холбогдоогүй"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Сүлжээгүй"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi унтарсан"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 48498b7..b2041df 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"नवीन वापरकर्ता"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"वाय-फाय"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"इंटरनेट"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"विमानासाठी सुरक्षित"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"नेटवर्क उपलब्ध आहेत"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"नेटवर्क उपलब्ध नाहीत"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"कनेक्ट केले नाही"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"नेटवर्क नाही"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"वाय-फाय बंद"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 4204417..db7d3ce 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Pengguna baharu"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Selamat pesawat"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Rangkaian tersedia"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Rangkaian tidak tersedia"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Tidak Disambungkan"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tiada Rangkaian"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Dimatikan"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 9b7488e..4dfdcf3 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"အသုံးပြုသူ အသစ်"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"အင်တာနက်"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"လေယာဉ်ပျံလုံခြုံရေး"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"ကွန်ရက်များ ရနိုင်သည်"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"ကွန်ရက်များ မရနိုင်ပါ"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ချိတ်ဆက်မထားပါ"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ကွန်ရက်မရှိပါ"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ဝိုင်ဖိုင်ပိတ်ရန်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 490ba99..398631e 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Ny bruker"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internett"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Trygg på fly"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Nettverk er tilgjengelige"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Nettverk er utilgjengelige"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ikke tilkoblet"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ingen nettverk"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi er av"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index cf267fd..6c70a7f 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"नयाँ प्रयोगकर्ता"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"इन्टरनेट"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"हवाइजहाज मोडमा काम गर्ने सुरक्षित नेटवर्क"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"उपलब्ध नेटवर्कहरू"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"नेटवर्क उपलब्ध छैन"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"जोडिएको छैन"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"नेटवर्क छैन"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi बन्द"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index ddb9abc..b9a25832 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nieuwe gebruiker"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wifi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Geschikt voor vliegtuigen"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Netwerken beschikbaar"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Netwerken niet beschikbaar"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Niet verbonden"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Geen netwerk"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wifi uit"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index ce6723e..40d5c3e 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ୱାଇ-ଫାଇ"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"ଇଣ୍ଟରନେଟ୍"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"ଏୟାରପ୍ଲେନ୍-ସେଫ୍"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"ନେଟୱାର୍କଗୁଡ଼ିକ ଉପଲବ୍ଧ"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"ନେଟୱାର୍କ ଉପଲବ୍ଧ ନାହିଁ"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ସଂଯୁକ୍ତ ହୋଇନାହିଁ"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ନେଟ୍‌ୱର୍କ ନାହିଁ"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ୱାଇ-ଫାଇ ଅଫ୍‍"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index ad40d6c..4aece95 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ਵਾਈ-ਫਾਈ"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"ਇੰਟਰਨੈੱਟ"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"ਹਵਾਈ-ਜਹਾਜ਼ ਸੁਰੱਖਿਅਤ ਮੋਡ"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"ਨੈੱਟਵਰਕ ਉਪਲਬਧ ਹਨ"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"ਨੈੱਟਵਰਕ ਉਪਲਬਧ ਨਹੀਂ ਹਨ"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ਕੋਈ ਨੈੱਟਵਰਕ ਨਹੀਂ"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ਵਾਈ-ਫਾਈ ਬੰਦ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 75abfda..fbbbf11 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -360,12 +360,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nowy użytkownik"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Bezpieczne w trybie samolotowym"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Sieci dostępne"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Sieci niedostępne"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Brak połączenia"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Brak sieci"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi wyłączone"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 1ab1002..a29cf03 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -344,10 +344,8 @@
     <string name="quick_settings_ime_label" msgid="3351174938144332051">"Método de entrada"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string>
     <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Localização desativada"</string>
-    <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
-    <skip />
-    <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
-    <skip />
+    <string name="quick_settings_camera_label" msgid="1367149596242401934">"Bloquear câmera"</string>
+    <string name="quick_settings_mic_label" msgid="8245831073612564953">"Desativar microfone"</string>
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo de mídia"</string>
     <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
     <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Chamadas de emergência"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 06b67fb..478d1ee 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -344,10 +344,8 @@
     <string name="quick_settings_ime_label" msgid="3351174938144332051">"Método de Introdução"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string>
     <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Localização Desativada"</string>
-    <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
-    <skip />
-    <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
-    <skip />
+    <string name="quick_settings_camera_label" msgid="1367149596242401934">"Bloquear a câmara"</string>
+    <string name="quick_settings_mic_label" msgid="8245831073612564953">"Desativar o som do microfone"</string>
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo multimédia"</string>
     <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
     <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Apenas chamadas de emergência"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 1ab1002..a29cf03 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -344,10 +344,8 @@
     <string name="quick_settings_ime_label" msgid="3351174938144332051">"Método de entrada"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string>
     <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Localização desativada"</string>
-    <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
-    <skip />
-    <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
-    <skip />
+    <string name="quick_settings_camera_label" msgid="1367149596242401934">"Bloquear câmera"</string>
+    <string name="quick_settings_mic_label" msgid="8245831073612564953">"Desativar microfone"</string>
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo de mídia"</string>
     <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
     <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Chamadas de emergência"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 54d53af..f78a95a 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -359,12 +359,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Utilizator nou"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Sigur pentru avion"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Sunt disponibile rețele"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Nu sunt disponibile rețele"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Neconectată"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nicio rețea"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi deconectat"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index b6c2812..2ae6f0d 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -360,12 +360,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Новый пользователь"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Безопасные в самолете"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Сети доступны"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Сети недоступны"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Нет соединения"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Нет сети"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi выкл."</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index c1ba12b..fe92a8d 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -344,10 +344,8 @@
     <string name="quick_settings_ime_label" msgid="3351174938144332051">"ආදාන ක්‍රමය"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"ස්ථානය"</string>
     <string name="quick_settings_location_off_label" msgid="7923929131443915919">"ස්ථානය අක්‍රියයි"</string>
-    <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
-    <skip />
-    <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
-    <skip />
+    <string name="quick_settings_camera_label" msgid="1367149596242401934">"කැමරාව අවහිර කරන්න"</string>
+    <string name="quick_settings_mic_label" msgid="8245831073612564953">"මයික්‍රෆෝනය නිහඬ කරන්න"</string>
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"මාධ්‍ය උපාංගය"</string>
     <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
     <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"හදිසි ඇමතුම් පමණි"</string>
@@ -358,12 +356,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"නව පරිශීලකයා"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"අන්තර්ජාලය"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"ගුවන් යානා-ආරක්ෂිත"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"ජාල තිබේ"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"ජාල නොමැත"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"සම්බන්ධ වී නොමැත"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ජාලයක් නැත"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi අක්‍රියයි"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index dfa371b..9aeaef8 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -360,12 +360,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nový používateľ"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi‑Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Bezpečné v lietadle"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Siete sú k dispozícii"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Siete nie sú k dispozícii"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nepripojené"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Žiadna sieť"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Sieť Wi‑Fi je vypnutá"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 5f5ee65..e27c03d 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -360,12 +360,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nov uporabnik"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Varna uporaba v letalu"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Omrežja so na voljo"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Omrežja niso na voljo"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Povezava ni vzpostavljena"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ni omrežja"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi izklopljen"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index e1ca274..a5a7de7 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Përdorues i ri"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Të sigurta për në aeroplan"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Ofrohen rrjete"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Rrjetet nuk ofrohen"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nuk është i lidhur"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nuk ka rrjet"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi është i çaktivizuar"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index d7bc5a1..7f7c483 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -359,12 +359,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Нови корисник"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Безбедно за авион"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Мреже су доступне"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Мреже нису доступне"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Веза није успостављена"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Нема мреже"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi је искључен"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 3e38bdd..577719d 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Ny användare"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Flygplanssäker"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Nätverk är tillgängliga"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Inga nätverk är tillgängliga"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ej ansluten"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Inget nätverk"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi av"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index ebb7fa6..ec45b06 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"புதியவர்"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"வைஃபை"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"இணையம்"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"விமானப் பாதுகாப்பு நெட்வொர்க்குகள்"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"நெட்வொர்க்குகள் கிடைக்கின்றன"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"நெட்வொர்க்குகள் கிடைக்கவில்லை"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"இணைக்கப்படவில்லை"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"நெட்வொர்க் இல்லை"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"வைஃபையை முடக்கு"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 1456a8c..a62accb0 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -52,7 +52,7 @@
     <string name="usb_device_confirm_prompt" msgid="4091711472439910809">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>ని నిర్వహించడానికి <xliff:g id="APPLICATION">%1$s</xliff:g>ని తెరవాలా?"</string>
     <string name="usb_device_confirm_prompt_warn" msgid="990208659736311769">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>ని హ్యాండిల్ చేయడానికి <xliff:g id="APPLICATION">%1$s</xliff:g>ను తెరవాలా?\nఈ యాప్‌కు రికార్డ్ చేసే అనుమతి మంజూరు కాలేదు, అయినా ఈ USB పరికరం ద్వారా ఆడియోను క్యాప్చర్ చేయగలదు."</string>
     <string name="usb_accessory_confirm_prompt" msgid="5728408382798643421">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>ని నిర్వహించడానికి <xliff:g id="APPLICATION">%1$s</xliff:g>ని తెరవాలా?"</string>
-    <string name="usb_accessory_uri_prompt" msgid="6756649383432542382">"ఈ USB ఉపకరణంతో ఇన్‌స్టాల్ చేయబడిన అనువర్తనాలు ఏవీ పని చేయవు. ఈ ఉపకరణం గురించి <xliff:g id="URL">%1$s</xliff:g>లో మరింత తెలుసుకోండి"</string>
+    <string name="usb_accessory_uri_prompt" msgid="6756649383432542382">"ఈ USB ఉపకరణంతో ఇన్‌స్టాల్ చేయబడిన యాప్‌లు ఏవీ పని చేయవు. ఈ ఉపకరణం గురించి <xliff:g id="URL">%1$s</xliff:g>లో మరింత తెలుసుకోండి"</string>
     <string name="title_usb_accessory" msgid="1236358027511638648">"USB ఉపకరణం"</string>
     <string name="label_view" msgid="6815442985276363364">"వీక్షించండి"</string>
     <string name="always_use_device" msgid="210535878779644679">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> కనెక్ట్ అయి ఉన్న ఎల్లప్పుడూ <xliff:g id="APPLICATION">%1$s</xliff:g>ని తెరవండి"</string>
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"కొత్త వినియోగదారు"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"ఇంటర్నెట్"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"విమాన-సురక్షితం"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"నెట్‌వర్క్‌లు అందుబాటులో ఉన్నాయి"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"నెట్‌వర్క్‌లు అందుబాటులో లేవు"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"కనెక్ట్ చేయబడలేదు"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"నెట్‌వర్క్ లేదు"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ఆఫ్‌లో ఉంది"</string>
@@ -466,14 +463,14 @@
     <string name="user_add_user" msgid="4336657383006913022">"వినియోగదారుని జోడించండి"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"కొత్త వినియోగదారు"</string>
     <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"గెస్ట్ సెషన్‌ను ముగించాలా?"</string>
-    <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ సెషన్‌లోని అన్ని అనువర్తనాలు మరియు డేటా తొలగించబడతాయి."</string>
+    <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ సెషన్‌లోని అన్ని యాప్‌లు మరియు డేటా తొలగించబడతాయి."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"సెషన్‌ను ముగించు"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"పునఃస్వాగతం, అతిథి!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"మీరు మీ సెషన్‌ని కొనసాగించాలనుకుంటున్నారా?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"మొదటి నుండి ప్రారంభించు"</string>
     <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"అవును, కొనసాగించు"</string>
     <string name="guest_notification_title" msgid="4434456703930764167">"అతిథి వినియోగదారు"</string>
-    <string name="guest_notification_text" msgid="4202692942089571351">"అనువర్తనాలు, డేటా తొలగించేందుకు అతిథి వినియోగదారు తీసివేయండి"</string>
+    <string name="guest_notification_text" msgid="4202692942089571351">"యాప్‌లు, డేటా తొలగించేందుకు అతిథి వినియోగదారు తీసివేయండి"</string>
     <string name="guest_notification_remove_action" msgid="4153019027696868099">"అతిథిని తీసివేయి"</string>
     <string name="user_logout_notification_title" msgid="3644848998053832589">"వినియోగదారుని లాగ్ అవుట్ చేయండి"</string>
     <string name="user_logout_notification_text" msgid="7441286737342997991">"ప్రస్తుత వినియోగదారును లాగ్ అవుట్ చేయండి"</string>
@@ -486,7 +483,7 @@
       <item quantity="one">ఒక్క వినియోగదారుని మాత్రమే సృష్టించవచ్చు.</item>
     </plurals>
     <string name="user_remove_user_title" msgid="9124124694835811874">"వినియోగదారుని తీసివేయాలా?"</string>
-    <string name="user_remove_user_message" msgid="6702834122128031833">"ఈ వినియోగదారుకు సంబంధించిన అన్ని అనువర్తనాలు మరియు డేటా తొలగించబడతాయి."</string>
+    <string name="user_remove_user_message" msgid="6702834122128031833">"ఈ వినియోగదారుకు సంబంధించిన అన్ని యాప్‌లు మరియు డేటా తొలగించబడతాయి."</string>
     <string name="user_remove_user_remove" msgid="8387386066949061256">"తీసివేయి"</string>
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"బ్యాటరీ సేవర్ ఆన్‌లో ఉంది"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"పనితీరుని మరియు నేపథ్య డేటాను తగ్గిస్తుంది"</string>
@@ -542,30 +539,30 @@
     <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"మీ కార్యాలయ ప్రొఫైల్‌లో మీ సంస్థ ఒక ప్రమాణపత్ర అధికారాన్ని ఇన్‌స్టాల్ చేసింది. మీ సురక్షిత నెట్‌వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string>
     <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ఈ పరికరంలో ప్రమాణపత్ర అధికారం ఇన్‌స్టాల్ చేయబడింది. మీ సురక్షిత నెట్‌వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string>
     <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"మీ నిర్వాహకులు మీ పరికరంలోని ట్రాఫిక్‌ని పర్యవేక్షించగల నెట్‌వర్క్ లాగింగ్‌ని ఆన్ చేసారు."</string>
-    <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"మీరు <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
-    <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"మీరు ఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="VPN_APP_0">%1$s</xliff:g> మరియు <xliff:g id="VPN_APP_1">%2$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు."</string>
-    <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"మీ కార్యాలయ ప్రొఫైల్ ఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడింది."</string>
-    <string name="monitoring_description_personal_profile_named_vpn" msgid="8179722332380953673">"మీ వ్యక్తిగత ప్రొఫైల్ ఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడింది."</string>
+    <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"మీరు <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
+    <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"మీరు ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="VPN_APP_0">%1$s</xliff:g> మరియు <xliff:g id="VPN_APP_1">%2$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు."</string>
+    <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"మీ కార్యాలయ ప్రొఫైల్ ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడింది."</string>
+    <string name="monitoring_description_personal_profile_named_vpn" msgid="8179722332380953673">"మీ వ్యక్తిగత ప్రొఫైల్ ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడింది."</string>
     <string name="monitoring_description_do_header_generic" msgid="6130190408164834986">"మీ పరికరం <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> ద్వారా నిర్వహించబడుతోంది."</string>
     <string name="monitoring_description_do_header_with_name" msgid="2696255132542779511">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> మీ పరికరాన్ని నిర్వహించడానికి <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>ని ఉపయోగిస్తుంది."</string>
     <string name="monitoring_description_do_body" msgid="7700878065625769970">"మీ పరికరంతో అనుబంధించబడిన సెట్టింగ్‌లు, కార్పొరేట్ యాక్సెస్, యాప్‌లు, డేటా మరియు మీ పరికరం యొక్క స్థాన సమాచారాన్ని మీ నిర్వాహకులు పర్యవేక్షించగలరు మరియు నిర్వహించగలరు."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="1467280496376492558">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="645149183455573790">"మరింత తెలుసుకోండి"</string>
-    <string name="monitoring_description_do_body_vpn" msgid="7699280130070502303">"మీరు <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌సైట్‌లతో సహా మీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
+    <string name="monitoring_description_do_body_vpn" msgid="7699280130070502303">"మీరు <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="8292589617720435430">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"VPN సెట్టింగ్‌లను తెరవండి"</string>
     <string name="monitoring_description_ca_cert_settings_separator" msgid="7107390013344435439">" "</string>
     <string name="monitoring_description_ca_cert_settings" msgid="8329781950135541003">"విశ్వసనీయ ఆధారాలను తెరువు"</string>
     <string name="monitoring_description_network_logging" msgid="577305979174002252">"మీ నిర్వాహకులు మీ పరికరంలోని ట్రాఫిక్‌ని పర్యవేక్షించగల నెట్‌వర్క్ లాగింగ్‌ని ఆన్ చేసారు.\n\nమరింత సమాచారం కావాలంటే, మీ నిర్వాహకులను సంప్రదించండి."</string>
     <string name="monitoring_description_vpn" msgid="1685428000684586870">"మీరు VPN కనెక్షన్ సెటప్ చేయడానికి ఒక యాప్‌నకు అనుమతి ఇచ్చారు.\n\nఈ యాప్ ఇమెయిల్‌లు,యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ డివైజ్ మరియు నెట్‌వర్క్ కార్యకలాపాన్ని పర్యవేక్షించగలదు."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> ద్వారా మీ కార్యాలయ ప్రొఫైల్ నిర్వహించబడుతోంది.\n\nఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల సామర్థ్యం మీ నిర్వాహకులకు ఉంది.\n\nమరింత సమాచారం కావాలంటే, మీ నిర్వాహకులను సంప్రదించండి.\n\nమీరు VPNకి కూడా కనెక్ట్ అయ్యారు, ఇది మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> ద్వారా మీ కార్యాలయ ప్రొఫైల్ నిర్వహించబడుతోంది.\n\nఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల సామర్థ్యం మీ నిర్వాహకులకు ఉంది.\n\nమరింత సమాచారం కావాలంటే, మీ నిర్వాహకులను సంప్రదించండి.\n\nమీరు VPNకి కూడా కనెక్ట్ అయ్యారు, ఇది మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
     <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ఈ పరికరాన్ని మీ తల్లి/తండ్రి మేనేజ్ చేస్తున్నారు. మీ తల్లి/తండ్రి, మీరు ఉపయోగించే యాప్‌లు, మీ లొకేషన్, అలాగే మీ పరికర వినియోగ వ్యవధి వంటి సమాచారాన్ని చూడగలరు, మేనేజ్ చేయగలరు."</string>
     <string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
-    <string name="monitoring_description_app" msgid="376868879287922929">"మీరు ఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="APPLICATION">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు."</string>
-    <string name="monitoring_description_app_personal" msgid="1970094872688265987">"మీరు <xliff:g id="APPLICATION">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌‍సైట్‌లతో సహా మీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
-    <string name="branded_monitoring_description_app_personal" msgid="1703511985892688885">"మీరు <xliff:g id="APPLICATION">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌సైట్‌లతో సహా మీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
-    <string name="monitoring_description_app_work" msgid="3713084153786663662">"మీ కార్యాలయ ప్రొఫైల్ <xliff:g id="ORGANIZATION">%1$s</xliff:g> నిర్వహణలో ఉంది. ఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌సైట్‌లతో సహా మీ కార్యాలయ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="APPLICATION">%2$s</xliff:g>కి ప్రొఫైల్ కనెక్ట్ చేయబడింది.\n\nమరింత సమాచారం కోసం, మీ నిర్వాహకులను సంప్రదించండి."</string>
-    <string name="monitoring_description_app_personal_work" msgid="6175816356939166101">"మీ కార్యాలయ ప్రొఫైల్ <xliff:g id="ORGANIZATION">%1$s</xliff:g> నిర్వహణలో ఉంది. ఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌సైట్‌లతో సహా మీ కార్యాలయ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>కి ప్రొఫైల్ కనెక్ట్ చేయబడింది.\n\nమీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>కి కూడా మీరు కనెక్ట్ చేయబడ్డారు."</string>
+    <string name="monitoring_description_app" msgid="376868879287922929">"మీరు ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="APPLICATION">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు."</string>
+    <string name="monitoring_description_app_personal" msgid="1970094872688265987">"మీరు <xliff:g id="APPLICATION">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌‍సైట్‌లతో సహా మీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
+    <string name="branded_monitoring_description_app_personal" msgid="1703511985892688885">"మీరు <xliff:g id="APPLICATION">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
+    <string name="monitoring_description_app_work" msgid="3713084153786663662">"మీ కార్యాలయ ప్రొఫైల్ <xliff:g id="ORGANIZATION">%1$s</xliff:g> నిర్వహణలో ఉంది. ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ కార్యాలయ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="APPLICATION">%2$s</xliff:g>కి ప్రొఫైల్ కనెక్ట్ చేయబడింది.\n\nమరింత సమాచారం కోసం, మీ నిర్వాహకులను సంప్రదించండి."</string>
+    <string name="monitoring_description_app_personal_work" msgid="6175816356939166101">"మీ కార్యాలయ ప్రొఫైల్ <xliff:g id="ORGANIZATION">%1$s</xliff:g> నిర్వహణలో ఉంది. ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ కార్యాలయ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>కి ప్రొఫైల్ కనెక్ట్ చేయబడింది.\n\nమీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>కి కూడా మీరు కనెక్ట్ చేయబడ్డారు."</string>
     <string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ద్వారా అన్‌లాక్ చేయబడింది"</string>
     <string name="keyguard_indication_trust_disabled" msgid="6820793704816727918">"మీరు మాన్యువల్‌గా అన్‌లాక్ చేస్తే మినహా పరికరం లాక్ చేయబడి ఉంటుంది"</string>
     <string name="keyguard_indication_trust_unlocked_plugged_in" msgid="2323452175329362855">"<xliff:g id="KEYGUARD_INDICATION">%1$s</xliff:g>\n<xliff:g id="POWER_INDICATION">%2$s</xliff:g>"</string>
@@ -898,7 +895,7 @@
     <string name="tuner_lock_screen" msgid="2267383813241144544">"లాక్ స్క్రీన్"</string>
     <string name="thermal_shutdown_title" msgid="2702966892682930264">"వేడెక్కినందుకు ఫోన్ ఆఫ్ చేయబడింది"</string>
     <string name="thermal_shutdown_message" msgid="6142269839066172984">"మీ ఫోన్ ఇప్పుడు సాధారణంగా పని చేస్తోంది.\nమరింత సమాచారం కోసం ట్యాప్ చేయండి"</string>
-    <string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"మీ ఫోన్ చాలా వేడిగా ఉంది, కనుక చల్లబర్చడానికి ఆఫ్ చేయబడింది. మీ ఫోన్ ఇప్పుడు సాధారణంగా పని చేస్తుంది.\n\nమీరు ఇలా చేస్తే మీ ఫోన్ చాలా వేడెక్కవచ్చు:\n	• వనరు-ఆధారిత అనువర్తనాలు (గేమింగ్, వీడియో లేదా నావిగేషన్ వంటి అనువర్తనాలు) ఉపయోగించడం\n	• పెద్ద ఫైల్‌లను డౌన్‌లోడ్ లేదా అప్‌లోడ్ చేయడం\n	• అధిక ఉష్ణోగ్రతలలో మీ ఫోన్‌ని ఉపయోగించడం"</string>
+    <string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"మీ ఫోన్ చాలా వేడిగా ఉంది, కనుక చల్లబర్చడానికి ఆఫ్ చేయబడింది. మీ ఫోన్ ఇప్పుడు సాధారణంగా పని చేస్తుంది.\n\nమీరు ఇలా చేస్తే మీ ఫోన్ చాలా వేడెక్కవచ్చు:\n	• వనరు-ఆధారిత యాప్‌లు (గేమింగ్, వీడియో లేదా నావిగేషన్ వంటి యాప్‌లు) ఉపయోగించడం\n	• పెద్ద ఫైల్‌లను డౌన్‌లోడ్ లేదా అప్‌లోడ్ చేయడం\n	• అధిక ఉష్ణోగ్రతలలో మీ ఫోన్‌ని ఉపయోగించడం"</string>
     <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"తీసుకోవాల్సిన జాగ్రత్తలు ఏమిటో చూడండి"</string>
     <string name="high_temp_title" msgid="2218333576838496100">"ఫోన్ వేడెక్కుతోంది"</string>
     <string name="high_temp_notif_message" msgid="1277346543068257549">"ఫోన్‌ను చల్లబరిచే క్రమంలో కొన్ని ఫీచర్లు పరిమితం చేయబడ్డాయి.\nమరింత సమాచారం కోసం ట్యాప్ చేయండి"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 65ee106..03bb4c4 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ผู้ใช้ใหม่"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"อินเทอร์เน็ต"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"ใช้บนเครื่องบินได้อย่างปลอดภัย"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"เครือข่ายที่พร้อมใช้งาน"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"ใช้งานเครือข่ายไม่ได้"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ไม่ได้เชื่อมต่อ"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ไม่มีเครือข่าย"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ปิด WiFi"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 9eab088..f037fd5d 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Bagong user"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Ligtas gamitin sa eroplano"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Available ang mga network"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Hindi available ang mga network"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Hindi Nakakonekta"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Walang Network"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Naka-off ang Wi-Fi"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 921cce8..ca4434b 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Yeni kullanıcı"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Kablosuz"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"İnternet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Uçakta kullanımı güvenli"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Kullanılabilir ağlar"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Kullanılamayan ağlar"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Bağlı Değil"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ağ yok"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Kablosuz Kapalı"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 66a8cfb..4bbd062 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -360,12 +360,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Новий користувач"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Інтернет"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Безпечні в літаку"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Доступні мережі"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Недоступні мережі"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Не під’єднано."</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Немає мережі"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi вимкнено"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 3865bc0..28b28c4 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"نیا صارف"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"انٹرنیٹ"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"ہوائی جہاز کیلئے محفوظ"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"نیٹ ورکس دستیاب ہیں"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"نیٹ ورکس دستیاب نہیں ہیں"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"مربوط نہیں ہے"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"کوئی نیٹ ورک نہیں ہے"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"‏Wi-Fi آف ہے"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index d6061a6..315ed75 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -344,10 +344,8 @@
     <string name="quick_settings_ime_label" msgid="3351174938144332051">"Kiritish usuli"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Joylashuv"</string>
     <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Joylashuvni aniqlash xizmati yoqilmagan"</string>
-    <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
-    <skip />
-    <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
-    <skip />
+    <string name="quick_settings_camera_label" msgid="1367149596242401934">"Kamerani bloklash"</string>
+    <string name="quick_settings_mic_label" msgid="8245831073612564953">"Mikrofonni oʻchirish"</string>
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media qurilma"</string>
     <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
     <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Favqulodda chaqiruvlar"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 23e72c1..f0d1a90 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Người dùng mới"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"An toàn với máy bay"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Có mạng"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Không có mạng"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Chưa được kết nối"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Không có mạng nào"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Tắt Wi-Fi"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 55b524e..a18a426 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"新用户"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WLAN"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"互联网"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"可在飞机上安全使用"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"有可用的网络"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"没有可用的网络"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"未连接"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"无网络"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WLAN:关闭"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 701f32d..1268ced 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"新使用者"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"互聯網"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"飛行安全"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"有可用的網絡"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"網絡無法使用"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"未連線"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"沒有網絡"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi 關閉"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 4210ec1..e11013f 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"新使用者"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"網際網路"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"飛航安全"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"有可用的網路"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"沒有網路"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"未連線"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"沒有網路"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi 已關閉"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 940fc19..b022aca 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -358,12 +358,9 @@
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Umsebenzisi omusha"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"I-Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"I-inthanethi"</string>
-    <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
-    <skip />
-    <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
-    <skip />
+    <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Indiza ephephile"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Amanethiwekhi ayatholakala"</string>
+    <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Amanethiwekhi awatholakali"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Akuxhunyiwe"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ayikho inethiwekhi"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"I-Wi-Fi icimile"</string>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index db8bfec..de2db98 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -20,6 +20,7 @@
 
     <bool name="flag_notification_pipeline2">false</bool>
     <bool name="flag_notification_pipeline2_rendering">false</bool>
+    <bool name="flag_shade_is_opaque">true</bool>
 
     <!-- b/171917882 -->
     <bool name="flag_notification_twocolumn">false</bool>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index d706ec2..db260ce 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -319,6 +319,7 @@
         <item name="android:colorControlHighlight">@*android:color/primary_text_material_dark</item>
         <item name="*android:lockPatternStyle">@style/LockPatternStyle</item>
         <item name="passwordStyle">@style/PasswordTheme</item>
+        <item name="numPadKeyStyle">@style/NumPadKey</item>
         <item name="backgroundProtectedStyle">@style/BackgroundProtectedStyle</item>
         <item name="android:homeAsUpIndicator">@drawable/ic_arrow_back</item>
         <item name="shadowRadius">@dimen/keyguard_shadow_radius</item>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 4ddfccb2..6a6b964 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -27,10 +27,8 @@
 import android.content.res.ColorStateList;
 import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
 import android.view.KeyEvent;
 import android.view.View;
-import android.widget.ImageButton;
 
 import com.android.internal.widget.LockscreenCredential;
 import com.android.settingslib.Utils;
@@ -42,10 +40,9 @@
 public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView {
 
     protected PasswordTextView mPasswordEntry;
-    private View mOkButton;
-    private ImageButton mDeleteButton;
+    private NumPadButton mOkButton;
+    private NumPadButton mDeleteButton;
     private NumPadKey[] mButtons = new NumPadKey[10];
-    private View mDivider;
 
     public KeyguardPinBasedInputView(Context context) {
         this(context, null);
@@ -152,7 +149,6 @@
 
         mDeleteButton = findViewById(R.id.delete_button);
         mDeleteButton.setVisibility(View.VISIBLE);
-        mDivider = findViewById(R.id.divider);
 
         mButtons[0] = findViewById(R.id.key0);
         mButtons[1] = findViewById(R.id.key1);
@@ -181,12 +177,9 @@
         int deleteColor = Utils.getColorAttr(getContext(), android.R.attr.textColorSecondary)
                 .getDefaultColor();
         mDeleteButton.setImageTintList(ColorStateList.valueOf(deleteColor));
-        mDivider.setBackground(getContext().getDrawable(R.drawable.pin_divider));
 
-        ContextThemeWrapper themedContext = new ContextThemeWrapper(mContext,
-                R.style.Widget_TextView_NumPadKey);
-        mDeleteButton.setBackground(themedContext.getDrawable(R.drawable.ripple_drawable_pin));
-        mOkButton.setBackground(themedContext.getDrawable(R.drawable.ripple_drawable_pin));
+        mDeleteButton.reloadColors();
+        mOkButton.reloadColors();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 826020c..973b493 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -27,6 +27,7 @@
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -53,8 +54,9 @@
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final ConfigurationController mConfigurationController;
     private final NotificationIconAreaController mNotificationIconAreaController;
+    private final DozeParameters mDozeParameters;
 
-    private boolean mKeyguardStatusViewAnimating;
+    private boolean mKeyguardStatusViewVisibilityAnimating;
     private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
 
     @Inject
@@ -65,7 +67,8 @@
             KeyguardStateController keyguardStateController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             ConfigurationController configurationController,
-            NotificationIconAreaController notificationIconAreaController) {
+            NotificationIconAreaController notificationIconAreaController,
+            DozeParameters dozeParameters) {
         super(keyguardStatusView);
         mKeyguardSliceViewController = keyguardSliceViewController;
         mKeyguardClockSwitchController = keyguardClockSwitchController;
@@ -73,6 +76,7 @@
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mConfigurationController = configurationController;
         mNotificationIconAreaController = notificationIconAreaController;
+        mDozeParameters = dozeParameters;
     }
 
     @Override
@@ -140,7 +144,7 @@
      * Set keyguard status view alpha.
      */
     public void setAlpha(float alpha) {
-        if (!mKeyguardStatusViewAnimating) {
+        if (!mKeyguardStatusViewVisibilityAnimating) {
             mView.setAlpha(alpha);
         }
     }
@@ -194,8 +198,12 @@
      * Update position of the view with an optional animation
      */
     public void updatePosition(int x, int y, float scale, boolean animate) {
-        PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, CLOCK_ANIMATION_PROPERTIES,
-                animate);
+        // We animate the status view visible/invisible using Y translation, so don't change it
+        // while the animation is running.
+        if (!mKeyguardStatusViewVisibilityAnimating) {
+            PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, CLOCK_ANIMATION_PROPERTIES,
+                    animate);
+        }
 
         if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
             // reset any prior movement
@@ -223,10 +231,10 @@
             boolean goingToFullShade,
             int oldStatusBarState) {
         mView.animate().cancel();
-        mKeyguardStatusViewAnimating = false;
+        mKeyguardStatusViewVisibilityAnimating = false;
         if ((!keyguardFadingAway && oldStatusBarState == KEYGUARD
                 && statusBarState != KEYGUARD) || goingToFullShade) {
-            mKeyguardStatusViewAnimating = true;
+            mKeyguardStatusViewVisibilityAnimating = true;
             mView.animate()
                     .alpha(0f)
                     .setStartDelay(0)
@@ -242,7 +250,7 @@
             }
         } else if (oldStatusBarState == StatusBarState.SHADE_LOCKED && statusBarState == KEYGUARD) {
             mView.setVisibility(View.VISIBLE);
-            mKeyguardStatusViewAnimating = true;
+            mKeyguardStatusViewVisibilityAnimating = true;
             mView.setAlpha(0f);
             mView.animate()
                     .alpha(1f)
@@ -252,7 +260,7 @@
                     .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable);
         } else if (statusBarState == KEYGUARD) {
             if (keyguardFadingAway) {
-                mKeyguardStatusViewAnimating = true;
+                mKeyguardStatusViewVisibilityAnimating = true;
                 mView.animate()
                         .alpha(0)
                         .translationYBy(-getHeight() * 0.05f)
@@ -261,6 +269,22 @@
                         .setStartDelay(0)
                         .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable)
                         .start();
+            } else if (mDozeParameters.shouldControlUnlockedScreenOff()) {
+                mKeyguardStatusViewVisibilityAnimating = true;
+
+                mView.setVisibility(View.VISIBLE);
+                mView.setAlpha(0f);
+
+                float curTranslationY = mView.getTranslationY();
+                mView.setTranslationY(curTranslationY - getHeight() * 0.1f);
+                mView.animate()
+                        .setStartDelay((int) (StackStateAnimator.ANIMATION_DURATION_WAKEUP * .6f))
+                        .setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP)
+                        .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                        .alpha(1f)
+                        .translationY(curTranslationY)
+                        .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable)
+                        .start();
             } else {
                 mView.setVisibility(View.VISIBLE);
                 mView.setAlpha(1f);
@@ -367,17 +391,17 @@
     };
 
     private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = () -> {
-        mKeyguardStatusViewAnimating = false;
+        mKeyguardStatusViewVisibilityAnimating = false;
         mView.setVisibility(View.INVISIBLE);
     };
 
 
     private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = () -> {
-        mKeyguardStatusViewAnimating = false;
+        mKeyguardStatusViewVisibilityAnimating = false;
         mView.setVisibility(View.GONE);
     };
 
     private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = () -> {
-        mKeyguardStatusViewAnimating = false;
+        mKeyguardStatusViewVisibilityAnimating = false;
     };
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
new file mode 100644
index 0000000..cdf9858
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard;
+
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.GradientDrawable;
+import android.view.ContextThemeWrapper;
+import android.view.ViewGroup;
+
+import androidx.annotation.StyleRes;
+
+import com.android.internal.graphics.ColorUtils;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+
+/**
+ * Provides background color and radius animations for key pad buttons.
+ */
+class NumPadAnimator {
+    private ValueAnimator mAnimator;
+    private GradientDrawable mBackground;
+    private int mMargin;
+    private int mNormalColor;
+    private int mHighlightColor;
+    private int mStyle;
+
+    NumPadAnimator(Context context, final GradientDrawable background, @StyleRes int style) {
+        mBackground = (GradientDrawable) background.mutate();
+        mStyle = style;
+
+        reloadColors(context);
+
+        mMargin = context.getResources().getDimensionPixelSize(R.dimen.num_pad_key_margin);
+
+        // Actual values will be updated later, usually during an onLayout() call
+        mAnimator = ValueAnimator.ofFloat(0f);
+        mAnimator.setDuration(250);
+        mAnimator.setInterpolator(Interpolators.LINEAR);
+        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                public void onAnimationUpdate(ValueAnimator anim) {
+                    mBackground.setCornerRadius((float) anim.getAnimatedValue());
+                    mBackground.setColor(ColorUtils.blendARGB(mHighlightColor, mNormalColor,
+                            anim.getAnimatedFraction()));
+                }
+        });
+
+    }
+
+    void updateMargin(ViewGroup.MarginLayoutParams lp) {
+        lp.setMargins(mMargin, mMargin, mMargin, mMargin);
+    }
+
+    void onLayout(int height) {
+        float startRadius = height / 10f;
+        float endRadius = height / 2f;
+        mBackground.setCornerRadius(endRadius);
+        mAnimator.setFloatValues(startRadius, endRadius);
+    }
+
+    void start() {
+        mAnimator.cancel();
+        mAnimator.start();
+    }
+
+    /**
+     * Reload colors from resources.
+     **/
+    void reloadColors(Context context) {
+        int[] customAttrs = {android.R.attr.colorControlNormal,
+                android.R.attr.colorControlHighlight};
+
+        ContextThemeWrapper ctw = new ContextThemeWrapper(context, mStyle);
+        TypedArray a = ctw.obtainStyledAttributes(customAttrs);
+        mNormalColor = a.getColor(0, 0);
+        mHighlightColor = a.getColor(1, 0);
+        a.recycle();
+
+        mBackground.setColor(mNormalColor);
+    }
+}
+
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
new file mode 100644
index 0000000..3728106
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard;
+
+import android.content.Context;
+import android.graphics.drawable.GradientDrawable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.ViewGroup;
+
+/**
+ * Similar to the {@link NumPadKey}, but displays an image.
+ */
+public class NumPadButton extends AlphaOptimizedImageButton {
+
+    private final NumPadAnimator mAnimator;
+
+    public NumPadButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        mAnimator = new NumPadAnimator(context, (GradientDrawable) getBackground(),
+                attrs.getStyleAttribute());
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        mAnimator.updateMargin((ViewGroup.MarginLayoutParams) getLayoutParams());
+
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        // Set width/height to the same value to ensure a smooth circle for the bg
+        setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+
+        mAnimator.onLayout(b - t);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        mAnimator.start();
+        return super.onTouchEvent(event);
+    }
+
+    /**
+     * Reload colors from resources.
+     **/
+    public void reloadColors() {
+        mAnimator.reloadColors(getContext());
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index a518205..8ebcb20 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -18,11 +18,10 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
 import android.view.HapticFeedbackConstants;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -48,6 +47,8 @@
     private int mTextViewResId;
     private PasswordTextView mTextView;
 
+    private final NumPadAnimator mAnimator;
+
     private View.OnClickListener mListener = new View.OnClickListener() {
         @Override
         public void onClick(View thisView) {
@@ -73,7 +74,7 @@
     }
 
     public NumPadKey(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
+        this(context, attrs, R.attr.numPadKeyStyle);
     }
 
     public NumPadKey(Context context, AttributeSet attrs, int defStyle) {
@@ -84,7 +85,8 @@
         super(context, attrs, defStyle);
         setFocusable(true);
 
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NumPadKey);
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NumPadKey, defStyle,
+                contentResource);
 
         try {
             mDigit = a.getInt(R.styleable.NumPadKey_digit, mDigit);
@@ -116,20 +118,16 @@
                 final int len = klondike.length();
                 if (len > 0) {
                     mKlondikeText.setText(klondike);
-                } else {
+                } else if (mKlondikeText.getVisibility() != View.GONE) {
                     mKlondikeText.setVisibility(View.INVISIBLE);
                 }
             }
         }
 
-        a = context.obtainStyledAttributes(attrs, android.R.styleable.View);
-        if (!a.hasValueOrEmpty(android.R.styleable.View_background)) {
-            Drawable rippleDrawable = new ContextThemeWrapper(mContext,
-                    R.style.Widget_TextView_NumPadKey).getDrawable(R.drawable.ripple_drawable_pin);
-            setBackground(rippleDrawable);
-        }
-        a.recycle();
         setContentDescription(mDigitText.getText().toString());
+
+        mAnimator = new NumPadAnimator(context, (GradientDrawable) getBackground(),
+                R.style.NumPadKey);
     }
 
     /**
@@ -142,9 +140,8 @@
                 .getDefaultColor();
         mDigitText.setTextColor(textColor);
         mKlondikeText.setTextColor(klondikeColor);
-        Drawable rippleDrawable = new ContextThemeWrapper(mContext,
-                R.style.Widget_TextView_NumPadKey).getDrawable(R.drawable.ripple_drawable_pin);
-        setBackground(rippleDrawable);
+
+        mAnimator.reloadColors(getContext());
     }
 
     @Override
@@ -152,13 +149,21 @@
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
             doHapticKeyClick();
         }
+
+        mAnimator.start();
+
         return super.onTouchEvent(event);
     }
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        mAnimator.updateMargin((ViewGroup.MarginLayoutParams) getLayoutParams());
+
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         measureChildren(widthMeasureSpec, heightMeasureSpec);
+
+        // Set width/height to the same value to ensure a smooth circle for the bg
+        setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
     }
 
     @Override
@@ -176,6 +181,8 @@
 
         left = centerX - mKlondikeText.getMeasuredWidth() / 2;
         mKlondikeText.layout(left, top, left + mKlondikeText.getMeasuredWidth(), bottom);
+
+        mAnimator.onLayout(b - t);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
deleted file mode 100644
index c0f8cae..0000000
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2019 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;
-
-import android.annotation.NonNull;
-import android.app.Notification;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.ArraySet;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.statusbar.NotificationInteractionTracker;
-import com.android.systemui.statusbar.NotificationLifetimeExtender;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.util.time.SystemClock;
-
-import javax.inject.Inject;
-
-/**
- * Extends the lifetime of foreground notification services such that they show for at least
- * five seconds
- */
-public class ForegroundServiceLifetimeExtender implements NotificationLifetimeExtender {
-
-    private static final String TAG = "FGSLifetimeExtender";
-    @VisibleForTesting
-    static final int MIN_FGS_TIME_MS = 5000;
-
-    private NotificationSafeToRemoveCallback mNotificationSafeToRemoveCallback;
-    private ArraySet<NotificationEntry> mManagedEntries = new ArraySet<>();
-    private Handler mHandler = new Handler(Looper.getMainLooper());
-    private final SystemClock mSystemClock;
-    private final NotificationInteractionTracker mInteractionTracker;
-
-    @Inject
-    public ForegroundServiceLifetimeExtender(
-            NotificationInteractionTracker interactionTracker,
-            SystemClock systemClock) {
-        mSystemClock = systemClock;
-        mInteractionTracker = interactionTracker;
-    }
-
-    @Override
-    public void setCallback(@NonNull NotificationSafeToRemoveCallback callback) {
-        mNotificationSafeToRemoveCallback = callback;
-    }
-
-    @Override
-    public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
-        if ((entry.getSbn().getNotification().flags
-                & Notification.FLAG_FOREGROUND_SERVICE) == 0) {
-            return false;
-        }
-
-        // Entry has triggered a HUN or some other interruption, therefore it has been seen and the
-        // interrupter might be retaining it anyway.
-        if (entry.hasInterrupted()) {
-            return false;
-        }
-
-        boolean hasInteracted = mInteractionTracker.hasUserInteractedWith(entry.getKey());
-        long aliveTime = mSystemClock.uptimeMillis() - entry.getCreationTime();
-        return aliveTime < MIN_FGS_TIME_MS && !hasInteracted;
-    }
-
-    @Override
-    public boolean shouldExtendLifetimeForPendingNotification(
-            @NonNull NotificationEntry entry) {
-        return shouldExtendLifetime(entry);
-    }
-
-    @Override
-    public void setShouldManageLifetime(
-            @NonNull NotificationEntry entry, boolean shouldManage) {
-        if (!shouldManage) {
-            mManagedEntries.remove(entry);
-            return;
-        }
-
-        mManagedEntries.add(entry);
-
-        Runnable r = () -> {
-            if (mManagedEntries.contains(entry)) {
-                mManagedEntries.remove(entry);
-                if (mNotificationSafeToRemoveCallback != null) {
-                    mNotificationSafeToRemoveCallback.onSafeToRemove(entry.getKey());
-                }
-            }
-        };
-        long delayAmt = MIN_FGS_TIME_MS
-                - (mSystemClock.uptimeMillis() - entry.getCreationTime());
-        mHandler.postDelayed(r, delayAmt);
-    }
-}
-
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index d6cb114..04e26d3 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -50,7 +50,6 @@
             ForegroundServiceController foregroundServiceController,
             NotificationEntryManager notificationEntryManager,
             NotifPipeline notifPipeline,
-            ForegroundServiceLifetimeExtender fgsLifetimeExtender,
             SystemClock systemClock) {
         mContext = context;
         mForegroundServiceController = foregroundServiceController;
@@ -78,7 +77,6 @@
                 removeNotification(entry.getSbn());
             }
         });
-        mEntryManager.addNotificationLifetimeExtender(fgsLifetimeExtender);
 
         notifPipeline.addCollectionListener(new NotifCollectionListener() {
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index c2f80d4..033a355 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -35,11 +35,12 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.MathUtils;
-import android.util.TypedValue;
 import android.view.Surface;
 import android.view.View;
 import android.view.ViewTreeObserver;
 
+import com.android.internal.graphics.ColorUtils;
+import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.doze.DozeReceiver;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -157,16 +158,16 @@
 
     @Override
     public void dozeTimeTick() {
-        updateAodPosition();
+        updateAodPositionAndColor();
     }
 
     @Override
     public void onDozeAmountChanged(float linear, float eased) {
         mInterpolatedDarkAmount = eased;
-        updateAodPosition();
+        updateAodPositionAndColor();
     }
 
-    private void updateAodPosition() {
+    private void updateAodPositionAndColor() {
         mBurnInOffsetX = MathUtils.lerp(0f,
                 getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */)
                         - mMaxBurnInOffsetX,
@@ -175,6 +176,7 @@
                 getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */)
                         - 0.5f * mMaxBurnInOffsetY,
                 mInterpolatedDarkAmount);
+        updateColor();
         postInvalidate();
     }
 
@@ -231,21 +233,23 @@
         Log.v(TAG, "onAttachedToWindow");
 
         // Retrieve the colors each time, since it depends on day/night mode
-        final TypedValue tv = new TypedValue();
-        mContext.getTheme().resolveAttribute(R.attr.wallpaperTextColor, tv, true);
-        final int authIconColor = mContext.getResources()
-                .getColor(tv.resourceId, mContext.getTheme());
-        final int enrollIconColor = mContext.getColor(R.color.udfps_enroll_icon);
-
-        if (mShowReason == IUdfpsOverlayController.REASON_AUTH) {
-            mFingerprintDrawable.setTint(authIconColor);
-        } else if (mShowReason == IUdfpsOverlayController.REASON_ENROLL) {
-            mFingerprintDrawable.setTint(enrollIconColor);
-        }
+        updateColor();
 
         getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener);
     }
 
+    private void updateColor() {
+        if (mShowReason == IUdfpsOverlayController.REASON_AUTH) {
+            final int lockScreenIconColor = Utils.getColorAttrDefaultColor(mContext,
+                    com.android.systemui.R.attr.wallpaperTextColor);
+            final int ambientDisplayIconColor = Color.WHITE;
+            mFingerprintDrawable.setTint(ColorUtils.blendARGB(lockScreenIconColor,
+                    ambientDisplayIconColor, mInterpolatedDarkAmount));
+        } else if (mShowReason == IUdfpsOverlayController.REASON_ENROLL) {
+            mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon));
+        }
+    }
+
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 0fdaae8..5c8c9f2 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -96,7 +96,9 @@
      */
     private void updateAnimateScreenOff() {
         if (mCanAnimateTransition) {
-            final boolean controlScreenOff = mDozeParameters.getAlwaysOn() && mKeyguardShowing
+            final boolean controlScreenOff =
+                    mDozeParameters.getAlwaysOn()
+                    && (mKeyguardShowing || mDozeParameters.shouldControlUnlockedScreenOff())
                     && !mHost.isPowerSaveActive();
             mDozeParameters.setControlScreenOffAnimation(controlScreenOff);
             mHost.setAnimateScreenOff(controlScreenOff);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java
index f527775..5019e65 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java
@@ -18,6 +18,7 @@
 
 import android.os.Handler;
 import android.os.Message;
+import android.os.PowerManager;
 
 import com.android.systemui.dagger.SysUISingleton;
 
@@ -53,6 +54,17 @@
         mHandler.obtainMessage(what).sendToTarget();
     }
 
+    /**
+     * @param what Message to send.
+     * @param pmReason Reason this message was triggered - this should be a value from either
+     * {@link PowerManager.WakeReason} or {@link PowerManager.GoToSleepReason}.
+     */
+    void dispatch(int what, int pmReason) {
+        final Message message = mHandler.obtainMessage(what);
+        message.arg1 = pmReason;
+        message.sendToTarget();
+    }
+
     private Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
@@ -70,13 +82,13 @@
                     mScreenLifecycle.dispatchScreenTurnedOff();
                     break;
                 case STARTED_WAKING_UP:
-                    mWakefulnessLifecycle.dispatchStartedWakingUp();
+                    mWakefulnessLifecycle.dispatchStartedWakingUp(msg.arg1 /* pmReason */);
                     break;
                 case FINISHED_WAKING_UP:
                     mWakefulnessLifecycle.dispatchFinishedWakingUp();
                     break;
                 case STARTED_GOING_TO_SLEEP:
-                    mWakefulnessLifecycle.dispatchStartedGoingToSleep();
+                    mWakefulnessLifecycle.dispatchStartedGoingToSleep(msg.arg1 /* pmReason */);
                     break;
                 case FINISHED_GOING_TO_SLEEP:
                     mWakefulnessLifecycle.dispatchFinishedGoingToSleep();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index cb83656..1b033e9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -24,9 +24,11 @@
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.IBinder;
+import android.os.PowerManager;
 import android.os.Process;
 import android.os.Trace;
 import android.util.Log;
+import android.view.WindowManagerPolicyConstants;
 
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.IKeyguardDrawnCallback;
@@ -117,27 +119,32 @@
         }
 
         @Override // Binder interface
-        public void onStartedGoingToSleep(int reason) {
+        public void onStartedGoingToSleep(@PowerManager.GoToSleepReason int pmSleepReason) {
             checkPermission();
-            mKeyguardViewMediator.onStartedGoingToSleep(reason);
+            mKeyguardViewMediator.onStartedGoingToSleep(
+                    WindowManagerPolicyConstants.translateSleepReasonToOffReason(pmSleepReason));
             mKeyguardLifecyclesDispatcher.dispatch(
-                    KeyguardLifecyclesDispatcher.STARTED_GOING_TO_SLEEP);
+                    KeyguardLifecyclesDispatcher.STARTED_GOING_TO_SLEEP, pmSleepReason);
         }
 
         @Override // Binder interface
-        public void onFinishedGoingToSleep(int reason, boolean cameraGestureTriggered) {
+        public void onFinishedGoingToSleep(
+                @PowerManager.GoToSleepReason int pmSleepReason, boolean cameraGestureTriggered) {
             checkPermission();
-            mKeyguardViewMediator.onFinishedGoingToSleep(reason, cameraGestureTriggered);
+            mKeyguardViewMediator.onFinishedGoingToSleep(
+                    WindowManagerPolicyConstants.translateSleepReasonToOffReason(pmSleepReason),
+                    cameraGestureTriggered);
             mKeyguardLifecyclesDispatcher.dispatch(
                     KeyguardLifecyclesDispatcher.FINISHED_GOING_TO_SLEEP);
         }
 
         @Override // Binder interface
-        public void onStartedWakingUp() {
+        public void onStartedWakingUp(@PowerManager.WakeReason int pmWakeReason) {
             Trace.beginSection("KeyguardService.mBinder#onStartedWakingUp");
             checkPermission();
             mKeyguardViewMediator.onStartedWakingUp();
-            mKeyguardLifecyclesDispatcher.dispatch(KeyguardLifecyclesDispatcher.STARTED_WAKING_UP);
+            mKeyguardLifecyclesDispatcher.dispatch(
+                    KeyguardLifecyclesDispatcher.STARTED_WAKING_UP, pmWakeReason);
             Trace.endSection();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index f79b991..e732669 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -85,17 +85,17 @@
 import com.android.keyguard.KeyguardViewController;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.Dumpable;
-import com.android.systemui.R;
 import com.android.systemui.SystemUI;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.KeyguardService;
 import com.android.systemui.keyguard.dagger.KeyguardModule;
 import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -149,7 +149,8 @@
  * directly to the keyguard UI is posted to a {@link android.os.Handler} to ensure it is taken on the UI
  * thread of the keyguard.
  */
-public class KeyguardViewMediator extends SystemUI implements Dumpable {
+public class KeyguardViewMediator extends SystemUI implements Dumpable,
+        StatusBarStateController.StateListener {
     private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
     private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;
 
@@ -223,6 +224,7 @@
     private boolean mBootSendUserPresent;
     private boolean mShuttingDown;
     private boolean mDozing;
+    private boolean mAnimatingScreenOff;
     private final FalsingCollector mFalsingCollector;
 
     /** High level access to the power manager for WakeLocks */
@@ -709,6 +711,7 @@
     };
 
     private DeviceConfigProxy mDeviceConfig;
+    private DozeParameters mDozeParameters;
 
     /**
      * Injected constructor. See {@link KeyguardModule}.
@@ -725,7 +728,9 @@
             TrustManager trustManager,
             DeviceConfigProxy deviceConfig,
             NavigationModeController navigationModeController,
-            KeyguardDisplayManager keyguardDisplayManager) {
+            KeyguardDisplayManager keyguardDisplayManager,
+            DozeParameters dozeParameters,
+            StatusBarStateController statusBarStateController) {
         super(context);
         mFalsingCollector = falsingCollector;
         mLockPatternUtils = lockPatternUtils;
@@ -751,6 +756,8 @@
                 QuickStepContract.isGesturalMode(navigationModeController.addListener(mode -> {
                     mInGestureNavigationMode = QuickStepContract.isGesturalMode(mode);
                 }));
+        mDozeParameters = dozeParameters;
+        statusBarStateController.addCallback(this);
     }
 
     public void userActivity() {
@@ -863,11 +870,11 @@
 
     /**
      * Called to let us know the screen was turned off.
-     * @param why either {@link WindowManagerPolicyConstants#OFF_BECAUSE_OF_USER} or
-     *   {@link WindowManagerPolicyConstants#OFF_BECAUSE_OF_TIMEOUT}.
+     * @param offReason either {@link WindowManagerPolicyConstants#OFF_BECAUSE_OF_USER} or
+     * {@link WindowManagerPolicyConstants#OFF_BECAUSE_OF_TIMEOUT}.
      */
-    public void onStartedGoingToSleep(int why) {
-        if (DEBUG) Log.d(TAG, "onStartedGoingToSleep(" + why + ")");
+    public void onStartedGoingToSleep(@WindowManagerPolicyConstants.OffReason int offReason) {
+        if (DEBUG) Log.d(TAG, "onStartedGoingToSleep(" + offReason + ")");
         synchronized (this) {
             mDeviceInteractive = false;
             mGoingToSleep = true;
@@ -900,8 +907,11 @@
                 }
             } else if (mShowing) {
                 mPendingReset = true;
-            } else if ((why == WindowManagerPolicyConstants.OFF_BECAUSE_OF_TIMEOUT && timeout > 0)
-                    || (why == WindowManagerPolicyConstants.OFF_BECAUSE_OF_USER && !lockImmediately)) {
+            } else if (
+                    (offReason == WindowManagerPolicyConstants.OFF_BECAUSE_OF_TIMEOUT
+                            && timeout > 0)
+                            || (offReason == WindowManagerPolicyConstants.OFF_BECAUSE_OF_USER
+                            && !lockImmediately)) {
                 doKeyguardLaterLocked(timeout);
                 mLockLater = true;
             } else if (!mLockPatternUtils.isLockScreenDisabled(currentUser)) {
@@ -912,16 +922,23 @@
                 playSounds(true);
             }
         }
-        mUpdateMonitor.dispatchStartedGoingToSleep(why);
+        mUpdateMonitor.dispatchStartedGoingToSleep(offReason);
         notifyStartedGoingToSleep();
     }
 
-    public void onFinishedGoingToSleep(int why, boolean cameraGestureTriggered) {
-        if (DEBUG) Log.d(TAG, "onFinishedGoingToSleep(" + why + ")");
+    /**
+     * Called to let us know the screen finished turning off.
+     * @param offReason either {@link WindowManagerPolicyConstants#OFF_BECAUSE_OF_USER} or
+     * {@link WindowManagerPolicyConstants#OFF_BECAUSE_OF_TIMEOUT}.
+     */
+    public void onFinishedGoingToSleep(
+            @WindowManagerPolicyConstants.OffReason int offReason, boolean cameraGestureTriggered) {
+        if (DEBUG) Log.d(TAG, "onFinishedGoingToSleep(" + offReason + ")");
         synchronized (this) {
             mDeviceInteractive = false;
             mGoingToSleep = false;
             mWakeAndUnlocking = false;
+            mAnimatingScreenOff = mDozeParameters.shouldControlUnlockedScreenOff();
 
             resetKeyguardDonePendingLocked();
             mHideAnimationRun = false;
@@ -957,7 +974,7 @@
             }
 
         }
-        mUpdateMonitor.dispatchFinishedGoingToSleep(why);
+        mUpdateMonitor.dispatchFinishedGoingToSleep(offReason);
     }
 
     private boolean isKeyguardServiceEnabled() {
@@ -1074,6 +1091,7 @@
         // TODO: Rename all screen off/on references to interactive/sleeping
         synchronized (this) {
             mDeviceInteractive = true;
+            mAnimatingScreenOff = false;
             cancelDoKeyguardLaterLocked();
             cancelDoKeyguardForChildProfilesLocked();
             if (DEBUG) Log.d(TAG, "onStartedWakingUp, seq = " + mDelayedShowingSequence);
@@ -1287,6 +1305,10 @@
         return mHiding;
     }
 
+    public boolean isAnimatingScreenOff() {
+        return mAnimatingScreenOff;
+    }
+
     /**
      * Handles SET_OCCLUDED message sent by setOccluded()
      */
@@ -2259,6 +2281,16 @@
         setShowingLocked(mShowing);
     }
 
+    @Override
+    public void onDozeAmountChanged(float linear, float interpolated) {
+        // If we were animating the screen off, and we've completed the doze animation (doze amount
+        // is 1f), then show the activity lock screen.
+        if (mAnimatingScreenOff && mDozing && linear == 1f) {
+            mAnimatingScreenOff = false;
+            setShowingLocked(mShowing);
+        }
+    }
+
     /**
      * @param pulsing true when device temporarily wakes up to display an incoming notification.
      */
@@ -2289,7 +2321,14 @@
         mAodShowing = aodShowing;
         if (notifyDefaultDisplayCallbacks) {
             notifyDefaultDisplayCallbacks(showing);
-            updateActivityLockScreenState(showing, aodShowing);
+
+            if (!showing || !mAnimatingScreenOff) {
+                // Update the activity lock screen state unless we're animating in the keyguard
+                // for a screen off animation. In that case, we want the activity to remain visible
+                // until the animation completes. setShowingLocked is called again when the
+                // animation ends, so the activity lock screen will be shown at that time.
+                updateActivityLockScreenState(showing, aodShowing);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
index 5161deb..de00d50 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard;
 
 import android.annotation.IntDef;
+import android.os.PowerManager;
 import android.os.Trace;
 
 import com.android.systemui.Dumpable;
@@ -51,6 +52,9 @@
     public static final int WAKEFULNESS_GOING_TO_SLEEP = 3;
 
     private int mWakefulness = WAKEFULNESS_ASLEEP;
+    private @PowerManager.WakeReason int mLastWakeReason = PowerManager.WAKE_REASON_UNKNOWN;
+    private @PowerManager.GoToSleepReason int mLastSleepReason =
+            PowerManager.GO_TO_SLEEP_REASON_MIN;
 
     @Inject
     public WakefulnessLifecycle() {
@@ -60,11 +64,27 @@
         return mWakefulness;
     }
 
-    public void dispatchStartedWakingUp() {
+    /**
+     * Returns the most recent reason the device woke up. This is one of PowerManager.WAKE_REASON_*.
+     */
+    public @PowerManager.WakeReason int getLastWakeReason() {
+        return mLastWakeReason;
+    }
+
+    /**
+     * Returns the most recent reason the device went to sleep up. This is one of
+     * PowerManager.GO_TO_SLEEP_REASON_*.
+     */
+    public @PowerManager.GoToSleepReason int getLastSleepReason() {
+        return mLastSleepReason;
+    }
+
+    public void dispatchStartedWakingUp(@PowerManager.WakeReason int pmWakeReason) {
         if (getWakefulness() == WAKEFULNESS_WAKING) {
             return;
         }
         setWakefulness(WAKEFULNESS_WAKING);
+        mLastWakeReason = pmWakeReason;
         dispatch(Observer::onStartedWakingUp);
     }
 
@@ -76,11 +96,12 @@
         dispatch(Observer::onFinishedWakingUp);
     }
 
-    public void dispatchStartedGoingToSleep() {
+    public void dispatchStartedGoingToSleep(@PowerManager.GoToSleepReason int pmSleepReason) {
         if (getWakefulness() == WAKEFULNESS_GOING_TO_SLEEP) {
             return;
         }
         setWakefulness(WAKEFULNESS_GOING_TO_SLEEP);
+        mLastSleepReason = pmSleepReason;
         dispatch(Observer::onStartedGoingToSleep);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 626abfc..76281d8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -43,6 +43,7 @@
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardLiftController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.util.DeviceConfigProxy;
@@ -82,7 +83,9 @@
             @UiBackground Executor uiBgExecutor,
             DeviceConfigProxy deviceConfig,
             NavigationModeController navigationModeController,
-            KeyguardDisplayManager keyguardDisplayManager) {
+            KeyguardDisplayManager keyguardDisplayManager,
+            DozeParameters dozeParameters,
+            StatusBarStateController statusBarStateController) {
         return new KeyguardViewMediator(
                 context,
                 falsingCollector,
@@ -97,7 +100,9 @@
                 trustManager,
                 deviceConfig,
                 navigationModeController,
-                keyguardDisplayManager
+                keyguardDisplayManager,
+                dozeParameters,
+                statusBarStateController
         );
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index b2c2aa3..1e04516 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -46,7 +46,7 @@
 class PrivacyItemController @Inject constructor(
     private val appOpsController: AppOpsController,
     @Main uiExecutor: DelayableExecutor,
-    @Background private val bgExecutor: Executor,
+    @Background private val bgExecutor: DelayableExecutor,
     private val deviceConfigProxy: DeviceConfigProxy,
     private val userTracker: UserTracker,
     private val logger: PrivacyLogger,
@@ -75,6 +75,7 @@
         private const val DEFAULT_ALL_INDICATORS = false
         private const val DEFAULT_MIC_CAMERA = true
         private const val DEFAULT_LOCATION = false
+        const val TIME_TO_HOLD_INDICATORS = 5000L
     }
 
     @VisibleForTesting
@@ -87,9 +88,9 @@
                 ALL_INDICATORS, DEFAULT_ALL_INDICATORS)
     }
 
-    // TODO(b/168209929) Remove hardcode
     private fun isMicCameraEnabled(): Boolean {
-        return true
+        return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+                MIC_CAMERA, DEFAULT_MIC_CAMERA)
     }
 
     private fun isLocationEnabled(): Boolean {
@@ -101,6 +102,8 @@
     private var listening = false
     private val callbacks = mutableListOf<WeakReference<Callback>>()
     private val internalUiExecutor = MyExecutor(uiExecutor)
+    private var holdingIndicators = false
+    private var holdIndicatorsCancelled: Runnable? = null
 
     private val notifyChanges = Runnable {
         val list = privacyList
@@ -112,6 +115,11 @@
         uiExecutor.execute(notifyChanges)
     }
 
+    private val stopHoldingAndNotifyChanges = Runnable {
+        updatePrivacyList(true)
+        uiExecutor.execute(notifyChanges)
+    }
+
     var allIndicatorsAvailable = isAllIndicatorsEnabled()
         private set
     var micCameraAvailable = isMicCameraEnabled()
@@ -132,11 +140,12 @@
                             DEFAULT_ALL_INDICATORS)
                     callbacks.forEach { it.get()?.onFlagAllChanged(allIndicatorsAvailable) }
                 }
-                // TODO(b/168209929) Uncomment
-//                if (properties.keyset.contains(MIC_CAMERA)) {
-//                    micCameraAvailable = properties.getBoolean(MIC_CAMERA, DEFAULT_MIC_CAMERA)
-//                    callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) }
-//                }
+
+                if (properties.keyset.contains(MIC_CAMERA)) {
+                    micCameraAvailable = properties.getBoolean(MIC_CAMERA, DEFAULT_MIC_CAMERA)
+                    callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) }
+                }
+
                 if (properties.keyset.contains(LOCATION)) {
                     locationAvailable = properties.getBoolean(LOCATION, DEFAULT_LOCATION)
                     callbacks.forEach { it.get()?.onFlagLocationChanged(locationAvailable) }
@@ -193,6 +202,14 @@
         userTracker.addCallback(userTrackerCallback, bgExecutor)
     }
 
+    private fun setHoldTimer() {
+        holdIndicatorsCancelled?.run()
+        holdingIndicators = true
+        holdIndicatorsCancelled = bgExecutor.executeDelayed({
+            stopHoldingAndNotifyChanges.run()
+        }, TIME_TO_HOLD_INDICATORS)
+    }
+
     private fun update(updateUsers: Boolean) {
         bgExecutor.execute {
             if (updateUsers) {
@@ -257,9 +274,14 @@
         removeCallback(WeakReference(callback))
     }
 
-    private fun updatePrivacyList() {
+    private fun updatePrivacyList(stopHolding: Boolean = false) {
         if (!listening) {
             privacyList = emptyList()
+            if (holdingIndicators) {
+                holdIndicatorsCancelled?.run()
+                logger.cancelIndicatorsHold()
+                holdingIndicators = false
+            }
             return
         }
         val list = appOpsController.getActiveAppOpsForUser(UserHandle.USER_ALL).filter {
@@ -267,9 +289,43 @@
                     it.code == AppOpsManager.OP_PHONE_CALL_MICROPHONE ||
                     it.code == AppOpsManager.OP_PHONE_CALL_CAMERA
         }.mapNotNull { toPrivacyItem(it) }.distinct()
-        logger.logUpdatedPrivacyItemsList(
-                list.joinToString(separator = ", ", transform = PrivacyItem::toLog))
-        privacyList = list
+        processNewList(list, stopHolding)
+    }
+
+    /**
+     * The controller will only go from indicators to no indicators (and notify its listeners), if
+     * [TIME_TO_HOLD_INDICATORS] has passed since it received an empty list from [AppOpsController].
+     *
+     * If holding the last list (in the [TIME_TO_HOLD_INDICATORS] period) and a new non-empty list
+     * is retrieved from [AppOpsController], it will stop holding and notify about the new list.
+     */
+    private fun processNewList(list: List<PrivacyItem>, stopHolding: Boolean) {
+        if (list.isNotEmpty()) {
+            // The new elements is not empty, so regardless of whether we are holding or not, we
+            // clear the holding flag and cancel the delayed runnable.
+            if (holdingIndicators) {
+                holdIndicatorsCancelled?.run()
+                logger.cancelIndicatorsHold()
+                holdingIndicators = false
+            }
+            logger.logUpdatedPrivacyItemsList(
+                    list.joinToString(separator = ", ", transform = PrivacyItem::toLog))
+            privacyList = list
+        } else if (holdingIndicators && stopHolding) {
+            // We are holding indicators, received an empty list and were told to stop holding.
+            logger.finishIndicatorsHold()
+            logger.logUpdatedPrivacyItemsList("")
+            holdingIndicators = false
+            privacyList = list
+        } else if (holdingIndicators && !stopHolding) {
+            // Empty list while we are holding. Ignore
+        } else if (!holdingIndicators && privacyList.isNotEmpty()) {
+            // We are not holding, we were showing some indicators but now we should show nothing.
+            // Start holding.
+            logger.startIndicatorsHold(TIME_TO_HOLD_INDICATORS)
+            setHoldTimer()
+        }
+        // Else. We are not holding, we were not showing anything and the new list is empty. Ignore.
     }
 
     private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
index c88676e..f3b8d2e 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
@@ -47,6 +47,26 @@
         })
     }
 
+    fun startIndicatorsHold(time: Long) {
+        log(LogLevel.DEBUG, {
+            int1 = time.toInt() / 1000
+        }, {
+            "Starting privacy indicators hold for $int1 seconds"
+        })
+    }
+
+    fun cancelIndicatorsHold() {
+        log(LogLevel.VERBOSE, {}, {
+            "Cancel privacy indicators hold"
+        })
+    }
+
+    fun finishIndicatorsHold() {
+        log(LogLevel.DEBUG, {}, {
+            "Finish privacy indicators hold"
+        })
+    }
+
     fun logCurrentProfilesChanged(profiles: List<Int>) {
         log(LogLevel.INFO, {
             str1 = profiles.toString()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 16e9590..e8976d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -67,6 +67,7 @@
 
     private int mSideMargins;
     private boolean mQsDisabled;
+    private boolean mBackgroundVisible;
     private int mContentPadding = -1;
     private boolean mAnimateBottomOnNextLayout;
 
@@ -122,6 +123,14 @@
         return true;
     }
 
+    /**
+     * If QS should have a solid or transparent background.
+     */
+    public void setBackgroundVisible(boolean visible) {
+        mBackgroundVisible = visible;
+        updateBackgroundVisibility();
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         // QSPanel will show as many rows as it can (up to TileLayout.MAX_ROWS) such that the
@@ -174,7 +183,11 @@
         final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
         if (disabled == mQsDisabled) return;
         mQsDisabled = disabled;
-        mBackground.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
+        updateBackgroundVisibility();
+    }
+
+    private void updateBackgroundVisibility() {
+        mBackground.setVisibility(mQsDisabled || !mBackgroundVisible ? GONE : VISIBLE);
     }
 
     void updateResources(QSPanelController qsPanelController,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index dbdd04a..ed308ae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -44,6 +44,7 @@
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.dagger.QSFragmentComponent;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
@@ -105,6 +106,7 @@
     private QSPanelController mQSPanelController;
     private QuickQSPanelController mQuickQSPanelController;
     private QSCustomizerController mQSCustomizerController;
+    private FeatureFlags mFeatureFlags;
 
     @Inject
     public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
@@ -112,7 +114,7 @@
             StatusBarStateController statusBarStateController, CommandQueue commandQueue,
             QSDetailDisplayer qsDetailDisplayer, @Named(QS_PANEL) MediaHost qsMediaHost,
             @Named(QUICK_QS_PANEL) MediaHost qqsMediaHost,
-            QSFragmentComponent.Factory qsComponentFactory) {
+            QSFragmentComponent.Factory qsComponentFactory, FeatureFlags featureFlags) {
         mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
         mInjectionInflater = injectionInflater;
         mCommandQueue = commandQueue;
@@ -122,6 +124,7 @@
         mQsComponentFactory = qsComponentFactory;
         commandQueue.observe(getLifecycle(), this);
         mHost = qsTileHost;
+        mFeatureFlags = featureFlags;
         mStatusBarStateController = statusBarStateController;
     }
 
@@ -163,6 +166,7 @@
         mQSContainerImplController = qsFragmentComponent.getQSContainerImplController();
         mQSContainerImplController.init();
         mContainer = mQSContainerImplController.getView();
+        mContainer.setBackgroundVisible(!mFeatureFlags.isShadeOpaque());
 
         mQSDetail.setQsPanel(mQSPanelController, mHeader, mFooter);
         mQSAnimator = qsFragmentComponent.getQSAnimator();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
index f77431a..8fc2830 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
@@ -35,6 +35,8 @@
 import androidx.concurrent.futures.CallbackToFutureAdapter;
 import androidx.exifinterface.media.ExifInterface;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import com.google.common.util.concurrent.ListenableFuture;
 
 import java.io.File;
@@ -45,6 +47,7 @@
 import java.time.Instant;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
+import java.util.UUID;
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
@@ -72,7 +75,7 @@
     private static final String EXIF_WRITE_EXCEPTION =
             "ExifInterface threw an exception writing to the file descriptor.";
     private static final String RESOLVER_UPDATE_ZERO_ROWS =
-            "Failed to publishEntry. ContentResolver#update reported no rows updated.";
+            "Failed to publish entry. ContentResolver#update reported no rows updated.";
     private static final String IMAGE_COMPRESS_RETURNED_FALSE =
             "Bitmap.compress returned false. (Failure unknown)";
 
@@ -114,8 +117,8 @@
      *
      * @return a listenable future result
      */
-    ListenableFuture<Uri> export(Executor executor, Bitmap bitmap) {
-        return export(executor, bitmap, ZonedDateTime.now());
+    ListenableFuture<Result> export(Executor executor, UUID requestId, Bitmap bitmap) {
+        return export(executor, requestId, bitmap, ZonedDateTime.now());
     }
 
     /**
@@ -126,8 +129,10 @@
      *
      * @return a listenable future result
      */
-    ListenableFuture<Uri> export(Executor executor, Bitmap bitmap, ZonedDateTime captureTime) {
-        final Task task = new Task(mResolver, bitmap, captureTime, mCompressFormat, mQuality);
+    ListenableFuture<Result> export(Executor executor, UUID requestId, Bitmap bitmap,
+            ZonedDateTime captureTime) {
+        final Task task =
+                new Task(mResolver, requestId, bitmap, captureTime, mCompressFormat, mQuality);
         return CallbackToFutureAdapter.getFuture(
                 (completer) -> {
                     executor.execute(() -> {
@@ -142,42 +147,62 @@
         );
     }
 
+    static class Result {
+        UUID requestId;
+        String fileName;
+        long timestamp;
+        Uri uri;
+        CompressFormat format;
+    }
+
     private static class Task {
         private final ContentResolver mResolver;
+        private final UUID mRequestId;
+        private final Bitmap mBitmap;
         private final ZonedDateTime mCaptureTime;
         private final CompressFormat mFormat;
         private final int mQuality;
-        private final Bitmap mBitmap;
+        private final String mFileName;
 
-        Task(ContentResolver resolver, Bitmap bitmap, ZonedDateTime captureTime,
+        Task(ContentResolver resolver, UUID requestId, Bitmap bitmap, ZonedDateTime captureTime,
                 CompressFormat format, int quality) {
             mResolver = resolver;
+            mRequestId = requestId;
             mBitmap = bitmap;
             mCaptureTime = captureTime;
             mFormat = format;
             mQuality = quality;
+            mFileName = createFilename(mCaptureTime, mFormat);
         }
 
-        public Uri execute() throws ImageExportException, InterruptedException {
+        public Result execute() throws ImageExportException, InterruptedException {
             Trace.beginSection("ImageExporter_execute");
             Uri uri = null;
             Instant start = null;
+            Result result = new Result();
             try {
                 if (LogConfig.DEBUG_STORAGE) {
                     Log.d(TAG, "image export started");
                     start = Instant.now();
                 }
-                uri = createEntry(mFormat, mCaptureTime);
+
+                uri = createEntry(mFormat, mCaptureTime, mFileName);
                 throwIfInterrupted();
 
                 writeImage(mBitmap, mFormat, mQuality, uri);
                 throwIfInterrupted();
 
-                writeExif(uri, mBitmap.getWidth(), mBitmap.getHeight(), mCaptureTime);
+                writeExif(uri, mRequestId, mBitmap.getWidth(), mBitmap.getHeight(), mCaptureTime);
                 throwIfInterrupted();
 
                 publishEntry(uri);
 
+                result.timestamp = mCaptureTime.toInstant().toEpochMilli();
+                result.requestId = mRequestId;
+                result.uri = uri;
+                result.fileName = mFileName;
+                result.format = mFormat;
+
                 if (LogConfig.DEBUG_STORAGE) {
                     Log.d(TAG, "image export completed: "
                             + Duration.between(start, Instant.now()).toMillis() + " ms");
@@ -190,13 +215,15 @@
             } finally {
                 Trace.endSection();
             }
-            return uri;
+            return result;
         }
 
-        Uri createEntry(CompressFormat format, ZonedDateTime time) throws ImageExportException {
+        Uri createEntry(CompressFormat format, ZonedDateTime time, String fileName)
+                throws ImageExportException {
             Trace.beginSection("ImageExporter_createEntry");
             try {
-                final ContentValues values = createMetadata(time, format);
+                final ContentValues values = createMetadata(time, format, fileName);
+
                 Uri uri = mResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
                 if (uri == null) {
                     throw new ImageExportException(RESOLVER_INSERT_RETURNED_NULL);
@@ -225,7 +252,7 @@
             }
         }
 
-        void writeExif(Uri uri, int width, int height, ZonedDateTime captureTime)
+        void writeExif(Uri uri, UUID requestId, int width, int height, ZonedDateTime captureTime)
                 throws ImageExportException {
             Trace.beginSection("ImageExporter_writeExif");
             ParcelFileDescriptor pfd = null;
@@ -241,7 +268,7 @@
                     throw new ImageExportException(EXIF_READ_EXCEPTION, e);
                 }
 
-                updateExifAttributes(exif, width, height, captureTime);
+                updateExifAttributes(exif, requestId, width, height, captureTime);
                 try {
                     exif.saveAttributes();
                 } catch (IOException e) {
@@ -276,14 +303,16 @@
         }
     }
 
+    @VisibleForTesting
     static String createFilename(ZonedDateTime time, CompressFormat format) {
         return String.format(FILENAME_PATTERN, time, fileExtension(format));
     }
 
-    static ContentValues createMetadata(ZonedDateTime captureTime, CompressFormat format) {
+    static ContentValues createMetadata(ZonedDateTime captureTime, CompressFormat format,
+            String fileName) {
         ContentValues values = new ContentValues();
         values.put(MediaStore.MediaColumns.RELATIVE_PATH, SCREENSHOTS_PATH);
-        values.put(MediaStore.MediaColumns.DISPLAY_NAME, createFilename(captureTime, format));
+        values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
         values.put(MediaStore.MediaColumns.MIME_TYPE, getMimeType(format));
         values.put(MediaStore.MediaColumns.DATE_ADDED, captureTime.toEpochSecond());
         values.put(MediaStore.MediaColumns.DATE_MODIFIED, captureTime.toEpochSecond());
@@ -293,8 +322,10 @@
         return values;
     }
 
-    static void updateExifAttributes(ExifInterface exif, int width, int height,
+    static void updateExifAttributes(ExifInterface exif, UUID uniqueId, int width, int height,
             ZonedDateTime captureTime) {
+        exif.setAttribute(ExifInterface.TAG_IMAGE_UNIQUE_ID, uniqueId.toString());
+
         exif.setAttribute(ExifInterface.TAG_SOFTWARE, "Android " + Build.DISPLAY);
         exif.setAttribute(ExifInterface.TAG_IMAGE_WIDTH, Integer.toString(width));
         exif.setAttribute(ExifInterface.TAG_IMAGE_LENGTH, Integer.toString(height));
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 9dce191..0106f43 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -29,6 +29,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Icon;
@@ -49,8 +50,9 @@
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
 
+import com.google.common.util.concurrent.ListenableFuture;
+
 import java.text.DateFormat;
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
@@ -65,7 +67,6 @@
 class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
     private static final String TAG = logTag(SaveImageInBackgroundTask.class);
 
-    private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png";
     private static final String SCREENSHOT_ID_TEMPLATE = "Screenshot_%s";
     private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
 
@@ -73,14 +74,14 @@
     private final ScreenshotSmartActions mScreenshotSmartActions;
     private final ScreenshotController.SaveImageInBackgroundData mParams;
     private final ScreenshotController.SavedImageData mImageData;
-    private final String mImageFileName;
-    private final long mImageTime;
+
     private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
-    private final String mScreenshotId;
+    private String mScreenshotId;
     private final boolean mSmartActionsEnabled;
     private final Random mRandom = new Random();
     private final Supplier<ActionTransition> mSharedElementTransition;
     private final ImageExporter mImageExporter;
+    private long mImageTime;
 
     SaveImageInBackgroundTask(Context context, ImageExporter exporter,
             ScreenshotSmartActions screenshotSmartActions,
@@ -94,10 +95,6 @@
 
         // Prepare all the output metadata
         mParams = data;
-        mImageTime = System.currentTimeMillis();
-        String imageDate = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(mImageTime));
-        mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);
-        mScreenshotId = String.format(SCREENSHOT_ID_TEMPLATE, UUID.randomUUID());
 
         // Initialize screenshot notification smart actions provider.
         mSmartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
@@ -121,18 +118,26 @@
             }
             return null;
         }
+        // TODO: move to constructor / from ScreenshotRequest
+        final UUID requestId = UUID.randomUUID();
+        final UserHandle user = getUserHandleOfForegroundApplication(mContext);
+
         Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
 
         Bitmap image = mParams.image;
-
+        mScreenshotId = String.format(SCREENSHOT_ID_TEMPLATE, requestId);
         try {
             // Call synchronously here since already on a background thread.
-            Uri uri = mImageExporter.export(Runnable::run, image).get();
+            ListenableFuture<ImageExporter.Result> future =
+                    mImageExporter.export(Runnable::run, requestId, image);
+            ImageExporter.Result result = future.get();
+            final Uri uri = result.uri;
+            mImageTime = result.timestamp;
 
             CompletableFuture<List<Notification.Action>> smartActionsFuture =
                     mScreenshotSmartActions.getSmartActionsFuture(
                             mScreenshotId, uri, image, mSmartActionsProvider,
-                            mSmartActionsEnabled, getUserHandle(mContext));
+                            mSmartActionsEnabled, user);
 
             List<Notification.Action> smartActions = new ArrayList<>();
             if (mSmartActionsEnabled) {
@@ -336,22 +341,21 @@
         return deleteActionBuilder.build();
     }
 
-    private int getUserHandleOfForegroundApplication(Context context) {
+    private UserHandle getUserHandleOfForegroundApplication(Context context) {
+        UserManager manager = UserManager.get(context);
+        int result;
         // This logic matches
         // com.android.systemui.statusbar.phone.PhoneStatusBarPolicy#updateManagedProfile
         try {
-            return ActivityTaskManager.getService().getLastResumedActivityUserId();
+            result = ActivityTaskManager.getService().getLastResumedActivityUserId();
         } catch (RemoteException e) {
             if (DEBUG_ACTIONS) {
                 Log.d(TAG, "Failed to get UserHandle of foreground app: ", e);
             }
-            return context.getUserId();
+            result = context.getUserId();
         }
-    }
-
-    private UserHandle getUserHandle(Context context) {
-        UserManager manager = UserManager.get(context);
-        return manager.getUserInfo(getUserHandleOfForegroundApplication(context)).getUserHandle();
+        UserInfo userInfo = manager.getUserInfo(result);
+        return userInfo.getUserHandle();
     }
 
     private List<Notification.Action> buildSmartActions(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index d77d1ea..8801b25 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -55,6 +55,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.util.DisplayMetrics;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index c75efbc..54b1b2c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -29,6 +29,8 @@
 
 import com.google.common.util.concurrent.ListenableFuture;
 
+import java.time.ZonedDateTime;
+import java.util.UUID;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -52,6 +54,9 @@
     private final ImageExporter mImageExporter;
     private final ImageTileSet mImageTileSet;
 
+    private ZonedDateTime mCaptureTime;
+    private UUID mRequestId;
+
     public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor,
             Executor bgExecutor, ImageExporter exporter) {
         mContext = context;
@@ -68,6 +73,8 @@
      * @param after action to take after the flow is complete
      */
     public void run(final Runnable after) {
+        mCaptureTime = ZonedDateTime.now();
+        mRequestId = UUID.randomUUID();
         mConnection.start((session) -> startCapture(session, after));
     }
 
@@ -109,11 +116,12 @@
     void exportToFile(Bitmap bitmap, Session session, Runnable afterEnd) {
         mImageExporter.setFormat(Bitmap.CompressFormat.PNG);
         mImageExporter.setQuality(6);
-        ListenableFuture<Uri> future =
-                mImageExporter.export(mBgExecutor, bitmap);
+        ListenableFuture<ImageExporter.Result> future =
+                mImageExporter.export(mBgExecutor, mRequestId, bitmap, mCaptureTime);
         future.addListener(() -> {
             try {
-                launchViewer(future.get());
+                ImageExporter.Result result = future.get();
+                launchViewer(result.uri);
             } catch (InterruptedException | ExecutionException e) {
                 Toast.makeText(mContext, "Failed to write image", Toast.LENGTH_SHORT).show();
                 Log.e(TAG, "Error storing screenshot to media store", e.getCause());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index 964c499..d6db736 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -44,6 +44,10 @@
         return mFlagReader.isEnabled(R.bool.flag_notification_pipeline2_rendering);
     }
 
+    public boolean isShadeOpaque() {
+        return mFlagReader.isEnabled(R.bool.flag_shade_is_opaque);
+    }
+
     /** b/171917882 */
     public boolean isTwoColumnNotificationShadeEnabled() {
         return mFlagReader.isEnabled(R.bool.flag_notification_twocolumn);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 86377fb..2f0f90d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -86,6 +86,35 @@
     }
 }
 
+class PowerButtonReveal(
+    /** Approximate Y-value of the center of the power button on the physical device. */
+    val powerButtonY: Float
+) : LightRevealEffect {
+
+    private val OVAL_INITIAL_HEIGHT = 50f
+
+    private val WIDTH_INCREASE_MULTIPLIER = 1.25f
+
+    override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
+        val interpolatedAmount = Interpolators.FAST_OUT_SLOW_IN_REVERSE.getInterpolation(amount)
+        val fadeAmount =
+                LightRevealEffect.getPercentPastThreshold(interpolatedAmount, 0.5f)
+
+        with(scrim) {
+            revealGradientEndColorAlpha = 1f - fadeAmount
+            setRevealGradientBounds(
+                    width -
+                            width * WIDTH_INCREASE_MULTIPLIER * interpolatedAmount,
+                    powerButtonY - (OVAL_INITIAL_HEIGHT / 2f) -
+                            height * interpolatedAmount,
+                    width * WIDTH_INCREASE_MULTIPLIER +
+                            width * WIDTH_INCREASE_MULTIPLIER * interpolatedAmount,
+                    powerButtonY + (OVAL_INITIAL_HEIGHT / 2f) +
+                            height * interpolatedAmount)
+        }
+    }
+}
+
 /**
  * Scrim view that partially reveals the content underneath it using a [RadialGradient] with a
  * transparent center. The center position, size, and stops of the gradient can be manipulated to
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 20efa32..4a80572 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -205,6 +205,30 @@
     }
 
     /**
+     * @return whether to clip bottom of given view
+     */
+    private boolean shouldClipBottom(ExpandableView view) {
+        final boolean showShelf = ((ShelfState) getViewState()).hasItemsInStableShelf;
+        if (showShelf) {
+            if (mAmbientState.isShadeOpening()) {
+                final float viewEnd = view.getTranslationY()
+                        + view.getActualHeight()
+                        + mPaddingBetweenElements;
+                final float finalShelfStart = mMaxLayoutHeight - getIntrinsicHeight();
+                // While the shade is opening, only clip view if it overlaps with shelf;
+                // otherwise leave view unclipped.
+                if (viewEnd < finalShelfStart) {
+                    return false;
+                }
+            }
+            // Clip for scrolling.
+            return true;
+        }
+        // Don't clip since we have enough space to show all views.
+        return false;
+    }
+
+    /**
      * Update the shelf appearance based on the other notifications around it. This transforms
      * the icons from the notification area into the shelf.
      */
@@ -345,7 +369,9 @@
         clipTransientViews();
 
         setClipTopAmount(clipTopAmount);
-        boolean isHidden = getViewState().hidden || clipTopAmount >= getIntrinsicHeight();
+        boolean isHidden = getViewState().hidden
+                || clipTopAmount >= getIntrinsicHeight()
+                || mAmbientState.isShadeOpening();
         if (mShowNotificationShelf) {
             setVisibility(isHidden ? View.INVISIBLE : View.VISIBLE);
         }
@@ -468,7 +494,8 @@
         } else {
             shouldClipOwnTop = view.showingPulsing();
         }
-        if (viewEnd > notificationClipEnd && !shouldClipOwnTop
+        if (shouldClipBottom(view)
+                && viewEnd > notificationClipEnd && !shouldClipOwnTop
                 && (mAmbientState.isShadeExpanded() || !isPinned)) {
             int clipBottomAmount = (int) (viewEnd - notificationClipEnd);
             if (isPinned) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 1326d92..e391250 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -96,6 +96,8 @@
             }
         }
 
+    private var animatingScreenOff = false
+
     private var collapsedEnoughToHide: Boolean = false
 
     var pulsing: Boolean = false
@@ -232,9 +234,14 @@
     }
 
     override fun onDozeAmountChanged(linear: Float, eased: Float) {
-        if (updateDozeAmountIfBypass()) {
+        if (overrideDozeAmountIfBypass()) {
             return
         }
+
+        if (overrideDozeAmountIfAnimatingScreenOff(linear)) {
+            return
+        }
+
         if (linear != 1.0f && linear != 0.0f &&
             (mLinearDozeAmount == 0.0f || mLinearDozeAmount == 1.0f)) {
             // Let's notify the scroller that an animation started
@@ -257,7 +264,7 @@
     }
 
     override fun onStateChanged(newState: Int) {
-        updateDozeAmountIfBypass()
+        overrideDozeAmountIfBypass()
         if (bypassController.bypassEnabled &&
                 newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED &&
             (!statusBarStateController.isDozing || shouldAnimateVisibility())) {
@@ -265,6 +272,14 @@
             setNotificationsVisible(visible = true, increaseSpeed = false, animate = false)
             setNotificationsVisible(visible = false, increaseSpeed = false, animate = true)
         }
+
+        // If we want to control the screen off animation, check whether we are going from SHADE to
+        // KEYGUARD.
+        if (dozeParameters.shouldControlUnlockedScreenOff()) {
+            animatingScreenOff =
+                state == StatusBarState.SHADE && newState == StatusBarState.KEYGUARD
+        }
+
         this.state = newState
     }
 
@@ -280,7 +295,11 @@
         }
     }
 
-    private fun updateDozeAmountIfBypass(): Boolean {
+    /**
+     * @return Whether the doze amount was overridden because bypass is enabled. If true, the
+     * original doze amount should be ignored.
+     */
+    private fun overrideDozeAmountIfBypass(): Boolean {
         if (bypassController.bypassEnabled) {
             var amount = 1.0f
             if (statusBarStateController.state == StatusBarState.SHADE ||
@@ -293,6 +312,28 @@
         return false
     }
 
+    /**
+     * If we're playing the screen off animation, force the notification doze amount to be 1f (fully
+     * dozing). This is needed so that the notifications aren't briefly visible as the screen turns
+     * off and dozeAmount goes from 1f to 0f.
+     *
+     * @return Whether the doze amount was overridden because we are playing the screen off
+     * animation. If true, the original doze amount should be ignored.
+     */
+    private fun overrideDozeAmountIfAnimatingScreenOff(linearDozeAmount: Float): Boolean {
+        if (animatingScreenOff) {
+            if (linearDozeAmount == 1f) {
+                animatingScreenOff = false
+                return false
+            }
+
+            setDozeAmount(1f, 1f)
+            return true
+        }
+
+        return false
+    }
+
     private fun startVisibilityAnimation(increaseSpeed: Boolean) {
         if (mNotificationVisibleAmount == 0f || mNotificationVisibleAmount == 1f) {
             mVisibilityInterpolator = if (mNotificationsVisible)
@@ -345,6 +386,8 @@
     override fun onDozingChanged(isDozing: Boolean) {
         if (isDozing) {
             setNotificationsVisible(visible = false, animate = false, increaseSpeed = false)
+        } else {
+            animatingScreenOff = false
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 789e78e..bafa4a25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -938,6 +938,7 @@
 
     /** Whether or not the icon for this notification is visible in the shelf. */
     public void setShelfIconVisible(boolean shelfIconVisible) {
+        if (row == null) return;
         mShelfIconVisible = shelfIconVisible;
         updateShelfIconVisibility();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
index c7ac403..998ae9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
@@ -30,12 +30,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
-import java.util.HashMap;
-import java.util.Map;
-
 import javax.inject.Inject;
 
 /**
@@ -75,9 +71,6 @@
     public void attach(NotifPipeline pipeline) {
         mNotifPipeline = pipeline;
 
-        // extend the lifetime of foreground notification services to show for at least 5 seconds
-        mNotifPipeline.addNotificationLifetimeExtender(mForegroundLifetimeExtender);
-
         // filter out foreground service notifications that aren't necessary anymore
         mNotifPipeline.addPreGroupFilter(mNotifFilter);
 
@@ -119,64 +112,6 @@
     };
 
     /**
-     * Extends the lifetime of foreground notification services such that they show for at least
-     * five seconds
-     */
-    private final NotifLifetimeExtender mForegroundLifetimeExtender =
-            new NotifLifetimeExtender() {
-        private static final int MIN_FGS_TIME_MS = 5000;
-        private OnEndLifetimeExtensionCallback mEndCallback;
-        private Map<NotificationEntry, Runnable> mEndRunnables = new HashMap<>();
-
-        @Override
-        public String getName() {
-            return TAG;
-        }
-
-        @Override
-        public void setCallback(OnEndLifetimeExtensionCallback callback) {
-            mEndCallback = callback;
-        }
-
-        @Override
-        public boolean shouldExtendLifetime(NotificationEntry entry, int reason) {
-            if ((entry.getSbn().getNotification().flags
-                    & Notification.FLAG_FOREGROUND_SERVICE) == 0) {
-                return false;
-            }
-
-            final long currTime = System.currentTimeMillis();
-            final boolean extendLife = currTime - entry.getSbn().getPostTime() < MIN_FGS_TIME_MS;
-
-            if (extendLife) {
-                if (!mEndRunnables.containsKey(entry)) {
-                    final Runnable endExtensionRunnable = () -> {
-                        mEndRunnables.remove(entry);
-                        mEndCallback.onEndLifetimeExtension(
-                                mForegroundLifetimeExtender,
-                                entry);
-                    };
-
-                    final Runnable cancelRunnable = mMainExecutor.executeDelayed(
-                            endExtensionRunnable,
-                            MIN_FGS_TIME_MS - (currTime - entry.getSbn().getPostTime()));
-                    mEndRunnables.put(entry, cancelRunnable);
-                }
-            }
-
-            return extendLife;
-        }
-
-        @Override
-        public void cancelLifetimeExtension(NotificationEntry entry) {
-            Runnable cancelRunnable = mEndRunnables.remove(entry);
-            if (cancelRunnable != null) {
-                cancelRunnable.run();
-            }
-        }
-    };
-
-    /**
      * Puts foreground service notifications into its own section.
      */
     private final NotifSectioner mNotifSectioner = new NotifSectioner("ForegroundService") {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index ba03d01..92b381e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -614,6 +614,12 @@
         }
     }
 
+    public void setShouldFadeForShadeOpen(boolean shouldFade) {
+        if (!mViewState.gone) {
+            mViewState.setShouldFadeForShadeOpen(shouldFade);
+        }
+    }
+
     /**
      * @return whether the current view doesn't add height to the overall content. This means that
      * if it is added to a list of items, its content will still have the same height.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
index 628c4e2..21b6863 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
@@ -88,6 +88,12 @@
     public boolean hideSensitive;
     public boolean belowSpeedBump;
     public boolean inShelf;
+    public boolean shouldFadeForShadeOpen;
+
+    @Override
+    boolean shouldAnimateAlpha() {
+        return shouldFadeForShadeOpen;
+    }
 
     /**
      * A state indicating whether a headsup is currently fully visible, even when not scrolled.
@@ -171,6 +177,10 @@
         }
     }
 
+    public void setShouldFadeForShadeOpen(boolean shouldFade) {
+        shouldFadeForShadeOpen = shouldFade;
+    }
+
     @Override
     public void animateTo(View child, AnimationProperties properties) {
         super.animateTo(child, properties);
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 f07d874..a2ce9e1 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
@@ -3947,6 +3947,7 @@
         int numChildren = getChildCount();
         for (int i = 0; i < numChildren; i++) {
             ExpandableView child = (ExpandableView) getChildAt(i);
+            child.setShouldFadeForShadeOpen(mAmbientState.isShadeOpening());
             child.applyViewState();
         }
 
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 e6efba7..4bf7be3 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
@@ -568,11 +568,14 @@
             // Add padding before sections for overscroll effect.
             childViewState.yTranslation += ambientState.getSectionPadding();
         }
-        if (childViewState.yTranslation >= shelfStart) {
-            childViewState.hidden = !child.isExpandAnimationRunning() && !child.hasExpandingChild();
-            childViewState.inShelf = true;
-            childViewState.headsUpIsVisible = false;
-        }
+        boolean show = childViewState.yTranslation < shelfStart
+                && !ambientState.isAppearing();
+        childViewState.hidden = !show
+                && !child.isExpandAnimationRunning()
+                && !child.hasExpandingChild();
+        childViewState.inShelf = !show;
+        childViewState.headsUpIsVisible = show;
+        childViewState.alpha = show ? 1f : 0f;
     }
 
     protected int getMaxAllowedChildHeight(View child) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 66a48f1..43f1f43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -45,6 +45,7 @@
     public static final int ANIMATION_DURATION_CORNER_RADIUS = 200;
     public static final int ANIMATION_DURATION_WAKEUP = 500;
     public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448;
+    public static final int ANIMATION_DURATION_FADE_IN = 700;
     public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
     public static final int ANIMATION_DURATION_SWIPE = 260;
     public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
index 3da4e321..abe5c69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
+import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_FADE_IN;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -56,6 +58,16 @@
             return mAnimationFilter;
         }
     };
+
+    protected static final AnimationProperties ANIMATE_ALPHA = new AnimationProperties() {
+        AnimationFilter mAnimationFilter = new AnimationFilter();
+        @Override
+        public AnimationFilter getAnimationFilter() {
+            mAnimationFilter.animateAlpha();
+            return mAnimationFilter;
+        }
+    }.setDuration(ANIMATION_DURATION_FADE_IN);
+
     private static final int TAG_ANIMATOR_TRANSLATION_X = R.id.translation_x_animator_tag;
     private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
     private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
@@ -148,6 +160,10 @@
         scaleY = view.getScaleY();
     }
 
+    boolean shouldAnimateAlpha() {
+        return false;
+    }
+
     /**
      * Applies a {@link ViewState} to a normal view.
      */
@@ -200,24 +216,26 @@
         int oldVisibility = view.getVisibility();
         boolean becomesInvisible = this.alpha == 0.0f
                 || (this.hidden && (!isAnimating(view) || oldVisibility != View.VISIBLE));
-        boolean animatingAlpha = isAnimating(view, TAG_ANIMATOR_ALPHA);
-        if (animatingAlpha) {
-            updateAlphaAnimation(view);
+        if (isAnimating(view, TAG_ANIMATOR_ALPHA)) {
+            startAlphaAnimation(view, NO_NEW_ANIMATIONS);
         } else if (view.getAlpha() != this.alpha) {
-            // apply layer type
-            boolean becomesFullyVisible = this.alpha == 1.0f;
-            boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible
-                    && view.hasOverlappingRendering();
-            int layerType = view.getLayerType();
-            int newLayerType = newLayerTypeIsHardware
-                    ? View.LAYER_TYPE_HARDWARE
-                    : View.LAYER_TYPE_NONE;
-            if (layerType != newLayerType) {
-                view.setLayerType(newLayerType, null);
+            if (shouldAnimateAlpha()) {
+                startAlphaAnimation(view, ANIMATE_ALPHA);
+            } else {
+                // apply layer type
+                boolean becomesFullyVisible = this.alpha == 1.0f;
+                boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible
+                        && view.hasOverlappingRendering();
+                int layerType = view.getLayerType();
+                int newLayerType = newLayerTypeIsHardware
+                        ? View.LAYER_TYPE_HARDWARE
+                        : View.LAYER_TYPE_NONE;
+                if (layerType != newLayerType) {
+                    view.setLayerType(newLayerType, null);
+                }
+                // apply alpha
+                view.setAlpha(this.alpha);
             }
-
-            // apply alpha
-            view.setAlpha(this.alpha);
         }
 
         // apply visibility
@@ -322,10 +340,6 @@
         }
     }
 
-    private void updateAlphaAnimation(View view) {
-        startAlphaAnimation(view, NO_NEW_ANIMATIONS);
-    }
-
     private void startAlphaAnimation(final View child, AnimationProperties properties) {
         Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
         Float previousEndValue = getChildTag(child,TAG_END_ALPHA);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 6495144..8c2fa33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -194,6 +194,16 @@
         mPowerManager.setDozeAfterScreenOff(!controlScreenOffAnimation);
     }
 
+    /**
+     * Whether we want to control the screen off animation when the device is unlocked. If we do,
+     * we'll animate in AOD before turning off the screen, rather than simply fading to black and
+     * then abruptly showing AOD.
+     */
+    public boolean shouldControlUnlockedScreenOff() {
+        return getAlwaysOn() && SystemProperties.getBoolean(
+                "persist.sysui.show_new_screen_on_transitions", false);
+    }
+
     private boolean getBoolean(String propName, int resId) {
         return SystemProperties.getBoolean(propName, mResources.getBoolean(resId));
     }
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 1dfd1f3..57a64e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -196,13 +196,15 @@
     }
 
     public void run(Result result) {
-        final int y = getClockY(mPanelExpansion);
+        final int y = getClockY(mPanelExpansion, mDarkAmount);
         result.clockY = y;
+        result.clockYFullyDozing = getClockY(
+                1.0f /* panelExpansion */, 1.0f /* darkAmount */);
         result.clockAlpha = getClockAlpha(y);
         result.stackScrollerPadding = mBypassEnabled ? mUnlockedStackScrollerPadding
                 : y + mKeyguardStatusHeight;
         result.stackScrollerPaddingExpanded = mBypassEnabled ? mUnlockedStackScrollerPadding
-                : getClockY(1.0f) + mKeyguardStatusHeight;
+                : getClockY(1.0f, mDarkAmount) + mKeyguardStatusHeight;
         result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount);
         result.clockScale = interpolate(getBurnInScale(), 1.0f, 1.0f - mDarkAmount);
     }
@@ -259,7 +261,7 @@
         return (int) y;
     }
 
-    private int getClockY(float panelExpansion) {
+    private int getClockY(float panelExpansion, float darkAmount) {
         // Dark: Align the bottom edge of the clock at about half of the screen:
         float clockYDark = (mHasCustomClock ? getPreferredClockY() : getMaxClockY())
                 + burnInPreventionOffsetY();
@@ -273,7 +275,7 @@
         float clockY = MathUtils.lerp(clockYBouncer, clockYRegular, shadeExpansion);
         clockYDark = MathUtils.lerp(clockYBouncer, clockYDark, shadeExpansion);
 
-        float darkAmount = mBypassEnabled && !mHasCustomClock ? 1.0f : mDarkAmount;
+        darkAmount = mBypassEnabled && !mHasCustomClock ? 1.0f : darkAmount;
 
         if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
             // This will keep the clock at the top but out of the cutout area
@@ -295,7 +297,7 @@
      * @return Alpha from 0 to 1.
      */
     private float getClockAlpha(int y) {
-        float alphaKeyguard = Math.max(0, y / Math.max(1f, getClockY(1f)));
+        float alphaKeyguard = Math.max(0, y / Math.max(1f, getClockY(1f, mDarkAmount)));
         alphaKeyguard *= (1f - mQsExpansion);
         alphaKeyguard = Interpolators.ACCELERATE.getInterpolation(alphaKeyguard);
         return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount);
@@ -328,6 +330,11 @@
         public int clockY;
 
         /**
+         * The y translation of the clock when we're fully dozing.
+         */
+        public int clockYFullyDozing;
+
+        /**
          * The alpha value of the clock.
          */
         public float clockAlpha;
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 3e09784..b24d0e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -896,9 +896,8 @@
             int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
             int clockPreferredY = mKeyguardStatusViewController.getClockPreferredY(totalHeight);
             boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
-            final boolean hasVisibleNotifications = !bypassEnabled
-                    && (mNotificationStackScrollLayoutController.getVisibleNotificationCount() != 0
-                    || mMediaDataManager.hasActiveMedia());
+            final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
+                    .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
             mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications);
             mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding,
                     mNotificationStackScrollLayoutController.getIntrinsicContentHeight(),
@@ -3617,6 +3616,16 @@
             int oldState = mBarState;
             boolean keyguardShowing = statusBarState == KEYGUARD;
 
+            if (mDozeParameters.shouldControlUnlockedScreenOff() && isDozing() && keyguardShowing) {
+                // This means we're doing the screen off animation - position the keyguard status
+                // view where it'll be on AOD, so we can animate it in.
+                mKeyguardStatusViewController.updatePosition(
+                        mClockPositionResult.clockX,
+                        mClockPositionResult.clockYFullyDozing,
+                        mClockPositionResult.clockScale,
+                        false);
+            }
+
             mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
                     statusBarState,
                     keyguardFadingAway,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index fc1811b..1463148 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -49,6 +49,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.statusbar.BlurUtils;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.notification.stack.ViewState;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -125,6 +126,11 @@
      */
     public static final float BUSY_SCRIM_ALPHA = 1f;
 
+    /**
+     * Scrim opacity that can have text on top.
+     */
+    public static final float GAR_SCRIM_ALPHA = 0.6f;
+
     static final int TAG_KEY_ANIM = R.id.scrim;
     private static final int TAG_START_ALPHA = R.id.scrim_alpha_start;
     private static final int TAG_END_ALPHA = R.id.scrim_alpha_end;
@@ -199,10 +205,11 @@
             AlarmManager alarmManager, KeyguardStateController keyguardStateController,
             DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler,
             KeyguardUpdateMonitor keyguardUpdateMonitor, DockManager dockManager,
-            BlurUtils blurUtils, ConfigurationController configurationController) {
+            BlurUtils blurUtils, ConfigurationController configurationController,
+            FeatureFlags featureFlags) {
 
         mScrimStateListener = lightBarController::setScrimState;
-        mDefaultScrimAlpha = BUSY_SCRIM_ALPHA;
+        mDefaultScrimAlpha = featureFlags.isShadeOpaque() ? BUSY_SCRIM_ALPHA : GAR_SCRIM_ALPHA;
         mBlurUtils = blurUtils;
 
         mKeyguardStateController = keyguardStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 8ea173b..e07c3a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -193,6 +193,7 @@
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.NotificationShelfController;
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.PowerButtonReveal;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.StatusBarState;
@@ -372,6 +373,7 @@
     private boolean mWakeUpComingFromTouch;
     private PointF mWakeUpTouchLocation;
     private LightRevealScrim mLightRevealScrim;
+    private PowerButtonReveal mPowerButtonReveal;
 
     private final Object mQueueLock = new Object();
 
@@ -1382,7 +1384,7 @@
      * @param why the reason for the wake up
      */
     public void wakeUpIfDozing(long time, View where, String why) {
-        if (mDozing) {
+        if (mDozing && !mKeyguardViewMediator.isAnimatingScreenOff()) {
             mPowerManager.wakeUp(
                     time, PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:" + why);
             mWakeUpComingFromTouch = true;
@@ -2945,6 +2947,9 @@
         if (mBrightnessMirrorController != null) {
             mBrightnessMirrorController.updateResources();
         }
+
+        mPowerButtonReveal = new PowerButtonReveal(mContext.getResources().getDimensionPixelSize(
+                R.dimen.global_actions_top_padding));
     }
 
     // Visibility reporting
@@ -3652,6 +3657,16 @@
         updateQsExpansionEnabled();
         mKeyguardViewMediator.setDozing(mDozing);
 
+        final boolean usePowerButtonEffect =
+                (isDozing && mWakefulnessLifecycle.getLastSleepReason()
+                        == PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON)
+                        || (!isDozing && mWakefulnessLifecycle.getLastWakeReason()
+                        == PowerManager.WAKE_REASON_POWER_BUTTON);
+
+        mLightRevealScrim.setRevealEffect(usePowerButtonEffect
+                ? mPowerButtonReveal
+                : LiftReveal.INSTANCE);
+
         mNotificationsController.requestNotificationUpdate("onDozingChanged");
         updateDozingState();
         mDozeServiceHost.updateDozing();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 4cfbe69..0074dbe 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -22,6 +22,7 @@
 import android.testing.AndroidTestingRunner;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -50,6 +51,8 @@
     ConfigurationController mConfigurationController;
     @Mock
     NotificationIconAreaController mNotificationIconAreaController;
+    @Mock
+    DozeParameters mDozeParameters;
 
     private KeyguardStatusViewController mController;
 
@@ -64,7 +67,8 @@
                 mKeyguardStateController,
                 mKeyguardUpdateMonitor,
                 mConfigurationController,
-                mNotificationIconAreaController);
+                mNotificationIconAreaController,
+                mDozeParameters);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index e967a5d..6c3b37e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -24,10 +24,7 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -54,8 +51,6 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.util.time.FakeSystemClock;
 
-import junit.framework.Assert;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -84,8 +79,7 @@
         MockitoAnnotations.initMocks(this);
         mFsc = new ForegroundServiceController(mAppOpsController, mMainHandler);
         mListener = new ForegroundServiceNotificationListener(
-                mContext, mFsc, mEntryManager, mNotifPipeline,
-                mock(ForegroundServiceLifetimeExtender.class), mClock);
+                mContext, mFsc, mEntryManager, mNotifPipeline, mClock);
         ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
                 ArgumentCaptor.forClass(NotificationEntryListener.class);
         verify(mEntryManager).addNotificationEntryListener(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java
deleted file mode 100644
index 9a40421..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2019 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;
-
-import static com.android.systemui.ForegroundServiceLifetimeExtender.MIN_FGS_TIME_MS;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.app.Notification;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.systemui.statusbar.NotificationInteractionTracker;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class ForegroundServiceNotificationListenerTest extends SysuiTestCase {
-    private ForegroundServiceLifetimeExtender mExtender;
-    private NotificationEntry mEntry;
-    private Notification mNotif;
-    private final FakeSystemClock mClock = new FakeSystemClock();
-
-    @Mock
-    private NotificationInteractionTracker mInteractionTracker;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mExtender = new ForegroundServiceLifetimeExtender(mInteractionTracker, mClock);
-
-        mNotif = new Notification.Builder(mContext, "")
-                .setSmallIcon(R.drawable.ic_person)
-                .setContentTitle("Title")
-                .setContentText("Text")
-                .build();
-
-        mEntry = new NotificationEntryBuilder()
-                .setCreationTime(mClock.uptimeMillis())
-                .setNotification(mNotif)
-                .build();
-    }
-
-    /**
-     * ForegroundServiceLifetimeExtenderTest
-     */
-    @Test
-    public void testShouldExtendLifetime_should_foreground() {
-        // Extend the lifetime of a FGS notification iff it has not been visible
-        // for the minimum time
-        mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
-
-        // No time has elapsed, keep showing
-        assertTrue(mExtender.shouldExtendLifetime(mEntry));
-    }
-
-    @Test
-    public void testShouldExtendLifetime_shouldNot_foreground() {
-        mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
-
-        // Entry was created at mClock.uptimeMillis(), advance it MIN_FGS_TIME_MS + 1
-        mClock.advanceTime(MIN_FGS_TIME_MS + 1);
-        assertFalse(mExtender.shouldExtendLifetime(mEntry));
-    }
-
-    @Test
-    public void testShouldExtendLifetime_shouldNot_notForeground() {
-        mNotif.flags = 0;
-
-        // Entry was created at mClock.uptimeMillis(), advance it MIN_FGS_TIME_MS + 1
-        mClock.advanceTime(MIN_FGS_TIME_MS + 1);
-        assertFalse(mExtender.shouldExtendLifetime(mEntry));
-    }
-
-    @Test
-    public void testShouldExtendLifetime_shouldNot_interruped() {
-        // GIVEN a notification that would trigger lifetime extension
-        mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
-
-        // GIVEN the notification has alerted
-        mEntry.setInterruption();
-
-        // THEN the notification does not need to have its lifetime extended by this extender
-        assertFalse(mExtender.shouldExtendLifetime(mEntry));
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 67d0295..00943bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -44,6 +44,8 @@
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.DeviceConfigProxyFake;
@@ -73,6 +75,8 @@
     private @Mock TrustManager mTrustManager;
     private @Mock NavigationModeController mNavigationModeController;
     private @Mock KeyguardDisplayManager mKeyguardDisplayManager;
+    private @Mock DozeParameters mDozeParameters;
+    private @Mock StatusBarStateController mStatusBarStateController;
     private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
     private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
 
@@ -91,7 +95,7 @@
                 () -> mStatusBarKeyguardViewManager,
                 mDismissCallbackRegistry, mUpdateMonitor, mDumpManager, mUiBgExecutor,
                 mPowerManager, mTrustManager, mDeviceConfig, mNavigationModeController,
-                mKeyguardDisplayManager);
+                mKeyguardDisplayManager, mDozeParameters, mStatusBarStateController);
         mViewMediator.start();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
index 2e8e3ed..42e88b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
@@ -22,6 +22,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
+import android.os.PowerManager;
 import android.testing.AndroidTestingRunner;
 
 import androidx.test.filters.SmallTest;
@@ -58,7 +59,7 @@
 
     @Test
     public void dispatchStartedWakingUp() throws Exception {
-        mWakefulness.dispatchStartedWakingUp();
+        mWakefulness.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
 
         assertEquals(WakefulnessLifecycle.WAKEFULNESS_WAKING, mWakefulness.getWakefulness());
 
@@ -67,7 +68,7 @@
 
     @Test
     public void dispatchFinishedWakingUp() throws Exception {
-        mWakefulness.dispatchStartedWakingUp();
+        mWakefulness.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
         mWakefulness.dispatchFinishedWakingUp();
 
         assertEquals(WakefulnessLifecycle.WAKEFULNESS_AWAKE, mWakefulness.getWakefulness());
@@ -77,9 +78,9 @@
 
     @Test
     public void dispatchStartedGoingToSleep() throws Exception {
-        mWakefulness.dispatchStartedWakingUp();
+        mWakefulness.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
         mWakefulness.dispatchFinishedWakingUp();
-        mWakefulness.dispatchStartedGoingToSleep();
+        mWakefulness.dispatchStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN);
 
         assertEquals(WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP,
                 mWakefulness.getWakefulness());
@@ -89,9 +90,9 @@
 
     @Test
     public void dispatchFinishedGoingToSleep() throws Exception {
-        mWakefulness.dispatchStartedWakingUp();
+        mWakefulness.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
         mWakefulness.dispatchFinishedWakingUp();
-        mWakefulness.dispatchStartedGoingToSleep();
+        mWakefulness.dispatchStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN);
         mWakefulness.dispatchFinishedGoingToSleep();
 
         assertEquals(WakefulnessLifecycle.WAKEFULNESS_ASLEEP,
@@ -102,12 +103,12 @@
 
     @Test
     public void doesNotDispatchTwice() throws Exception {
-        mWakefulness.dispatchStartedWakingUp();
-        mWakefulness.dispatchStartedWakingUp();
+        mWakefulness.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
+        mWakefulness.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
         mWakefulness.dispatchFinishedWakingUp();
         mWakefulness.dispatchFinishedWakingUp();
-        mWakefulness.dispatchStartedGoingToSleep();
-        mWakefulness.dispatchStartedGoingToSleep();
+        mWakefulness.dispatchStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN);
+        mWakefulness.dispatchStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN);
         mWakefulness.dispatchFinishedGoingToSleep();
         mWakefulness.dispatchFinishedGoingToSleep();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
index c401fab..132bee0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
@@ -32,7 +32,6 @@
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
@@ -107,7 +106,6 @@
     }
 
     @Test
-    @Ignore // TODO(b/168209929)
     fun testMicCameraChanged() {
         changeMicCamera(false) // default is true
         executor.runAllReady()
@@ -140,7 +138,6 @@
     }
 
     @Test
-    @Ignore // TODO(b/168209929)
     fun testBothChanged() {
         changeAll(true)
         changeMicCamera(false)
@@ -162,7 +159,6 @@
     }
 
     @Test
-    @Ignore // TODO(b/168209929)
     fun testMicCamera_listening() {
         changeMicCamera(true)
         executor.runAllReady()
@@ -179,7 +175,6 @@
     }
 
     @Test
-    @Ignore // TODO(b/168209929)
     fun testAllFalse_notListening() {
         changeAll(true)
         executor.runAllReady()
@@ -191,7 +186,6 @@
     }
 
     @Test
-    @Ignore // TODO(b/168209929)
     fun testSomeListening_stillListening() {
         // Mic and camera are true by default
         changeAll(true)
@@ -203,7 +197,6 @@
     }
 
     @Test
-    @Ignore // TODO(b/168209929)
     fun testAllDeleted_micCameraFalse_stopListening() {
         changeMicCamera(false)
         changeAll(true)
@@ -215,7 +208,6 @@
     }
 
     @Test
-    @Ignore // TODO(b/168209929)
     fun testMicDeleted_stillListening() {
         changeMicCamera(true)
         executor.runAllReady()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index a8b3056..7ca468e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -97,6 +97,7 @@
 
     private lateinit var privacyItemController: PrivacyItemController
     private lateinit var executor: FakeExecutor
+    private lateinit var fakeClock: FakeSystemClock
     private lateinit var deviceConfigProxy: DeviceConfigProxy
 
     fun PrivacyItemController(): PrivacyItemController {
@@ -113,7 +114,8 @@
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
-        executor = FakeExecutor(FakeSystemClock())
+        fakeClock = FakeSystemClock()
+        executor = FakeExecutor(fakeClock)
         deviceConfigProxy = DeviceConfigProxyFake()
 
         // Listen to everything by default
@@ -420,6 +422,104 @@
         assertEquals(PrivacyType.TYPE_MICROPHONE, argCaptor.value[0].privacyType)
     }
 
+    @Test
+    fun testPassageOfTimeDoesNotRemoveIndicators() {
+        doReturn(listOf(
+                AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0)
+        )).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+        privacyItemController.addCallback(callback)
+
+        fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS * 10)
+        executor.runAllReady()
+
+        verify(callback, never()).onPrivacyItemsChanged(emptyList())
+        assertTrue(privacyItemController.privacyList.isNotEmpty())
+    }
+
+    @Test
+    fun testHoldingAfterEmptyBeforeTimeExpires() {
+        doReturn(listOf(
+                AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0)
+        )).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+        privacyItemController.addCallback(callback)
+        executor.runAllReady()
+
+        verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+
+        `when`(appOpsController.getActiveAppOpsForUser(anyInt())).thenReturn(emptyList())
+        argCaptorCallback.value.onActiveStateChanged(
+                AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, false)
+        executor.runAllReady()
+
+        fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS / 5)
+        executor.runAllReady()
+
+        verify(callback, never()).onPrivacyItemsChanged(emptyList())
+        assertTrue(privacyItemController.privacyList.isNotEmpty())
+    }
+
+    @Test
+    fun testAfterHoldingIndicatorsAreEmpty() {
+        doReturn(listOf(
+                AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0)
+        )).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+        privacyItemController.addCallback(callback)
+        executor.runAllReady()
+
+        verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+
+        `when`(appOpsController.getActiveAppOpsForUser(anyInt())).thenReturn(emptyList())
+        argCaptorCallback.value.onActiveStateChanged(
+                AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, false)
+        executor.runAllReady()
+
+        executor.advanceClockToLast()
+        executor.runAllReady()
+
+        verify(callback).onPrivacyItemsChanged(emptyList())
+        assertTrue(privacyItemController.privacyList.isEmpty())
+    }
+
+    @Test
+    fun testHoldingStopsIfNewIndicatorsAppear() {
+        doReturn(listOf(
+                AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0)
+        )).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+        privacyItemController.addCallback(callback)
+        executor.runAllReady()
+
+        verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+
+        `when`(appOpsController.getActiveAppOpsForUser(anyInt())).thenReturn(emptyList())
+        argCaptorCallback.value.onActiveStateChanged(
+                AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, false)
+        executor.runAllReady()
+
+        fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS / 2)
+        executor.runAllReady()
+
+        doReturn(listOf(
+                AppOpItem(AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, 0)
+        )).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        argCaptorCallback.value.onActiveStateChanged(
+                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true)
+        executor.runAllReady()
+
+        executor.advanceClockToLast()
+        executor.runAllReady()
+
+        verify(callback, never()).onPrivacyItemsChanged(emptyList())
+        verify(callback, atLeastOnce()).onPrivacyItemsChanged(capture(argCaptor))
+
+        val lastList = argCaptor.allValues.last()
+        assertEquals(1, lastList.size)
+        assertEquals(PrivacyType.TYPE_MICROPHONE, lastList.single().privacyType)
+    }
+
     private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
     private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index a6b0330..1260eaf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -51,6 +51,7 @@
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.phone.AutoTileManager;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -87,6 +88,8 @@
     private MediaHost mQSMediaHost;
     @Mock
     private MediaHost mQQSMediaHost;
+    @Mock
+    private FeatureFlags mFeatureFlags;
 
     public QSFragmentTest() {
         super(QSFragment.class);
@@ -175,6 +178,7 @@
                 new QSDetailDisplayer(),
                 mQSMediaHost,
                 mQQSMediaHost,
-                mQsComponentFactory);
+                mQsComponentFactory,
+                mFeatureFlags);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java
index f2bf7aa..b0f78ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java
@@ -32,7 +32,6 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
-import android.net.Uri;
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.provider.MediaStore;
@@ -55,6 +54,7 @@
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
+import java.util.UUID;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 
@@ -79,10 +79,13 @@
     public void testUpdateExifAttributes_timeZoneUTC() throws IOException {
         ExifInterface exifInterface = new ExifInterface(new ByteArrayInputStream(EXIF_FILE_TAG),
                 ExifInterface.STREAM_TYPE_EXIF_DATA_ONLY);
-
-        ImageExporter.updateExifAttributes(exifInterface, 100, 100,
+        ImageExporter.updateExifAttributes(exifInterface,
+                UUID.fromString("3c11da99-9284-4863-b1d5-6f3684976814"), 100, 100,
                 ZonedDateTime.of(LocalDateTime.of(2020, 12, 15, 18, 15), ZoneId.of("UTC")));
 
+        assertEquals("Exif " + ExifInterface.TAG_IMAGE_UNIQUE_ID,
+                "3c11da99-9284-4863-b1d5-6f3684976814",
+                exifInterface.getAttribute(ExifInterface.TAG_IMAGE_UNIQUE_ID));
         assertEquals("Exif " + ExifInterface.TAG_OFFSET_TIME_ORIGINAL, "+00:00",
                 exifInterface.getAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL));
     }
@@ -93,24 +96,38 @@
         ContentResolver contentResolver = context.getContentResolver();
         ImageExporter exporter = new ImageExporter(contentResolver);
 
+        UUID requestId = UUID.fromString("3c11da99-9284-4863-b1d5-6f3684976814");
         Bitmap original = createCheckerBitmap(10, 10, 10);
 
-        ListenableFuture<Uri> direct = exporter.export(DIRECT_EXECUTOR, original, CAPTURE_TIME);
+        ListenableFuture<ImageExporter.Result> direct =
+                exporter.export(DIRECT_EXECUTOR, requestId, original, CAPTURE_TIME);
         assertTrue("future should be done", direct.isDone());
         assertFalse("future should not be canceled", direct.isCancelled());
-        Uri result = direct.get();
+        ImageExporter.Result result = direct.get();
 
-        assertNotNull("Uri should not be null", result);
+        assertEquals("Result should contain the same request id", requestId, result.requestId);
+        assertEquals("Filename should contain the correct filename",
+                "Screenshot_20201215-131500.png", result.fileName);
+        assertNotNull("CompressFormat should be set", result.format);
+        assertEquals("The default CompressFormat should be PNG", CompressFormat.PNG, result.format);
+        assertNotNull("Uri should not be null", result.uri);
+        assertEquals("Timestamp should match input", CAPTURE_TIME.toInstant().toEpochMilli(),
+                result.timestamp);
+
         Bitmap decoded = null;
-        try (InputStream in = contentResolver.openInputStream(result)) {
+        try (InputStream in = contentResolver.openInputStream(result.uri)) {
             decoded = BitmapFactory.decodeStream(in);
             assertNotNull("decoded image should not be null", decoded);
             assertTrue("original and decoded image should be identical", original.sameAs(decoded));
 
-            try (ParcelFileDescriptor pfd = contentResolver.openFile(result, "r", null)) {
+            try (ParcelFileDescriptor pfd = contentResolver.openFile(result.uri, "r", null)) {
                 assertNotNull(pfd);
                 ExifInterface exifInterface = new ExifInterface(pfd.getFileDescriptor());
 
+                assertEquals("Exif " + ExifInterface.TAG_IMAGE_UNIQUE_ID,
+                        "3c11da99-9284-4863-b1d5-6f3684976814",
+                        exifInterface.getAttribute(ExifInterface.TAG_IMAGE_UNIQUE_ID));
+
                 assertEquals("Exif " + ExifInterface.TAG_SOFTWARE, "Android " + Build.DISPLAY,
                         exifInterface.getAttribute(ExifInterface.TAG_SOFTWARE));
 
@@ -130,13 +147,14 @@
             if (decoded != null) {
                 decoded.recycle();
             }
-            contentResolver.delete(result, null);
+            contentResolver.delete(result.uri, null);
         }
     }
 
     @Test
     public void testMediaStoreMetadata() {
-        ContentValues values = ImageExporter.createMetadata(CAPTURE_TIME, CompressFormat.PNG);
+        String name = ImageExporter.createFilename(CAPTURE_TIME, CompressFormat.PNG);
+        ContentValues values = ImageExporter.createMetadata(CAPTURE_TIME, CompressFormat.PNG, name);
         assertEquals("Pictures/Screenshots",
                 values.getAsString(MediaStore.MediaColumns.RELATIVE_PATH));
         assertEquals("Screenshot_20201215-131500.png",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
index 639e791..0954621 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
@@ -31,7 +31,6 @@
 import android.app.Notification;
 import android.os.Bundle;
 import android.os.UserHandle;
-import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -46,7 +45,6 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 
@@ -71,7 +69,6 @@
     private NotificationEntryBuilder mEntryBuilder;
     private AppOpsCoordinator mAppOpsCoordinator;
     private NotifFilter mForegroundFilter;
-    private NotifLifetimeExtender mForegroundNotifLifetimeExtender;
     private NotifSectioner mFgsSection;
 
     private FakeSystemClock mClock = new FakeSystemClock();
@@ -98,13 +95,6 @@
         verify(mNotifPipeline, times(1)).addPreGroupFilter(filterCaptor.capture());
         mForegroundFilter = filterCaptor.getValue();
 
-        // capture lifetime extender
-        ArgumentCaptor<NotifLifetimeExtender> lifetimeExtenderCaptor =
-                ArgumentCaptor.forClass(NotifLifetimeExtender.class);
-        verify(mNotifPipeline, times(1)).addNotificationLifetimeExtender(
-                lifetimeExtenderCaptor.capture());
-        mForegroundNotifLifetimeExtender = lifetimeExtenderCaptor.getValue();
-
         mFgsSection = mAppOpsCoordinator.getSectioner();
     }
 
@@ -160,55 +150,6 @@
     }
 
     @Test
-    public void extendLifetimeText_notForeground() {
-        // GIVEN the notification doesn't represent a foreground service
-        mEntryBuilder.modifyNotification(mContext)
-                .setFlag(FLAG_FOREGROUND_SERVICE, false);
-
-        // THEN don't extend the lifetime
-        assertFalse(mForegroundNotifLifetimeExtender
-                .shouldExtendLifetime(mEntryBuilder.build(),
-                        NotificationListenerService.REASON_CLICK));
-    }
-
-    @Test
-    public void extendLifetimeText_foregroundNotifRecentlyPosted() {
-        // GIVEN the notification represents a foreground service that was just posted
-        Notification notification = new Notification.Builder(mContext, "test_channel")
-                .setFlag(FLAG_FOREGROUND_SERVICE, true)
-                .build();
-        NotificationEntry entry = mEntryBuilder
-                .setSbn(new StatusBarNotification(TEST_PKG, TEST_PKG, NOTIF_USER_ID, "",
-                        NOTIF_USER_ID, NOTIF_USER_ID, notification,
-                        new UserHandle(NOTIF_USER_ID), "", System.currentTimeMillis()))
-                .setNotification(notification)
-                .build();
-
-        // THEN extend the lifetime
-        assertTrue(mForegroundNotifLifetimeExtender
-                .shouldExtendLifetime(entry, NotificationListenerService.REASON_CLICK));
-    }
-
-    @Test
-    public void extendLifetimeText_foregroundNotifOld() {
-        // GIVEN the notification represents a foreground service that was posted 10 seconds ago
-        Notification notification = new Notification.Builder(mContext, "test_channel")
-                .setFlag(FLAG_FOREGROUND_SERVICE, true)
-                .build();
-        NotificationEntry entry = mEntryBuilder
-                .setSbn(new StatusBarNotification(TEST_PKG, TEST_PKG, NOTIF_USER_ID, "",
-                        NOTIF_USER_ID, NOTIF_USER_ID, notification,
-                        new UserHandle(NOTIF_USER_ID), "",
-                        System.currentTimeMillis() - 10000))
-                .setNotification(notification)
-                .build();
-
-        // THEN don't extend the lifetime because the extended time exceeds MIN_FGS_TIME_MS
-        assertFalse(mForegroundNotifLifetimeExtender
-                .shouldExtendLifetime(entry, NotificationListenerService.REASON_CLICK));
-    }
-
-    @Test
     public void testIncludeFGSInSection_importanceDefault() {
         // GIVEN the notification represents a colorized foreground service with > min importance
         mEntryBuilder
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 342b2f5..30c3e6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -50,6 +50,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.statusbar.BlurUtils;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -104,6 +105,8 @@
     private BlurUtils mBlurUtils;
     @Mock
     private ConfigurationController mConfigurationController;
+    @Mock
+    private FeatureFlags mFeatureFlags;
 
 
     private static class AnimatorListener implements Animator.AnimatorListener {
@@ -211,13 +214,13 @@
         when(mDelayedWakeLockBuilder.setTag(any(String.class)))
                 .thenReturn(mDelayedWakeLockBuilder);
         when(mDelayedWakeLockBuilder.build()).thenReturn(mWakeLock);
-
+        when(mFeatureFlags.isShadeOpaque()).thenReturn(true);
         when(mDockManager.isDocked()).thenReturn(false);
 
         mScrimController = new ScrimController(mLightBarController,
                 mDozeParamenters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder,
                 new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor,
-                mDockManager, mBlurUtils, mConfigurationController);
+                mDockManager, mBlurUtils, mConfigurationController, mFeatureFlags);
         mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
         mScrimController.attachViews(mScrimBehind, mScrimInFront, mScrimForBubble);
         mScrimController.setAnimatorListener(mAnimatorListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 51ce8e5..2c781ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -317,7 +317,7 @@
         when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
 
         WakefulnessLifecycle wakefulnessLifecycle = new WakefulnessLifecycle();
-        wakefulnessLifecycle.dispatchStartedWakingUp();
+        wakefulnessLifecycle.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
         wakefulnessLifecycle.dispatchFinishedWakingUp();
 
         when(mGradientColors.supportsDarkText()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 273a77b..c83835b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -62,6 +62,7 @@
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.settingslib.R;
 import com.android.settingslib.graph.SignalDrawable;
 import com.android.settingslib.mobile.MobileMappings.Config;
@@ -82,6 +83,8 @@
 import org.junit.runner.Description;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -126,6 +129,8 @@
     private ConnectivityManager.NetworkCallback mDefaultCallbackInNetworkController;
     private ConnectivityManager.NetworkCallback mNetworkCallback;
 
+    MockitoSession mMockingSession = null;
+
     @Rule
     public TestWatcher failWatcher = new TestWatcher() {
         @Override
@@ -145,7 +150,11 @@
 
     @Before
     public void setUp() throws Exception {
-        FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL, true);
+        mMockingSession = ExtendedMockito.mockitoSession().strictness(Strictness.LENIENT)
+                .mockStatic(FeatureFlagUtils.class).startMocking();
+        ExtendedMockito.doReturn(true).when(() -> FeatureFlagUtils.isEnabled(mContext,
+                FeatureFlagUtils.SETTINGS_PROVIDER_MODEL));
+
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         Settings.Global.putInt(mContext.getContentResolver(), Global.AIRPLANE_MODE_ON, 0);
         TestableResources res = mContext.getOrCreateTestableResources();
@@ -227,7 +236,9 @@
 
     @After
     public void tearDown() throws Exception {
-        FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL, false);
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
     }
 
     protected void setupNetworkController() {
diff --git a/services/Android.bp b/services/Android.bp
index da24719..b11a2e8 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -147,11 +147,20 @@
             baseline_file: "api/lint-baseline.txt",
         },
     },
-    dist: {
-        targets: ["sdk", "win_sdk"],
-        dir: "apistubs/android/system-server/api",
-        dest: "android.txt",
-    },
+    dists: [
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/system-server/api",
+            dest: "android.txt",
+            tag: ".api.txt"
+        },
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/system-server/api",
+            dest: "removed.txt",
+            tag: ".removed-api.txt",
+        },
+    ]
 }
 
 java_library {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 7483ff3..2434e2c 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -141,12 +141,12 @@
 
     public FullScreenMagnificationGestureHandler(Context context,
             FullScreenMagnificationController fullScreenMagnificationController,
-            ScaleChangedListener listener,
+            Callback callback,
             boolean detectTripleTap,
             boolean detectShortcutTrigger,
             @NonNull WindowMagnificationPromptController promptController,
             int displayId) {
-        super(displayId, detectTripleTap, detectShortcutTrigger, listener);
+        super(displayId, detectTripleTap, detectShortcutTrigger, callback);
         if (DEBUG_ALL) {
             Log.i(mLogTag,
                     "FullScreenMagnificationGestureHandler(detectTripleTap = " + detectTripleTap
@@ -211,16 +211,14 @@
     }
 
     @Override
-    public void notifyShortcutTriggered() {
-        if (mDetectShortcutTrigger) {
-            boolean wasMagnifying = mFullScreenMagnificationController.resetIfNeeded(mDisplayId,
-                    /* animate */ true);
-            if (wasMagnifying) {
-                clearAndTransitionToStateDetecting();
-            } else {
-                mPromptController.showNotificationIfNeeded();
-                mDetectingState.toggleShortcutTriggered();
-            }
+    public void handleShortcutTriggered() {
+        boolean wasMagnifying = mFullScreenMagnificationController.resetIfNeeded(mDisplayId,
+                /* animate */ true);
+        if (wasMagnifying) {
+            clearAndTransitionToStateDetecting();
+        } else {
+            mPromptController.showNotificationIfNeeded();
+            mDetectingState.toggleShortcutTriggered();
         }
     }
 
@@ -395,7 +393,6 @@
             if (DEBUG_PANNING_SCALING) Slog.i(mLogTag, "Scaled content to: " + scale + "x");
             mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false,
                     AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
-            mListener.onMagnificationScaleChanged(mDisplayId, getMode());
             return /* handled: */ true;
         }
 
@@ -869,6 +866,8 @@
                 mPromptController.showNotificationIfNeeded();
                 zoomOn(up.getX(), up.getY());
             }
+
+            mCallback.onTripleTapped(mDisplayId, getMode());
         }
 
         private boolean isMagnifying() {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index df88ceb..2a65b64 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -34,11 +34,24 @@
 import com.android.server.accessibility.AccessibilityManagerService;
 
 /**
- * Handles all magnification controllers initialization, generic interactions
- * and magnification mode transition.
+ * Handles all magnification controllers initialization, generic interactions,
+ * magnification mode transition and magnification switch UI show/hide logic
+ * in the following callbacks:
+ *
+ * <ol>
+ *   <li> 1. {@link #onTouchInteractionStart} shows magnification switch UI when
+ *   the user touch interaction starts if magnification capabilities is all. </li>
+ *   <li> 2. {@link #onTouchInteractionEnd} shows magnification switch UI when
+ *   the user touch interaction ends if magnification capabilities is all. </li>
+ *   <li> 3. {@link #onShortcutTriggered} updates magnification switch UI depending on
+ *   magnification capabilities and magnification active state when magnification shortcut
+ *   is triggered.</li>
+ *   <li> 4. {@link #onTripleTapped} updates magnification switch UI depending on magnification
+ *   capabilities and magnification active state when triple-tap gesture is detected. </li>
+ * </ol>
  */
 public class MagnificationController implements WindowMagnificationManager.Callback,
-        MagnificationGestureHandler.ScaleChangedListener {
+        MagnificationGestureHandler.Callback {
 
     private static final boolean DEBUG = false;
     private static final String TAG = "MagnificationController";
@@ -84,16 +97,44 @@
     public void onPerformScaleAction(int displayId, float scale) {
         getWindowMagnificationMgr().setScale(displayId, scale);
         getWindowMagnificationMgr().persistScale(displayId);
-        onMagnificationScaleChanged(displayId,
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
     }
 
     @Override
-    public void onMagnificationScaleChanged(int displayId, int mode) {
+    public void onTouchInteractionStart(int displayId, int mode) {
+        handleUserInteractionChanged(displayId, mode);
+    }
+
+    @Override
+    public void onTouchInteractionEnd(int displayId, int mode) {
+        handleUserInteractionChanged(displayId, mode);
+    }
+
+    private void handleUserInteractionChanged(int displayId, int mode) {
         if (mMagnificationCapabilities != Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL) {
             return;
         }
-        getWindowMagnificationMgr().showMagnificationButton(displayId, mode);
+        if (isActivated(displayId, mode)) {
+            getWindowMagnificationMgr().showMagnificationButton(displayId, mode);
+        }
+    }
+
+    @Override
+    public void onShortcutTriggered(int displayId, int mode) {
+        updateMagnificationButton(displayId, mode);
+    }
+
+    @Override
+    public void onTripleTapped(int displayId, int mode) {
+        updateMagnificationButton(displayId, mode);
+    }
+
+    private void updateMagnificationButton(int displayId, int mode) {
+        if (isActivated(displayId, mode) && mMagnificationCapabilities
+                == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL) {
+            getWindowMagnificationMgr().showMagnificationButton(displayId, mode);
+        } else {
+            getWindowMagnificationMgr().removeMagnificationButton(displayId);
+        }
     }
 
     /**
@@ -272,6 +313,18 @@
         return mTempPoint;
     }
 
+    private boolean isActivated(int displayId, int mode) {
+        boolean isActivated = false;
+        if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
+                && mFullScreenMagnificationController != null) {
+            isActivated = mFullScreenMagnificationController.isMagnifying(displayId);
+        } else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
+                && mWindowMagnificationMgr != null) {
+            isActivated = mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId);
+        }
+        return isActivated;
+    }
+
     private final class DisableMagnificationCallback implements
             MagnificationAnimationCallback {
         private final TransitionCallBack mTransitionCallBack;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
index 386d0bb..bbe40b6 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
@@ -17,6 +17,8 @@
 package com.android.server.accessibility.magnification;
 
 import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_UP;
 
 import android.annotation.NonNull;
 import android.util.Log;
@@ -59,26 +61,53 @@
      */
     protected final boolean mDetectTripleTap;
 
-    /** Interface for listening to the magnification scaling gesture. */
-    public interface ScaleChangedListener {
+    /** Callback interface to report that magnification is interactive with a user. */
+    public interface Callback {
         /**
-         * Called when the magnification scale is changed by users.
+         * Called when the touch interaction is started by a user.
          *
          * @param displayId The logical display id
-         * @param mode      The magnification mode
+         * @param mode The magnification mode
          */
-        void onMagnificationScaleChanged(int displayId, int mode);
+        void onTouchInteractionStart(int displayId, int mode);
+
+        /**
+         * Called when the touch interaction is ended by a user.
+         *
+         * @param displayId The logical display id
+         * @param mode The magnification mode
+         */
+        void onTouchInteractionEnd(int displayId, int mode);
+
+        /**
+         * Called when the magnification shortcut is triggered by a user. The magnification
+         * shortcut can be accessibility button or volume shortcut.
+         *
+         * @param displayId The logical display id
+         * @param mode The magnification mode
+         */
+        void onShortcutTriggered(int displayId, int mode);
+
+        /**
+         * Called when the triple-tap gesture is handled. The magnification
+         * shortcut can be a triple-tap gesture or accessibility button.
+         * Called when the triple-tap gesture is handled
+         *
+         * @param displayId The logical display id
+         * @param mode The magnification mode
+         */
+        void onTripleTapped(int displayId, int mode);
     }
 
-    protected final ScaleChangedListener mListener;
+    protected final Callback mCallback;
 
     protected MagnificationGestureHandler(int displayId, boolean detectTripleTap,
             boolean detectShortcutTrigger,
-            @NonNull ScaleChangedListener listener) {
+            @NonNull Callback callback) {
         mDisplayId = displayId;
         mDetectTripleTap = detectTripleTap;
         mDetectShortcutTrigger = detectShortcutTrigger;
-        mListener = listener;
+        mCallback = callback;
 
         mDebugInputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null;
         mDebugOutputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null;
@@ -96,6 +125,13 @@
             dispatchTransformedEvent(event, rawEvent, policyFlags);
         } else {
             onMotionEventInternal(event, rawEvent, policyFlags);
+
+            final int action = event.getAction();
+            if (action == MotionEvent.ACTION_DOWN) {
+                mCallback.onTouchInteractionStart(mDisplayId, getMode());
+            } else if (action == ACTION_UP || action == ACTION_CANCEL) {
+                mCallback.onTouchInteractionEnd(mDisplayId, getMode());
+            }
         }
     }
 
@@ -107,8 +143,7 @@
         return false;
     }
 
-    final void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent,
-            int policyFlags) {
+    final void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
         if (DEBUG_EVENT_STREAM) {
             storeEventInto(mDebugOutputEventHistory, event);
             try {
@@ -140,7 +175,20 @@
     /**
      * Called when the shortcut target is magnification.
      */
-    public abstract void notifyShortcutTriggered();
+    public void notifyShortcutTriggered() {
+        if (DEBUG_ALL) {
+            Slog.i(mLogTag, "notifyShortcutTriggered():");
+        }
+        if (mDetectShortcutTrigger) {
+            handleShortcutTriggered();
+            mCallback.onShortcutTriggered(mDisplayId, getMode());
+        }
+    }
+
+    /**
+     * Handles shortcut triggered event.
+     */
+    abstract void handleShortcutTriggered();
 
     /**
      * Indicates the magnification mode.
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 7f26b27..55a911e 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -88,9 +88,9 @@
 
     public WindowMagnificationGestureHandler(Context context,
             WindowMagnificationManager windowMagnificationMgr,
-            ScaleChangedListener listener,
+            Callback callback,
             boolean detectTripleTap, boolean detectShortcutTrigger, int displayId) {
-        super(displayId, detectTripleTap, detectShortcutTrigger, listener);
+        super(displayId, detectTripleTap, detectShortcutTrigger, callback);
         if (DEBUG_ALL) {
             Slog.i(mLogTag,
                     "WindowMagnificationGestureHandler() , displayId = " + displayId + ")");
@@ -115,7 +115,6 @@
                             @Override
                             public void setScale(int displayId, float scale) {
                                 mWindowMagnificationMgr.setScale(displayId, scale);
-                                mListener.onMagnificationScaleChanged(displayId, getMode());
                             }
 
                             @Override
@@ -153,13 +152,7 @@
     }
 
     @Override
-    public void notifyShortcutTriggered() {
-        if (DEBUG_ALL) {
-            Slog.i(mLogTag, "notifyShortcutTriggered():");
-        }
-        if (!mDetectShortcutTrigger) {
-            return;
-        }
+    public void handleShortcutTriggered() {
         final Point screenSize = mTempPoint;
         getScreenSize(mTempPoint);
         toggleMagnification(screenSize.x / 2.0f, screenSize.y / 2.0f);
@@ -206,6 +199,7 @@
             Slog.i(mLogTag, "onTripleTap()");
         }
         toggleMagnification(up.getX(), up.getY());
+        mCallback.onTripleTapped(mDisplayId, getMode());
     }
 
     void resetToDetectState() {
diff --git a/services/appwidget/java/com/android/server/appwidget/OWNERS b/services/appwidget/java/com/android/server/appwidget/OWNERS
new file mode 100644
index 0000000..d724cac
--- /dev/null
+++ b/services/appwidget/java/com/android/server/appwidget/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/appwidget/OWNERS
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 3750f14..2f3ad19 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -120,6 +120,9 @@
         "time_zone_distro",
         "time_zone_distro_installer",
         "android.hardware.authsecret-V1.0-java",
+        "android.hardware.boot-V1.0-java",
+        "android.hardware.boot-V1.1-java",
+        "android.hardware.boot-V1.2-java",
         "android.hardware.broadcastradio-V2.0-java",
         "android.hardware.health-V1.0-java",
         "android.hardware.health-V2.0-java",
@@ -223,6 +226,8 @@
         "java/com/android/server/connectivity/NetworkRanker.java",
         "java/com/android/server/connectivity/PermissionMonitor.java",
         "java/com/android/server/connectivity/ProxyTracker.java",
+        "java/com/android/server/connectivity/QosCallbackAgentConnection.java",
+        "java/com/android/server/connectivity/QosCallbackTracker.java",
         "java/com/android/server/connectivity/TcpKeepaliveController.java",
         "java/com/android/server/connectivity/Vpn.java",
         "java/com/android/server/connectivity/VpnIkev2Utils.java",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7541833..b6232a0 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -94,6 +94,7 @@
 import android.net.INetworkMonitorCallbacks;
 import android.net.INetworkPolicyListener;
 import android.net.INetworkStatsService;
+import android.net.IQosCallback;
 import android.net.ISocketKeepaliveCallback;
 import android.net.InetAddresses;
 import android.net.IpMemoryStore;
@@ -121,6 +122,10 @@
 import android.net.NetworkWatchlistManager;
 import android.net.PrivateDnsConfigParcel;
 import android.net.ProxyInfo;
+import android.net.QosCallbackException;
+import android.net.QosFilter;
+import android.net.QosSocketFilter;
+import android.net.QosSocketInfo;
 import android.net.RouteInfo;
 import android.net.RouteInfoParcel;
 import android.net.SocketKeepalive;
@@ -204,6 +209,7 @@
 import com.android.server.connectivity.NetworkRanker;
 import com.android.server.connectivity.PermissionMonitor;
 import com.android.server.connectivity.ProxyTracker;
+import com.android.server.connectivity.QosCallbackTracker;
 import com.android.server.connectivity.Vpn;
 import com.android.server.net.BaseNetworkObserver;
 import com.android.server.net.LockdownVpnTracker;
@@ -279,6 +285,10 @@
     // Default to 30s linger time-out. Modifiable only for testing.
     private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
     private static final int DEFAULT_LINGER_DELAY_MS = 30_000;
+
+    // The maximum number of network request allowed per uid before an exception is thrown.
+    private static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
+
     @VisibleForTesting
     protected int mLingerDelayMs;  // Can't be final, or test subclass constructors can't change it.
 
@@ -291,6 +301,8 @@
     @VisibleForTesting
     protected final PermissionMonitor mPermissionMonitor;
 
+    private final PerUidCounter mNetworkRequestCounter;
+
     private KeyStore mKeyStore;
 
     @VisibleForTesting
@@ -614,6 +626,7 @@
     private final LocationPermissionChecker mLocationPermissionChecker;
 
     private KeepaliveTracker mKeepaliveTracker;
+    private QosCallbackTracker mQosCallbackTracker;
     private NetworkNotificationManager mNotifier;
     private LingerMonitor mLingerMonitor;
 
@@ -858,6 +871,66 @@
     };
 
     /**
+     * Keeps track of the number of requests made under different uids.
+     */
+    public static class PerUidCounter {
+        private final int mMaxCountPerUid;
+
+        // Map from UID to number of NetworkRequests that UID has filed.
+        @GuardedBy("mUidToNetworkRequestCount")
+        private final SparseIntArray mUidToNetworkRequestCount = new SparseIntArray();
+
+        /**
+         * Constructor
+         *
+         * @param maxCountPerUid the maximum count per uid allowed
+         */
+        public PerUidCounter(final int maxCountPerUid) {
+            mMaxCountPerUid = maxCountPerUid;
+        }
+
+        /**
+         * Increments the request count of the given uid.  Throws an exception if the number
+         * of open requests for the uid exceeds the value of maxCounterPerUid which is the value
+         * passed into the constructor. see: {@link #PerUidCounter(int)}.
+         *
+         * @throws ServiceSpecificException with
+         * {@link ConnectivityManager.Errors.TOO_MANY_REQUESTS} if the number of requests for
+         * the uid exceed the allowed number.
+         *
+         * @param uid the uid that the request was made under
+         */
+        public void incrementCountOrThrow(final int uid) {
+            synchronized (mUidToNetworkRequestCount) {
+                final int networkRequests = mUidToNetworkRequestCount.get(uid, 0) + 1;
+                if (networkRequests >= mMaxCountPerUid) {
+                    throw new ServiceSpecificException(
+                            ConnectivityManager.Errors.TOO_MANY_REQUESTS);
+                }
+                mUidToNetworkRequestCount.put(uid, networkRequests);
+            }
+        }
+
+        /**
+         * Decrements the request count of the given uid.
+         *
+         * @param uid the uid that the request was made under
+         */
+        public void decrementCount(final int uid) {
+            synchronized (mUidToNetworkRequestCount) {
+                final int requests = mUidToNetworkRequestCount.get(uid, 0);
+                if (requests < 1) {
+                    logwtf("BUG: too small request count " + requests + " for UID " + uid);
+                } else if (requests == 1) {
+                    mUidToNetworkRequestCount.delete(uid);
+                } else {
+                    mUidToNetworkRequestCount.put(uid, requests - 1);
+                }
+            }
+        }
+    }
+
+    /**
      * Dependencies of ConnectivityService, for injection in tests.
      */
     @VisibleForTesting
@@ -945,6 +1018,7 @@
         mSystemProperties = mDeps.getSystemProperties();
         mNetIdManager = mDeps.makeNetIdManager();
         mContext = Objects.requireNonNull(context, "missing Context");
+        mNetworkRequestCounter = new PerUidCounter(MAX_NETWORK_REQUESTS_PER_UID);
 
         mMetricsLog = logger;
         mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
@@ -1115,11 +1189,7 @@
         userAllContext.registerReceiver(
                 mIntentReceiver, intentFilter, NETWORK_STACK, mHandler);
 
-        try {
-            mNMS.registerObserver(mDataActivityObserver);
-        } catch (RemoteException e) {
-            loge("Error registering observer :" + e);
-        }
+        mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mNMS);
 
         mSettingsObserver = new SettingsObserver(mContext, mHandler);
         registerSettingsCallbacks();
@@ -1129,6 +1199,7 @@
 
         mKeepaliveTracker = new KeepaliveTracker(mContext, mHandler);
         mNotifier = new NetworkNotificationManager(mContext, mTelephonyManager);
+        mQosCallbackTracker = new QosCallbackTracker(mHandler, mNetworkRequestCounter);
 
         final int dailyLimit = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT,
@@ -1802,30 +1873,6 @@
         }
     }
 
-    private INetworkManagementEventObserver mDataActivityObserver = new BaseNetworkObserver() {
-        @Override
-        public void interfaceClassDataActivityChanged(int transportType, boolean active,
-                long tsNanos, int uid) {
-            sendDataActivityBroadcast(transportTypeToLegacyType(transportType), active, tsNanos);
-        }
-    };
-
-    // This is deprecated and only to support legacy use cases.
-    private int transportTypeToLegacyType(int type) {
-        switch (type) {
-            case NetworkCapabilities.TRANSPORT_CELLULAR:
-                return ConnectivityManager.TYPE_MOBILE;
-            case NetworkCapabilities.TRANSPORT_WIFI:
-                return ConnectivityManager.TYPE_WIFI;
-            case NetworkCapabilities.TRANSPORT_BLUETOOTH:
-                return ConnectivityManager.TYPE_BLUETOOTH;
-            case NetworkCapabilities.TRANSPORT_ETHERNET:
-                return ConnectivityManager.TYPE_ETHERNET;
-            default:
-                loge("Unexpected transport in transportTypeToLegacyType: " + type);
-        }
-        return ConnectivityManager.TYPE_NONE;
-    }
     /**
      * Ensures that the system cannot call a particular method.
      */
@@ -2274,20 +2321,6 @@
         sendStickyBroadcast(makeGeneralIntent(info, bcastType));
     }
 
-    private void sendDataActivityBroadcast(int deviceType, boolean active, long tsNanos) {
-        Intent intent = new Intent(ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE);
-        intent.putExtra(ConnectivityManager.EXTRA_DEVICE_TYPE, deviceType);
-        intent.putExtra(ConnectivityManager.EXTRA_IS_ACTIVE, active);
-        intent.putExtra(ConnectivityManager.EXTRA_REALTIME_NS, tsNanos);
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL,
-                    RECEIVE_DATA_ACTIVITY_CHANGE, null, null, 0, null, null);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
     private void sendStickyBroadcast(Intent intent) {
         synchronized (this) {
             if (!mSystemReady
@@ -2393,74 +2426,6 @@
     }
 
     /**
-     * Setup data activity tracking for the given network.
-     *
-     * Every {@code setupDataActivityTracking} should be paired with a
-     * {@link #removeDataActivityTracking} for cleanup.
-     */
-    private void setupDataActivityTracking(NetworkAgentInfo networkAgent) {
-        final String iface = networkAgent.linkProperties.getInterfaceName();
-
-        final int timeout;
-        final int type;
-
-        if (networkAgent.networkCapabilities.hasTransport(
-                NetworkCapabilities.TRANSPORT_CELLULAR)) {
-            timeout = Settings.Global.getInt(mContext.getContentResolver(),
-                                             Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE,
-                                             10);
-            type = NetworkCapabilities.TRANSPORT_CELLULAR;
-        } else if (networkAgent.networkCapabilities.hasTransport(
-                NetworkCapabilities.TRANSPORT_WIFI)) {
-            timeout = Settings.Global.getInt(mContext.getContentResolver(),
-                                             Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI,
-                                             15);
-            type = NetworkCapabilities.TRANSPORT_WIFI;
-        } else {
-            return; // do not track any other networks
-        }
-
-        if (timeout > 0 && iface != null) {
-            try {
-                mNMS.addIdleTimer(iface, timeout, type);
-            } catch (Exception e) {
-                // You shall not crash!
-                loge("Exception in setupDataActivityTracking " + e);
-            }
-        }
-    }
-
-    /**
-     * Remove data activity tracking when network disconnects.
-     */
-    private void removeDataActivityTracking(NetworkAgentInfo networkAgent) {
-        final String iface = networkAgent.linkProperties.getInterfaceName();
-        final NetworkCapabilities caps = networkAgent.networkCapabilities;
-
-        if (iface != null && (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
-                              caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))) {
-            try {
-                // the call fails silently if no idle timer setup for this interface
-                mNMS.removeIdleTimer(iface);
-            } catch (Exception e) {
-                loge("Exception in removeDataActivityTracking " + e);
-            }
-        }
-    }
-
-    /**
-     * Update data activity tracking when network state is updated.
-     */
-    private void updateDataActivityTracking(NetworkAgentInfo newNetwork,
-            NetworkAgentInfo oldNetwork) {
-        if (newNetwork != null) {
-            setupDataActivityTracking(newNetwork);
-        }
-        if (oldNetwork != null) {
-            removeDataActivityTracking(oldNetwork);
-        }
-    }
-    /**
      * Reads the network specific MTU size from resources.
      * and set it on it's iface.
      */
@@ -2874,13 +2839,7 @@
                         Log.wtf(TAG, "Non-virtual networks cannot have underlying networks");
                         break;
                     }
-                    final ArrayList<Network> underlying;
-                    try {
-                        underlying = ((Bundle) arg.second).getParcelableArrayList(
-                                NetworkAgent.UNDERLYING_NETWORKS_KEY);
-                    } catch (NullPointerException | ClassCastException e) {
-                        break;
-                    }
+                    final List<Network> underlying = (List<Network>) arg.second;
                     final Network[] oldUnderlying = nai.declaredUnderlyingNetworks;
                     nai.declaredUnderlyingNetworks = (underlying != null)
                             ? underlying.toArray(new Network[0]) : null;
@@ -2893,6 +2852,7 @@
                         updateCapabilitiesForNetwork(nai);
                         notifyIfacesChangedForNetworkStats();
                     }
+                    break;
                 }
             }
         }
@@ -3454,6 +3414,8 @@
         // of rematchAllNetworksAndRequests
         notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
         mKeepaliveTracker.handleStopAllKeepalives(nai, SocketKeepalive.ERROR_INVALID_NETWORK);
+
+        mQosCallbackTracker.handleNetworkReleased(nai.network);
         for (String iface : nai.linkProperties.getAllInterfaceNames()) {
             // Disable wakeup packet monitoring for each interface.
             wakeupModifyInterface(iface, nai.networkCapabilities, false);
@@ -3484,7 +3446,7 @@
         // the default network disconnecting. Find out why, fix the rematch code, and delete this.
         if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
             mDefaultNetworkNai = null;
-            updateDataActivityTracking(null /* newNetwork */, nai);
+            mNetworkActivityTracker.updateDataActivityTracking(null /* newNetwork */, nai);
             notifyLockdownVpn(nai);
             ensureNetworkTransitionWakelock(nai.toShortString());
         }
@@ -3723,7 +3685,7 @@
         nri.unlinkDeathRecipient();
         mNetworkRequests.remove(nri.request);
 
-        decrementNetworkRequestPerUidCount(nri);
+        mNetworkRequestCounter.decrementCount(nri.mUid);
 
         mNetworkRequestInfoLogs.log("RELEASE " + nri);
         if (nri.request.isRequest()) {
@@ -3796,19 +3758,6 @@
         }
     }
 
-    private void decrementNetworkRequestPerUidCount(final NetworkRequestInfo nri) {
-        synchronized (mUidToNetworkRequestCount) {
-            final int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
-            if (requests < 1) {
-                Log.wtf(TAG, "BUG: too small request count " + requests + " for UID " + nri.mUid);
-            } else if (requests == 1) {
-                mUidToNetworkRequestCount.removeAt(mUidToNetworkRequestCount.indexOfKey(nri.mUid));
-            } else {
-                mUidToNetworkRequestCount.put(nri.mUid, requests - 1);
-            }
-        }
-    }
-
     @Override
     public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
         enforceNetworkStackSettingsOrSetup();
@@ -4635,6 +4584,10 @@
         Log.w(TAG, s);
     }
 
+    private static void logwtf(String s) {
+        Log.wtf(TAG, s);
+    }
+
     private static void loge(String s) {
         Log.e(TAG, s);
     }
@@ -5377,11 +5330,6 @@
     private final HashMap<Messenger, NetworkProviderInfo> mNetworkProviderInfos = new HashMap<>();
     private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests = new HashMap<>();
 
-    private static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
-    // Map from UID to number of NetworkRequests that UID has filed.
-    @GuardedBy("mUidToNetworkRequestCount")
-    private final SparseIntArray mUidToNetworkRequestCount = new SparseIntArray();
-
     private static class NetworkProviderInfo {
         public final String name;
         public final Messenger messenger;
@@ -5495,7 +5443,7 @@
             mBinder = null;
             mPid = getCallingPid();
             mUid = mDeps.getCallingUid();
-            enforceRequestCountLimit();
+            mNetworkRequestCounter.incrementCountOrThrow(mUid);
         }
 
         NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder) {
@@ -5508,7 +5456,7 @@
             mPid = getCallingPid();
             mUid = mDeps.getCallingUid();
             mPendingIntent = null;
-            enforceRequestCountLimit();
+            mNetworkRequestCounter.incrementCountOrThrow(mUid);
 
             try {
                 mBinder.linkToDeath(this, 0);
@@ -5545,17 +5493,6 @@
             return null;
         }
 
-        private void enforceRequestCountLimit() {
-            synchronized (mUidToNetworkRequestCount) {
-                int networkRequests = mUidToNetworkRequestCount.get(mUid, 0) + 1;
-                if (networkRequests >= MAX_NETWORK_REQUESTS_PER_UID) {
-                    throw new ServiceSpecificException(
-                            ConnectivityManager.Errors.TOO_MANY_REQUESTS);
-                }
-                mUidToNetworkRequestCount.put(mUid, networkRequests);
-            }
-        }
-
         void unlinkDeathRecipient() {
             if (mBinder != null) {
                 mBinder.unlinkToDeath(this, 0);
@@ -5777,9 +5714,14 @@
             // Policy already enforced.
             return;
         }
-        if (mPolicyManagerInternal.isUidRestrictedOnMeteredNetworks(uid)) {
-            // If UID is restricted, don't allow them to bring up metered APNs.
-            networkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            if (mPolicyManager.isUidRestrictedOnMeteredNetworks(uid)) {
+                // If UID is restricted, don't allow them to bring up metered APNs.
+                networkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -6114,7 +6056,7 @@
         final NetworkAgentInfo nai = new NetworkAgentInfo(na,
                 new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
                 currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
-                this, mNetd, mDnsResolver, mNMS, providerId, uid);
+                this, mNetd, mDnsResolver, mNMS, providerId, uid, mQosCallbackTracker);
 
         // Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
         processCapabilitiesFromAgent(nai, nc);
@@ -7314,7 +7256,8 @@
             if (oldDefaultNetwork != null) {
                 mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
             }
-            updateDataActivityTracking(newDefaultNetwork, oldDefaultNetwork);
+            mNetworkActivityTracker.updateDataActivityTracking(
+                    newDefaultNetwork, oldDefaultNetwork);
             // Notify system services of the new default.
             makeDefault(newDefaultNetwork);
 
@@ -7912,10 +7855,11 @@
     }
 
     @Override
-    public void startNattKeepaliveWithFd(Network network, FileDescriptor fd, int resourceId,
+    public void startNattKeepaliveWithFd(Network network, ParcelFileDescriptor pfd, int resourceId,
             int intervalSeconds, ISocketKeepaliveCallback cb, String srcAddr,
             String dstAddr) {
         try {
+            final FileDescriptor fd = pfd.getFileDescriptor();
             mKeepaliveTracker.startNattKeepalive(
                     getNetworkAgentInfoForNetwork(network), fd, resourceId,
                     intervalSeconds, cb,
@@ -7923,24 +7867,25 @@
         } finally {
             // FileDescriptors coming from AIDL calls must be manually closed to prevent leaks.
             // startNattKeepalive calls Os.dup(fd) before returning, so we can close immediately.
-            if (fd != null && Binder.getCallingPid() != Process.myPid()) {
-                IoUtils.closeQuietly(fd);
+            if (pfd != null && Binder.getCallingPid() != Process.myPid()) {
+                IoUtils.closeQuietly(pfd);
             }
         }
     }
 
     @Override
-    public void startTcpKeepalive(Network network, FileDescriptor fd, int intervalSeconds,
+    public void startTcpKeepalive(Network network, ParcelFileDescriptor pfd, int intervalSeconds,
             ISocketKeepaliveCallback cb) {
         try {
             enforceKeepalivePermission();
+            final FileDescriptor fd = pfd.getFileDescriptor();
             mKeepaliveTracker.startTcpKeepalive(
                     getNetworkAgentInfoForNetwork(network), fd, intervalSeconds, cb);
         } finally {
             // FileDescriptors coming from AIDL calls must be manually closed to prevent leaks.
             // startTcpKeepalive calls Os.dup(fd) before returning, so we can close immediately.
-            if (fd != null && Binder.getCallingPid() != Process.myPid()) {
-                IoUtils.closeQuietly(fd);
+            if (pfd != null && Binder.getCallingPid() != Process.myPid()) {
+                IoUtils.closeQuietly(pfd);
             }
         }
     }
@@ -8391,7 +8336,7 @@
             // Decrement the reference count for this NetworkRequestInfo. The reference count is
             // incremented when the NetworkRequestInfo is created as part of
             // enforceRequestCountLimit().
-            decrementNetworkRequestPerUidCount(nri);
+            mNetworkRequestCounter.decrementCount(nri.mUid);
             return;
         }
 
@@ -8457,7 +8402,7 @@
         // Decrement the reference count for this NetworkRequestInfo. The reference count is
         // incremented when the NetworkRequestInfo is created as part of
         // enforceRequestCountLimit().
-        decrementNetworkRequestPerUidCount(nri);
+        mNetworkRequestCounter.decrementCount(nri.mUid);
 
         iCb.unlinkToDeath(cbInfo, 0);
     }
@@ -8666,4 +8611,194 @@
 
         notifyDataStallSuspected(p, network.getNetId());
     }
+
+    private final LegacyNetworkActivityTracker mNetworkActivityTracker;
+
+    /**
+     * Class used for updating network activity tracking with netd and notify network activity
+     * changes.
+     */
+    private static final class LegacyNetworkActivityTracker {
+        private final Context mContext;
+        private final INetworkManagementService mNMS;
+
+        LegacyNetworkActivityTracker(@NonNull Context context,
+                @NonNull INetworkManagementService nms) {
+            mContext = context;
+            mNMS = nms;
+            try {
+                mNMS.registerObserver(mDataActivityObserver);
+            } catch (RemoteException e) {
+                loge("Error registering observer :" + e);
+            }
+        }
+
+        // TODO: Migrate away the dependency with INetworkManagementEventObserver.
+        private final INetworkManagementEventObserver mDataActivityObserver =
+                new BaseNetworkObserver() {
+                    @Override
+                    public void interfaceClassDataActivityChanged(int transportType, boolean active,
+                            long tsNanos, int uid) {
+                        sendDataActivityBroadcast(transportTypeToLegacyType(transportType), active,
+                                tsNanos);
+                    }
+                };
+
+        // This is deprecated and only to support legacy use cases.
+        private int transportTypeToLegacyType(int type) {
+            switch (type) {
+                case NetworkCapabilities.TRANSPORT_CELLULAR:
+                    return ConnectivityManager.TYPE_MOBILE;
+                case NetworkCapabilities.TRANSPORT_WIFI:
+                    return ConnectivityManager.TYPE_WIFI;
+                case NetworkCapabilities.TRANSPORT_BLUETOOTH:
+                    return ConnectivityManager.TYPE_BLUETOOTH;
+                case NetworkCapabilities.TRANSPORT_ETHERNET:
+                    return ConnectivityManager.TYPE_ETHERNET;
+                default:
+                    loge("Unexpected transport in transportTypeToLegacyType: " + type);
+            }
+            return ConnectivityManager.TYPE_NONE;
+        }
+
+        public void sendDataActivityBroadcast(int deviceType, boolean active, long tsNanos) {
+            final Intent intent = new Intent(ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE);
+            intent.putExtra(ConnectivityManager.EXTRA_DEVICE_TYPE, deviceType);
+            intent.putExtra(ConnectivityManager.EXTRA_IS_ACTIVE, active);
+            intent.putExtra(ConnectivityManager.EXTRA_REALTIME_NS, tsNanos);
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL,
+                        RECEIVE_DATA_ACTIVITY_CHANGE,
+                        null /* resultReceiver */,
+                        null /* scheduler */,
+                        0 /* initialCode */,
+                        null /* initialData */,
+                        null /* initialExtra */);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        /**
+         * Setup data activity tracking for the given network.
+         *
+         * Every {@code setupDataActivityTracking} should be paired with a
+         * {@link #removeDataActivityTracking} for cleanup.
+         */
+        private void setupDataActivityTracking(NetworkAgentInfo networkAgent) {
+            final String iface = networkAgent.linkProperties.getInterfaceName();
+
+            final int timeout;
+            final int type;
+
+            if (networkAgent.networkCapabilities.hasTransport(
+                    NetworkCapabilities.TRANSPORT_CELLULAR)) {
+                timeout = Settings.Global.getInt(mContext.getContentResolver(),
+                        Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE,
+                        10);
+                type = NetworkCapabilities.TRANSPORT_CELLULAR;
+            } else if (networkAgent.networkCapabilities.hasTransport(
+                    NetworkCapabilities.TRANSPORT_WIFI)) {
+                timeout = Settings.Global.getInt(mContext.getContentResolver(),
+                        Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI,
+                        15);
+                type = NetworkCapabilities.TRANSPORT_WIFI;
+            } else {
+                return; // do not track any other networks
+            }
+
+            if (timeout > 0 && iface != null) {
+                try {
+                    // TODO: Access INetd directly instead of NMS
+                    mNMS.addIdleTimer(iface, timeout, type);
+                } catch (Exception e) {
+                    // You shall not crash!
+                    loge("Exception in setupDataActivityTracking " + e);
+                }
+            }
+        }
+
+        /**
+         * Remove data activity tracking when network disconnects.
+         */
+        private void removeDataActivityTracking(NetworkAgentInfo networkAgent) {
+            final String iface = networkAgent.linkProperties.getInterfaceName();
+            final NetworkCapabilities caps = networkAgent.networkCapabilities;
+
+            if (iface != null && (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
+                    || caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))) {
+                try {
+                    // the call fails silently if no idle timer setup for this interface
+                    // TODO: Access INetd directly instead of NMS
+                    mNMS.removeIdleTimer(iface);
+                } catch (Exception e) {
+                    // You shall not crash!
+                    loge("Exception in removeDataActivityTracking " + e);
+                }
+            }
+        }
+
+        /**
+         * Update data activity tracking when network state is updated.
+         */
+        public void updateDataActivityTracking(NetworkAgentInfo newNetwork,
+                NetworkAgentInfo oldNetwork) {
+            if (newNetwork != null) {
+                setupDataActivityTracking(newNetwork);
+            }
+            if (oldNetwork != null) {
+                removeDataActivityTracking(oldNetwork);
+            }
+        }
+    }
+    /**
+     * Registers {@link QosSocketFilter} with {@link IQosCallback}.
+     *
+     * @param socketInfo the socket information
+     * @param callback the callback to register
+     */
+    @Override
+    public void registerQosSocketCallback(@NonNull final QosSocketInfo socketInfo,
+            @NonNull final IQosCallback callback) {
+        final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(socketInfo.getNetwork());
+        if (nai == null || nai.networkCapabilities == null) {
+            try {
+                callback.onError(QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED);
+            } catch (final RemoteException ex) {
+                loge("registerQosCallbackInternal: RemoteException", ex);
+            }
+            return;
+        }
+        registerQosCallbackInternal(new QosSocketFilter(socketInfo), callback, nai);
+    }
+
+    /**
+     * Register a {@link IQosCallback} with base {@link QosFilter}.
+     *
+     * @param filter the filter to register
+     * @param callback the callback to register
+     * @param nai the agent information related to the filter's network
+     */
+    @VisibleForTesting
+    public void registerQosCallbackInternal(@NonNull final QosFilter filter,
+            @NonNull final IQosCallback callback, @NonNull final NetworkAgentInfo nai) {
+        if (filter == null) throw new IllegalArgumentException("filter must be non-null");
+        if (callback == null) throw new IllegalArgumentException("callback must be non-null");
+
+        if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
+            enforceConnectivityRestrictedNetworksPermission();
+        }
+        mQosCallbackTracker.registerCallback(callback, filter, nai);
+    }
+
+    /**
+     * Unregisters the given callback.
+     *
+     * @param callback the callback to unregister
+     */
+    @Override
+    public void unregisterQosCallback(@NonNull final IQosCallback callback) {
+        mQosCallbackTracker.unregisterCallback(callback);
+    }
 }
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 1ea4a89..d30a640 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -405,6 +405,9 @@
             if (mLastPowerStateFromRadio != powerState) {
                 mLastPowerStateFromRadio = powerState;
                 try {
+                    // TODO: The interface changes that comes from netd are handled by BSS itself.
+                    // There are still events caused by setting or removing idle timer, so keep
+                    // reporting from here until setting idler timer moved to CS.
                     getBatteryStats().noteMobileRadioPowerState(powerState, tsNanos, uid);
                 } catch (RemoteException e) {
                 }
@@ -415,6 +418,9 @@
             if (mLastPowerStateFromWifi != powerState) {
                 mLastPowerStateFromWifi = powerState;
                 try {
+                    // TODO: The interface changes that comes from netd are handled by BSS itself.
+                    // There are still events caused by setting or removing idle timer, so keep
+                    // reporting from here until setting idler timer moved to CS.
                     getBatteryStats().noteWifiRadioPowerState(powerState, tsNanos, uid);
                 } catch (RemoteException e) {
                 }
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 2fdc796..76db019 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -31,16 +31,19 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.ParcelUuid;
 import android.os.PersistableBundle;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.os.UserHandle;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
+import android.util.Log;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
@@ -155,6 +158,11 @@
 
     @NonNull private final PersistableBundleUtils.LockingReadWriteHelper mConfigDiskRwHelper;
 
+    @GuardedBy("mLock")
+    @NonNull
+    private final Map<IBinder, PolicyListenerBinderDeath> mRegisteredPolicyListeners =
+            new ArrayMap<>();
+
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) {
         mContext = requireNonNull(context, "Missing context");
@@ -497,19 +505,60 @@
         }
     }
 
+    /** Binder death recipient used to remove a registered policy listener. */
+    private class PolicyListenerBinderDeath implements Binder.DeathRecipient {
+        @NonNull private final IVcnUnderlyingNetworkPolicyListener mListener;
+
+        PolicyListenerBinderDeath(@NonNull IVcnUnderlyingNetworkPolicyListener listener) {
+            mListener = listener;
+        }
+
+        @Override
+        public void binderDied() {
+            Log.e(TAG, "app died without removing VcnUnderlyingNetworkPolicyListener");
+            removeVcnUnderlyingNetworkPolicyListener(mListener);
+        }
+    }
+
     /** Adds the provided listener for receiving VcnUnderlyingNetworkPolicy updates. */
+    @GuardedBy("mLock")
     @Override
     public void addVcnUnderlyingNetworkPolicyListener(
-            IVcnUnderlyingNetworkPolicyListener listener) {
-        // TODO(b/175739863): implement policy listener registration
-        throw new UnsupportedOperationException("Not yet implemented");
+            @NonNull IVcnUnderlyingNetworkPolicyListener listener) {
+        requireNonNull(listener, "listener was null");
+
+        mContext.enforceCallingPermission(
+                android.Manifest.permission.NETWORK_FACTORY,
+                "Must have permission NETWORK_FACTORY to register a policy listener");
+
+        PolicyListenerBinderDeath listenerBinderDeath = new PolicyListenerBinderDeath(listener);
+
+        synchronized (mLock) {
+            mRegisteredPolicyListeners.put(listener.asBinder(), listenerBinderDeath);
+
+            try {
+                listener.asBinder().linkToDeath(listenerBinderDeath, 0 /* flags */);
+            } catch (RemoteException e) {
+                // Remote binder already died - cleanup registered Listener
+                listenerBinderDeath.binderDied();
+            }
+        }
     }
 
     /** Removes the provided listener from receiving VcnUnderlyingNetworkPolicy updates. */
+    @GuardedBy("mLock")
     @Override
     public void removeVcnUnderlyingNetworkPolicyListener(
-            IVcnUnderlyingNetworkPolicyListener listener) {
-        // TODO(b/175739863): implement policy listener unregistration
-        throw new UnsupportedOperationException("Not yet implemented");
+            @NonNull IVcnUnderlyingNetworkPolicyListener listener) {
+        requireNonNull(listener, "listener was null");
+
+        synchronized (mLock) {
+            PolicyListenerBinderDeath listenerBinderDeath =
+                    mRegisteredPolicyListeners.remove(listener.asBinder());
+
+            if (listenerBinderDeath != null) {
+                listener.asBinder().unlinkToDeath(listenerBinderDeath, 0 /* flags */);
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 2c83da5..a6a00fb 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -129,8 +129,10 @@
                 Slog.d(TAG, "Vibration thread finished with status " + status);
             }
             synchronized (mLock) {
-                mThread = null;
-                reportFinishVibrationLocked(status);
+                if (mCurrentVibration != null && mCurrentVibration.id == vibrationId) {
+                    mThread = null;
+                    reportFinishVibrationLocked(status);
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index fcb32a1..e476ca9 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -5373,7 +5373,7 @@
             for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
                 final ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i);
                 if (pr.uid == callingUid) {
-                    if (pr.getWindowProcessController().areBackgroundActivityStartsAllowed()) {
+                    if (pr.getWindowProcessController().areBackgroundFgsStartsAllowed()) {
                         ret = FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER;
                         break;
                     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index aada21d..df90a40 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -180,6 +180,7 @@
 import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.appwidget.AppWidgetManager;
+import android.compat.Compatibility;
 import android.content.AutofillOptions;
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
@@ -303,6 +304,7 @@
 import com.android.internal.app.ProcessMap;
 import com.android.internal.app.SystemUserHomeActivity;
 import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.compat.CompatibilityChangeConfig;
 import com.android.internal.content.PackageHelper;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
@@ -2533,11 +2535,8 @@
             Slog.d(TAG_SWITCH, "updateActivityUsageStats: comp="
                     + activity + " hash=" + appToken.hashCode() + " event=" + event);
         }
-        synchronized (this) {
-            if (mUsageStatsService != null) {
-                mUsageStatsService.reportEvent(activity, userId, event, appToken.hashCode(),
-                        taskRoot);
-            }
+        if (mUsageStatsService != null) {
+            mUsageStatsService.reportEvent(activity, userId, event, appToken.hashCode(), taskRoot);
         }
         if (mContentCaptureService != null && (event == Event.ACTIVITY_PAUSED
                 || event == Event.ACTIVITY_RESUMED || event == Event.ACTIVITY_STOPPED
@@ -2557,10 +2556,8 @@
             Slog.d(TAG_SWITCH, "updateActivityUsageStats: package="
                     + packageName + " event=" + event);
         }
-        synchronized (this) {
-            if (mUsageStatsService != null) {
-                mUsageStatsService.reportEvent(packageName, userId, event);
-            }
+        if (mUsageStatsService != null) {
+            mUsageStatsService.reportEvent(packageName, userId, event);
         }
     }
 
@@ -2575,12 +2572,10 @@
             Slog.d(TAG_SWITCH, "updateForegroundServiceUsageStats: comp="
                     + service + " started=" + started);
         }
-        synchronized (this) {
-            if (mUsageStatsService != null) {
-                mUsageStatsService.reportEvent(service, userId,
-                        started ? UsageEvents.Event.FOREGROUND_SERVICE_START
-                                : UsageEvents.Event.FOREGROUND_SERVICE_STOP, 0, null);
-            }
+        if (mUsageStatsService != null) {
+            mUsageStatsService.reportEvent(service, userId,
+                    started ? UsageEvents.Event.FOREGROUND_SERVICE_START
+                            : UsageEvents.Event.FOREGROUND_SERVICE_STOP, 0, null);
         }
     }
 
@@ -6098,7 +6093,7 @@
         return pfd;
     }
 
-    void reportGlobalUsageEventLocked(int event) {
+    void reportGlobalUsageEvent(int event) {
         final int currentUserId = mUserController.getCurrentUserId();
         mUsageStatsService.reportEvent(Event.DEVICE_EVENT_PACKAGE_NAME, currentUserId, event);
         int[] profiles = mUserController.getCurrentProfileIds();
@@ -6112,8 +6107,8 @@
         }
     }
 
-    void reportCurWakefulnessUsageEventLocked() {
-        reportGlobalUsageEventLocked(mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE
+    void reportCurWakefulnessUsageEvent() {
+        reportGlobalUsageEvent(mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE
                 ? UsageEvents.Event.SCREEN_INTERACTIVE
                 : UsageEvents.Event.SCREEN_NON_INTERACTIVE);
     }
@@ -6127,7 +6122,7 @@
             if (wasAwake != isAwake) {
                 // Also update state in a special way for running foreground services UI.
                 mServices.updateScreenStateLocked(isAwake);
-                reportCurWakefulnessUsageEventLocked();
+                reportCurWakefulnessUsageEvent();
                 mActivityTaskManager.onScreenAwakeChanged(isAwake);
                 mOomAdjProfiler.onWakefulnessChanged(wakefulness);
             }
@@ -14351,6 +14346,8 @@
             if (disableHiddenApiChecks || disableTestApiChecks) {
                 enforceCallingPermission(android.Manifest.permission.DISABLE_HIDDEN_API_CHECKS,
                         "disable hidden API checks");
+
+                enableTestApiAccess(ii.packageName);
             }
 
             final long origId = Binder.clearCallingIdentity();
@@ -14518,6 +14515,25 @@
                     app.userId,
                     "finished inst");
         }
+
+        disableTestApiAccess(app.info.packageName);
+    }
+
+    private void enableTestApiAccess(String packageName) {
+        if (mPlatformCompat != null) {
+            Compatibility.ChangeConfig config = new Compatibility.ChangeConfig(
+                    Collections.singleton(166236554L /* VMRuntime.ALLOW_TEST_API_ACCESS */),
+                    Collections.emptySet());
+            CompatibilityChangeConfig override = new CompatibilityChangeConfig(config);
+            mPlatformCompat.setOverridesForTest(override, packageName);
+        }
+    }
+
+    private void disableTestApiAccess(String packageName) {
+        if (mPlatformCompat != null) {
+            mPlatformCompat.clearOverrideForTest(166236554L /* VMRuntime.ALLOW_TEST_API_ACCESS */,
+                    packageName);
+        }
     }
 
     public void finishInstrumentation(IApplicationThread target,
@@ -16506,11 +16522,9 @@
         }
 
         public void reportCurKeyguardUsageEvent(boolean keyguardShowing) {
-            synchronized(ActivityManagerService.this) {
-                ActivityManagerService.this.reportGlobalUsageEventLocked(keyguardShowing
-                        ? UsageEvents.Event.KEYGUARD_SHOWN
-                        : UsageEvents.Event.KEYGUARD_HIDDEN);
-            }
+            ActivityManagerService.this.reportGlobalUsageEvent(keyguardShowing
+                    ? UsageEvents.Event.KEYGUARD_SHOWN
+                    : UsageEvents.Event.KEYGUARD_HIDDEN);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index ada7eea..a3fac05 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -593,7 +593,7 @@
         }
 
         if (modemInfo != null) {
-            mStats.updateMobileRadioState(modemInfo, elapsedRealtime, uptime);
+            mStats.noteModemControllerActivity(modemInfo, elapsedRealtime, uptime);
         }
 
         if (updateFlags == UPDATE_ALL) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 4fb7abb..b1cbb4a 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -21,6 +21,8 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.net.INetworkManagementEventObserver;
+import android.net.NetworkCapabilities;
 import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
 import android.os.BatteryUsageStats;
@@ -29,6 +31,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.INetworkManagementService;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.ParcelFormatException;
@@ -36,6 +39,7 @@
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -54,6 +58,7 @@
 import android.telephony.TelephonyManager;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.os.BatteryStatsHelper;
 import com.android.internal.os.BatteryStatsImpl;
@@ -68,6 +73,7 @@
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.Watchdog;
+import com.android.server.net.BaseNetworkObserver;
 import com.android.server.pm.UserManagerInternal;
 
 import java.io.File;
@@ -123,6 +129,40 @@
     private final Handler mHandler;
     private final Object mLock = new Object();
 
+    @GuardedBy("mStats")
+    private int mLastPowerStateFromRadio = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+    @GuardedBy("mStats")
+    private int mLastPowerStateFromWifi = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+    private final INetworkManagementEventObserver mActivityChangeObserver =
+            new BaseNetworkObserver() {
+                @Override
+                public void interfaceClassDataActivityChanged(int transportType, boolean active,
+                        long tsNanos, int uid) {
+                    final int powerState = active
+                            ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
+                            : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+                    final long timestampNanos;
+                    if (tsNanos <= 0) {
+                        timestampNanos = SystemClock.elapsedRealtimeNanos();
+                    } else {
+                        timestampNanos = tsNanos;
+                    }
+
+                    switch (transportType) {
+                        case NetworkCapabilities.TRANSPORT_CELLULAR:
+                            noteMobileRadioPowerState(powerState, timestampNanos, uid);
+                            break;
+                        case NetworkCapabilities.TRANSPORT_WIFI:
+                            noteWifiRadioPowerState(powerState, timestampNanos, uid);
+                            break;
+                        default:
+                            Slog.d(TAG, "Received unexpected transport in "
+                                    + "interfaceClassDataActivityChanged unexpected type: "
+                                    + transportType);
+                    }
+                }
+            };
+
     /**
      * Replaces the information in the given rpmStats with up-to-date information.
      */
@@ -225,6 +265,13 @@
     public void systemServicesReady() {
         mStats.systemServicesReady(mContext);
         mWorker.systemServicesReady();
+        final INetworkManagementService nms = INetworkManagementService.Stub.asInterface(
+                ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
+        try {
+            nms.registerObserver(mActivityChangeObserver);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Could not register INetworkManagement event observer " + e);
+        }
         Watchdog.getInstance().addMonitor(this);
     }
 
@@ -1040,6 +1087,10 @@
             mHandler.post(() -> {
                 final boolean update;
                 synchronized (mStats) {
+                    // Ignore if no power state change.
+                    if (mLastPowerStateFromRadio == powerState) return;
+
+                    mLastPowerStateFromRadio = powerState;
                     update = mStats.noteMobileRadioPowerStateLocked(powerState, timestampNs, uid,
                             elapsedRealtime, uptime);
                 }
@@ -1343,6 +1394,10 @@
                 // There was a change in WiFi power state.
                 // Collect data now for the past activity.
                 synchronized (mStats) {
+                    // Ignore if no power state change.
+                    if (mLastPowerStateFromWifi == powerState) return;
+
+                    mLastPowerStateFromWifi = powerState;
                     if (mStats.isOnBattery()) {
                         final String type =
                                 (powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
@@ -1798,7 +1853,7 @@
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
-                mStats.updateMobileRadioState(info, elapsedRealtime, uptime);
+                mStats.noteModemControllerActivity(info, elapsedRealtime, uptime);
             });
         }
     }
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 9eb7c07..dd09a1c 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -133,7 +133,7 @@
     static final int REPORT_UNFREEZE_MSG = 4;
 
     //TODO:change this static definition into a configurable flag.
-    static final int FREEZE_TIMEOUT_MS = 10000;
+    static final long FREEZE_TIMEOUT_MS = 600000;
 
     static final int DO_FREEZE = 1;
     static final int REPORT_UNFREEZE = 2;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 47b7e1b..2273779 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -340,12 +340,23 @@
     private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
 
     /**
-     * Enable memory tag checks in non-system apps. This flag will only have an effect on
-     * hardware supporting the ARM Memory Tagging Extension (MTE).
+     * Enable asynchronous (ASYNC) memory tag checking in this process. This
+     * flag will only have an effect on hardware supporting the ARM Memory
+     * Tagging Extension (MTE).
      */
     @ChangeId
     @Disabled
-    private static final long NATIVE_MEMORY_TAGGING = 135772972; // This is a bug id.
+    private static final long NATIVE_MEMTAG_ASYNC = 135772972; // This is a bug id.
+
+    /**
+     * Enable synchronous (SYNC) memory tag checking in this process. This flag
+     * will only have an effect on hardware supporting the ARM Memory Tagging
+     * Extension (MTE). If both NATIVE_MEMTAG_ASYNC and this option is selected,
+     * this option takes preference and MTE is enabled in SYNC mode.
+     */
+    @ChangeId
+    @Disabled
+    private static final long NATIVE_MEMTAG_SYNC = 177438394; // This is a bug id.
 
     /**
      * Enable sampled memory bug detection in the app.
@@ -1655,23 +1666,23 @@
         return gidArray;
     }
 
-    private boolean shouldEnableMemoryTagging(ProcessRecord app) {
+    // Returns the memory tagging level to be enabled. If memory tagging isn't
+    // requested, returns zero.
+    private int getMemtagLevel(ProcessRecord app) {
         // Ensure the hardware + kernel actually supports MTE.
         if (!Zygote.nativeSupportsMemoryTagging()) {
-            return false;
+            return 0;
         }
 
-        // Enable MTE for system apps if supported.
-        if ((app.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-            return true;
+        if (mPlatformCompat.isChangeEnabled(NATIVE_MEMTAG_SYNC, app.info)) {
+            return Zygote.MEMORY_TAG_LEVEL_SYNC;
         }
 
-        // Enable MTE if the compat feature is enabled.
-        if (mPlatformCompat.isChangeEnabled(NATIVE_MEMORY_TAGGING, app.info)) {
-            return true;
+        if (mPlatformCompat.isChangeEnabled(NATIVE_MEMTAG_ASYNC, app.info)) {
+            return Zygote.MEMORY_TAG_LEVEL_ASYNC;
         }
 
-        return false;
+        return 0;
     }
 
     private boolean shouldEnableTaggedPointers(ProcessRecord app) {
@@ -1695,8 +1706,9 @@
 
     private int decideTaggingLevel(ProcessRecord app) {
         // Check MTE support first, as it should take precedence over TBI.
-        if (shouldEnableMemoryTagging(app)) {
-            return Zygote.MEMORY_TAG_LEVEL_ASYNC;
+        int memtagLevel = getMemtagLevel(app);
+        if (memtagLevel != 0) {
+            return memtagLevel;
         }
 
         if (shouldEnableTaggedPointers(app)) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index ffe1d68..3bbc837 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -3046,15 +3046,11 @@
         }
 
         void reportGlobalUsageEventLocked(int event) {
-            synchronized (mService) {
-                mService.reportGlobalUsageEventLocked(event);
-            }
+            mService.reportGlobalUsageEvent(event);
         }
 
         void reportCurWakefulnessUsageEvent() {
-            synchronized (mService) {
-                mService.reportCurWakefulnessUsageEventLocked();
-            }
+            mService.reportCurWakefulnessUsageEvent();
         }
 
         void taskSupervisorRemoveUser(@UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 26f5c4c..9aea7c4 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -154,6 +154,8 @@
 
         mPreferredDeviceforComm = null;
         initCommunicationStrategyId();
+
+        mSystemServer.registerUserStartedReceiver(mContext);
     }
 
     /*package*/ Context getContext() {
@@ -993,6 +995,10 @@
         }
     }
 
+    /*package*/ void broadcastStickyIntentToCurrentProfileGroup(Intent intent) {
+        mSystemServer.broadcastStickyIntentToCurrentProfileGroup(intent);
+    }
+
     /*package*/ void dump(PrintWriter pw, String prefix) {
         if (mBrokerHandler != null) {
             pw.println(prefix + "Message handler (watch for unhandled messages):");
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 82586b8..076cbff 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -16,7 +16,6 @@
 package com.android.server.audio;
 
 import android.annotation.NonNull;
-import android.app.ActivityManager;
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
@@ -37,7 +36,6 @@
 import android.os.Binder;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -1270,7 +1268,7 @@
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_CURRENT);
+            mDeviceBroker.broadcastStickyIntentToCurrentProfileGroup(intent);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 7115c9a..c94b48c 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -572,9 +572,9 @@
 
     // Pre-scale for Bluetooth Absolute Volume
     private float[] mPrescaleAbsoluteVolume = new float[] {
-        0.5f,    // Pre-scale for index 1
-        0.7f,    // Pre-scale for index 2
-        0.85f,   // Pre-scale for index 3
+        0.6f,    // Pre-scale for index 1
+        0.8f,    // Pre-scale for index 2
+        0.9f,   // Pre-scale for index 3
     };
 
     private NotificationManager mNm;
@@ -5591,7 +5591,9 @@
                 profile, suppressNoisyIntent, a2dpVolume);
     }
 
-    /*package*/ void setMusicMute(boolean mute) {
+    /** only public for mocking/spying, do not call outside of AudioService */
+    @VisibleForTesting
+    public void setMusicMute(boolean mute) {
         mStreamStates[AudioSystem.STREAM_MUSIC].muteInternally(mute);
     }
 
@@ -7071,7 +7073,9 @@
         }
     }
 
-    /*package*/ void checkMusicActive(int deviceType, String caller) {
+    /** only public for mocking/spying, do not call outside of AudioService */
+    @VisibleForTesting
+    public void checkMusicActive(int deviceType, String caller) {
         if (mSafeMediaVolumeDevices.contains(deviceType)) {
             sendMsg(mAudioHandler,
                     MSG_CHECK_MUSIC_ACTIVE,
diff --git a/services/core/java/com/android/server/audio/SystemServerAdapter.java b/services/core/java/com/android/server/audio/SystemServerAdapter.java
index 68893f8..22456bc 100644
--- a/services/core/java/com/android/server/audio/SystemServerAdapter.java
+++ b/services/core/java/com/android/server/audio/SystemServerAdapter.java
@@ -18,11 +18,20 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
 import android.media.AudioManager;
 import android.os.Binder;
 import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
 
 import java.util.Objects;
 
@@ -82,4 +91,58 @@
             Binder.restoreCallingIdentity(ident);
         }
     }
+
+    /**
+     * Send sticky broadcast to current user's profile group (including current user)
+     */
+    @VisibleForTesting
+    public void broadcastStickyIntentToCurrentProfileGroup(Intent intent) {
+        int[] profileIds = LocalServices.getService(
+                ActivityManagerInternal.class).getCurrentProfileIds();
+        for (int userId : profileIds) {
+            ActivityManager.broadcastStickyIntent(intent, userId);
+        }
+    }
+
+    /**
+     * Broadcast sticky intents when a profile is started. This is needed because newly created
+     * profiles would not receive the intents until the next state change.
+     */
+    /*package*/ void registerUserStartedReceiver(Context context) {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_USER_STARTED);
+        context.registerReceiverAsUser(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (Intent.ACTION_USER_STARTED.equals(intent.getAction())) {
+                    final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+                            UserHandle.USER_NULL);
+                    if (userId == UserHandle.USER_NULL) {
+                        return;
+                    }
+
+                    UserManager userManager = context.getSystemService(UserManager.class);
+                    final UserInfo profileParent = userManager.getProfileParent(userId);
+                    if (profileParent == null) {
+                        return;
+                    }
+
+                    // get sticky intents from parent and broadcast them to the started profile
+                    broadcastProfileParentStickyIntent(context, AudioManager.ACTION_HDMI_AUDIO_PLUG,
+                            userId, profileParent.id);
+                    broadcastProfileParentStickyIntent(context, AudioManager.ACTION_HEADSET_PLUG,
+                            userId, profileParent.id);
+                }
+            }
+        }, UserHandle.ALL, filter, null, null);
+    }
+
+    private void broadcastProfileParentStickyIntent(Context context, String intentAction,
+            int profileId, int parentId) {
+        Intent intent = context.registerReceiverAsUser(/*receiver*/ null, UserHandle.of(parentId),
+                new IntentFilter(intentAction), /*broadcastPermission*/ null, /*scheduler*/ null);
+        if (intent != null) {
+            ActivityManager.broadcastStickyIntent(intent, profileId);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 52152ab..14292d9c 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -37,7 +37,6 @@
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.FingerprintSensorProperties;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -62,8 +61,6 @@
     private static final String TAG = "BiometricService/AuthSession";
     private static final boolean DEBUG = false;
 
-
-
     /*
      * Defined in biometrics.proto
      */
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 3387049..614c5f1 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -815,12 +815,16 @@
             final long ident = Binder.clearCallingIdentity();
             try {
                 if (args.length > 0 && "--proto".equals(args[0])) {
+                    final boolean clearSchedulerBuffer = args.length > 1
+                            && "--clear-scheduler-buffer".equals(args[1]);
+                    Slog.d(TAG, "ClearSchedulerBuffer: " + clearSchedulerBuffer);
                     final ProtoOutputStream proto = new ProtoOutputStream(fd);
                     proto.write(BiometricServiceStateProto.AUTH_SESSION_STATE,
                             mCurrentAuthSession != null ? mCurrentAuthSession.getState()
                                     : STATE_AUTH_IDLE);
                     for (BiometricSensor sensor : mSensors) {
-                        byte[] serviceState = sensor.impl.dumpSensorServiceStateProto();
+                        byte[] serviceState = sensor.impl
+                                .dumpSensorServiceStateProto(clearSchedulerBuffer);
                         proto.write(BiometricServiceStateProto.SENSOR_SERVICE_STATES, serviceState);
                     }
                     proto.flush();
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 5663495..3f6ae64 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -33,6 +33,7 @@
 import android.util.EventLog;
 import android.util.Slog;
 
+import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.Utils;
 
 import java.util.ArrayList;
@@ -298,4 +299,9 @@
             mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
         }
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_AUTHENTICATE;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index f3c37ef..8fa3bbb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -24,6 +24,8 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.NoSuchElementException;
 
 /**
@@ -80,6 +82,12 @@
     @NonNull protected Callback mCallback;
 
     /**
+     * Returns a ClientMonitorEnum constant defined in biometrics.proto
+     * @return
+     */
+    public abstract int getProtoEnum();
+
+    /**
      * @param context    system_server context
      * @param token      a unique token for the client
      * @param listener   recipient of related events (e.g. authentication)
@@ -195,10 +203,16 @@
         return mSensorId;
     }
 
+    @VisibleForTesting
+    public Callback getCallback() {
+        return mCallback;
+    }
+
     @Override
     public String toString() {
         return "{[" + mSequentialId + "] "
                 + this.getClass().getSimpleName()
+                + ", " + getProtoEnum()
                 + ", " + getOwnerString()
                 + ", " + getCookie() + "}";
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index aa7faf5..c86bfcb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -28,8 +28,11 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.biometrics.BiometricSchedulerProto;
+import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
 
 import java.io.PrintWriter;
@@ -51,6 +54,8 @@
 public class BiometricScheduler {
 
     private static final String BASE_TAG = "BiometricScheduler";
+    // Number of recent operations to keep in our logs for dumpsys
+    private static final int LOG_NUM_RECENT_OPERATIONS = 50;
 
     /**
      * Contains all the necessary information for a HAL operation.
@@ -200,6 +205,10 @@
     @VisibleForTesting @Nullable Operation mCurrentOperation;
     @NonNull private final ArrayDeque<CrashState> mCrashStates;
 
+    private int mTotalOperationsHandled;
+    private final int mRecentOperationsLimit;
+    @NonNull private final List<Integer> mRecentOperations;
+
     // Internal callback, notified when an operation is complete. Notifies the requester
     // that the operation is complete, before performing internal scheduler work (such as
     // starting the next client).
@@ -240,7 +249,12 @@
                             mCurrentOperation.mClientMonitor.getSensorId(), false /* active */);
                 }
 
+                if (mRecentOperations.size() >= mRecentOperationsLimit) {
+                    mRecentOperations.remove(0);
+                }
+                mRecentOperations.add(mCurrentOperation.mClientMonitor.getProtoEnum());
                 mCurrentOperation = null;
+                mTotalOperationsHandled++;
                 startNextOperationIfIdle();
             });
         }
@@ -249,13 +263,15 @@
     @VisibleForTesting
     BiometricScheduler(@NonNull String tag,
             @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
-            @NonNull IBiometricService biometricService) {
+            @NonNull IBiometricService biometricService, int recentOperationsLimit) {
         mBiometricTag = tag;
         mInternalCallback = new InternalCallback();
         mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher;
         mPendingOperations = new ArrayDeque<>();
         mBiometricService = biometricService;
         mCrashStates = new ArrayDeque<>();
+        mRecentOperationsLimit = recentOperationsLimit;
+        mRecentOperations = new ArrayList<>();
     }
 
     /**
@@ -267,7 +283,7 @@
     public BiometricScheduler(@NonNull String tag,
             @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
         this(tag, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface(
-                ServiceManager.getService(Context.BIOMETRIC_SERVICE)));
+                ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS);
     }
 
     /**
@@ -602,6 +618,24 @@
         }
     }
 
+    public byte[] dumpProtoState(boolean clearSchedulerBuffer) {
+        final ProtoOutputStream proto = new ProtoOutputStream();
+        proto.write(BiometricSchedulerProto.CURRENT_OPERATION, mCurrentOperation != null
+                ? mCurrentOperation.mClientMonitor.getProtoEnum() : BiometricsProto.CM_NONE);
+        proto.write(BiometricSchedulerProto.TOTAL_OPERATIONS, mTotalOperationsHandled);
+        Slog.d(getTag(), "Total operations: " + mTotalOperationsHandled);
+        for (int i = 0; i < mRecentOperations.size(); i++) {
+            Slog.d(getTag(), "Operation: " + mRecentOperations.get(i));
+            proto.write(BiometricSchedulerProto.RECENT_OPERATIONS, mRecentOperations.get(i));
+        }
+        proto.flush();
+
+        if (clearSchedulerBuffer) {
+            mRecentOperations.clear();
+        }
+        return proto.getBytes();
+    }
+
     /**
      * Clears the scheduler of anything work-related. This should be used for example when the
      * HAL dies.
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 8bf9680..8d81016 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -24,6 +24,8 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.BiometricsProto;
+
 import java.util.Arrays;
 
 /**
@@ -106,4 +108,9 @@
                 false /* enrollSuccessful */);
         super.onError(error, vendorCode);
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_ENROLL;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
index 6a622c3..741946e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -23,6 +23,8 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.BiometricsProto;
+
 public abstract class GenerateChallengeClient<T> extends HalClientMonitor<T> {
 
     private static final String TAG = "GenerateChallengeClient";
@@ -50,4 +52,9 @@
 
         startHalOperation();
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_GENERATE_CHALLENGE;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index 8529e81..ce24e5e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -24,6 +24,7 @@
 import android.util.Slog;
 
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.BiometricsProto;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -166,4 +167,9 @@
         }
         ((EnumerateConsumer) mCurrentTask).onEnumerationResult(identifier, remaining);
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_INTERNAL_CLEANUP;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index 2693f2f..9d19fdf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -24,6 +24,7 @@
 import android.util.Slog;
 
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.BiometricsProto;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -123,4 +124,9 @@
     public List<BiometricAuthenticator.Identifier> getUnknownHALTemplates() {
         return mUnknownHALTemplates;
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_ENUMERATE;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
index 630e5ea..cede4a7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
@@ -24,6 +24,8 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.BiometricsProto;
+
 import java.util.Map;
 
 /**
@@ -70,4 +72,9 @@
     public void unableToStart() {
 
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_INVALIDATE;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
index c97003b..5ba1b00 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
@@ -23,6 +23,8 @@
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.IInvalidationCallback;
 
+import com.android.server.biometrics.BiometricsProto;
+
 /**
  * ClientMonitor subclass responsible for coordination of authenticatorId invalidation of other
  * sensors. See {@link InvalidationClient} for the ClientMonitor subclass responsible for initiating
@@ -89,4 +91,9 @@
         mBiometricManager.invalidateAuthenticatorIds(getTargetUserId(), getSensorId(),
                 mInvalidationCallback);
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_INVALIDATION_REQUESTER;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java b/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java
index 3ca0691..edde3d4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java
@@ -67,10 +67,23 @@
         return mStatsClient;
     }
 
-    private boolean isAnyFieldUnknown() {
-        return mStatsModality == BiometricsProtoEnums.MODALITY_UNKNOWN
-                || mStatsAction == BiometricsProtoEnums.ACTION_UNKNOWN
-                || mStatsClient == BiometricsProtoEnums.CLIENT_UNKNOWN;
+    private boolean shouldSkipLogging() {
+        boolean shouldSkipLogging = (mStatsModality == BiometricsProtoEnums.MODALITY_UNKNOWN
+                || mStatsAction == BiometricsProtoEnums.ACTION_UNKNOWN);
+
+        if (mStatsModality == BiometricsProtoEnums.MODALITY_UNKNOWN) {
+            Slog.w(TAG, "Unknown field detected: MODALITY_UNKNOWN, will not report metric");
+        }
+
+        if (mStatsAction == BiometricsProtoEnums.ACTION_UNKNOWN) {
+            Slog.w(TAG, "Unknown field detected: ACTION_UNKNOWN, will not report metric");
+        }
+
+        if (mStatsClient == BiometricsProtoEnums.CLIENT_UNKNOWN) {
+            Slog.w(TAG, "Unknown field detected: CLIENT_UNKNOWN");
+        }
+
+        return shouldSkipLogging;
     }
 
     protected final void logOnAcquired(Context context, int acquiredInfo, int vendorCode,
@@ -101,7 +114,7 @@
                     + ", VendorCode: " + vendorCode);
         }
 
-        if (isAnyFieldUnknown()) {
+        if (shouldSkipLogging()) {
             return;
         }
 
@@ -138,7 +151,7 @@
             Slog.v(TAG, "Error latency: " + latency);
         }
 
-        if (isAnyFieldUnknown()) {
+        if (shouldSkipLogging()) {
             return;
         }
 
@@ -189,7 +202,7 @@
             Slog.v(TAG, "Authentication latency: " + latency);
         }
 
-        if (isAnyFieldUnknown()) {
+        if (shouldSkipLogging()) {
             return;
         }
 
@@ -219,7 +232,7 @@
             Slog.v(TAG, "Enroll latency: " + latency);
         }
 
-        if (isAnyFieldUnknown()) {
+        if (shouldSkipLogging()) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index 4ea48fd..e062695 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -25,6 +25,8 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.BiometricsProto;
+
 import java.util.Map;
 
 /**
@@ -90,4 +92,9 @@
             mCallback.onClientFinished(this, true /* success */);
         }
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_REMOVE;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
index 187193d..90fa1b4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
@@ -21,6 +21,8 @@
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.os.IBinder;
 
+import com.android.server.biometrics.BiometricsProto;
+
 public abstract class RevokeChallengeClient<T> extends HalClientMonitor<T> {
 
     public RevokeChallengeClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
@@ -42,4 +44,9 @@
         startHalOperation();
         mCallback.onClientFinished(this, true /* success */);
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_REVOKE_CHALLENGE;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
index 54ab2e5..f37cf18 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
@@ -52,8 +52,8 @@
     }
 
     @Override
-    public byte[] dumpSensorServiceStateProto() throws RemoteException {
-        return mFaceService.dumpSensorServiceStateProto(mSensorId);
+    public byte[] dumpSensorServiceStateProto(boolean clearSchedulerBuffer) throws RemoteException {
+        return mFaceService.dumpSensorServiceStateProto(mSensorId, clearSchedulerBuffer);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index f055d55..1a63dde 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -147,13 +147,13 @@
         }
 
         @Override
-        public byte[] dumpSensorServiceStateProto(int sensorId) {
+        public byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer) {
             Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
 
             final ProtoOutputStream proto = new ProtoOutputStream();
             final ServiceProvider provider = getProviderForSensor(sensorId);
             if (provider != null) {
-                provider.dumpProtoState(sensorId, proto);
+                provider.dumpProtoState(sensorId, proto, clearSchedulerBuffer);
             }
             proto.flush();
             return proto.getBytes();
@@ -405,7 +405,7 @@
                     final ProtoOutputStream proto = new ProtoOutputStream(fd);
                     for (ServiceProvider provider : mServiceProviders) {
                         for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) {
-                            provider.dumpProtoState(props.sensorId, proto);
+                            provider.dumpProtoState(props.sensorId, proto, false);
                         }
                     }
                     proto.flush();
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index 51b427d..32428ac1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -121,7 +121,8 @@
 
     void scheduleInternalCleanup(int sensorId, int userId);
 
-    void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto);
+    void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
+            boolean clearSchedulerBuffer);
 
     void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd);
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
index 13bbb7e8..211d79c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
@@ -21,6 +21,8 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.face.AuthenticationFrame;
+import android.hardware.biometrics.face.BaseFrame;
 import android.hardware.face.Face;
 import android.hardware.face.IFaceServiceReceiver;
 import android.os.Binder;
@@ -180,12 +182,21 @@
         mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationFailed();
     }
 
+    // TODO(b/174619156): replace with notifyAuthenticationFrame and notifyEnrollmentFrame.
     @Override
-    public void notifyAcquired(int userId, int acquireInfo)  {
+    public void notifyAcquired(int userId, int acquireInfo) {
         Utils.checkPermission(mContext, TEST_BIOMETRIC);
 
-        mSensor.getSessionForUser(userId).mHalSessionCallback
-                .onAcquired((byte) acquireInfo, 0 /* vendorCode */);
+        BaseFrame data = new BaseFrame();
+        data.acquiredInfo = (byte) acquireInfo;
+
+        AuthenticationFrame authenticationFrame = new AuthenticationFrame();
+        authenticationFrame.data = data;
+
+        // TODO(b/174619156): Currently onAuthenticationFrame and onEnrollmentFrame are the same.
+        // This will need to call the correct callback once the onAcquired callback is removed.
+        mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationFrame(
+                authenticationFrame);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index f09df1e..632cc4b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -22,6 +22,7 @@
 import android.hardware.biometrics.BiometricFaceConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.face.EnrollmentType;
 import android.hardware.biometrics.face.IFace;
 import android.hardware.biometrics.face.ISession;
 import android.hardware.face.Face;
@@ -114,7 +115,8 @@
 
         try {
             // TODO(b/172593978): Pass features.
-            mCancellationSignal = getFreshDaemon().enroll(mSequentialId,
+            // TODO(b/174619156): Handle accessibility enrollment.
+            mCancellationSignal = getFreshDaemon().enroll(mSequentialId, EnrollmentType.DEFAULT,
                     HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken),
                     mPreviewSurface);
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
index 5fb194c..773647b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
@@ -23,6 +23,7 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
 import java.util.Map;
@@ -65,4 +66,9 @@
         mAuthenticatorIds.put(getTargetUserId(), authenticatorId);
         mCallback.onClientFinished(this, true /* success */);
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_GET_AUTHENTICATOR_ID;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 810489b1c..20318e3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -571,9 +571,10 @@
     }
 
     @Override
-    public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
+    public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
+            boolean clearSchedulerBuffer) {
         if (mSensors.contains(sensorId)) {
-            mSensors.get(sensorId).dumpProtoState(sensorId, proto);
+            mSensors.get(sensorId).dumpProtoState(sensorId, proto, clearSchedulerBuffer);
         }
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index f355158..71bac57 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -25,6 +25,7 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.HardwareAuthTokenUtils;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 import com.android.server.biometrics.sensors.LockoutCache;
@@ -81,4 +82,9 @@
         mLockoutResetDispatcher.notifyLockoutResetCallbacks(getSensorId());
         mCallback.onClientFinished(this, true /* success */);
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_RESET_LOCKOUT;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 82ad387..9b00ba6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -22,6 +22,8 @@
 import android.content.pm.UserInfo;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.face.AuthenticationFrame;
+import android.hardware.biometrics.face.EnrollmentFrame;
 import android.hardware.biometrics.face.Error;
 import android.hardware.biometrics.face.IFace;
 import android.hardware.biometrics.face.ISession;
@@ -167,7 +169,8 @@
         }
 
         @Override
-        public void onAcquired(byte info, int vendorCode) {
+        public void onAuthenticationFrame(AuthenticationFrame frame) {
+            // TODO(b/174619156): propagate the frame to an AuthenticationClient
             mHandler.post(() -> {
                 final BaseClientMonitor client = mScheduler.getCurrentClient();
                 if (!(client instanceof AcquisitionClient)) {
@@ -177,7 +180,23 @@
                 }
 
                 final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
-                acquisitionClient.onAcquired(info, vendorCode);
+                acquisitionClient.onAcquired(frame.data.acquiredInfo, frame.data.vendorCode);
+            });
+        }
+
+        @Override
+        public void onEnrollmentFrame(EnrollmentFrame frame) {
+            // TODO(b/174619156): propagate the frame to an EnrollmentClient
+            mHandler.post(() -> {
+                final BaseClientMonitor client = mScheduler.getCurrentClient();
+                if (!(client instanceof AcquisitionClient)) {
+                    Slog.e(mTag, "onAcquired for non-acquisition client: "
+                            + Utils.getClientName(client));
+                    return;
+                }
+
+                final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
+                acquisitionClient.onAcquired(frame.data.acquiredInfo, frame.data.vendorCode);
             });
         }
 
@@ -467,12 +486,13 @@
         mTestHalEnabled = enabled;
     }
 
-    void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
+    void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
+            boolean clearSchedulerBuffer) {
         final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES);
 
         proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
         proto.write(SensorStateProto.MODALITY, SensorStateProto.FACE);
-        proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null);
+        proto.write(SensorStateProto.SCHEDULER, mScheduler.dumpProtoState(clearSchedulerBuffer));
 
         for (UserInfo user : UserManager.get(mContext).getUsers()) {
             final int userId = user.getUserHandle().getIdentifier();
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
index 34bf9bc..dc1efa0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
@@ -49,8 +49,8 @@
             }
 
             @Override
-            public ICancellationSignal enroll(int cookie, HardwareAuthToken hat,
-                    NativeHandle previewSurface) {
+            public ICancellationSignal enroll(int cookie, byte enrollmentType,
+                    HardwareAuthToken hat, NativeHandle previewSurface) {
                 return null;
             }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java
index 50483d9..189c726 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java
@@ -50,7 +50,7 @@
     }
 
     @Override
-    public ICancellationSignal enroll(int cookie, HardwareAuthToken hat,
+    public ICancellationSignal enroll(int cookie, byte enrollmentType, HardwareAuthToken hat,
             NativeHandle previewSurface) {
         return null;
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 5e7ddeb..7010d96 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -767,12 +767,13 @@
     }
 
     @Override
-    public void dumpProtoState(int sensorId, ProtoOutputStream proto) {
+    public void dumpProtoState(int sensorId, ProtoOutputStream proto,
+            boolean clearSchedulerBuffer) {
         final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES);
 
         proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
         proto.write(SensorStateProto.MODALITY, SensorStateProto.FACE);
-        proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null);
+        proto.write(SensorStateProto.SCHEDULER, mScheduler.dumpProtoState(clearSchedulerBuffer));
 
         for (UserInfo user : UserManager.get(mContext).getUsers()) {
             final int userId = user.getUserHandle().getIdentifier();
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
index 442303b..722a3b8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
@@ -27,6 +27,7 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
@@ -88,4 +89,9 @@
     boolean getValue() {
         return mValue;
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_GET_FEATURE;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
index e0548e0..14a4648 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
@@ -23,6 +23,7 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
 import java.util.ArrayList;
@@ -71,4 +72,9 @@
             mCallback.onClientFinished(this, false /* success */);
         }
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_RESET_LOCKOUT;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
index 4356043..6290e00 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
@@ -25,6 +25,7 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
@@ -88,4 +89,9 @@
             mCallback.onClientFinished(this, false /* success */);
         }
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_SET_FEATURE;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
index 0e72f94..70e2033 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
@@ -24,6 +24,7 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
 import java.io.File;
@@ -91,4 +92,9 @@
             mCallback.onClientFinished(this, false /* success */);
         }
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_UPDATE_ACTIVE_USER;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
index 312a3ba..34a9099 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
@@ -53,8 +53,8 @@
     }
 
     @Override
-    public byte[] dumpSensorServiceStateProto() throws RemoteException {
-        return mFingerprintService.dumpSensorServiceStateProto(mSensorId);
+    public byte[] dumpSensorServiceStateProto(boolean clearSchedulerBuffer) throws RemoteException {
+        return mFingerprintService.dumpSensorServiceStateProto(mSensorId, clearSchedulerBuffer);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index d541eb3..0265cb9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -116,13 +116,13 @@
         }
 
         @Override
-        public byte[] dumpSensorServiceStateProto(int sensorId) {
+        public byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer) {
             Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
 
             final ProtoOutputStream proto = new ProtoOutputStream();
             final ServiceProvider provider = getProviderForSensor(sensorId);
             if (provider != null) {
-                provider.dumpProtoState(sensorId, proto);
+                provider.dumpProtoState(sensorId, proto, clearSchedulerBuffer);
             }
             proto.flush();
             return proto.getBytes();
@@ -419,7 +419,7 @@
                     for (ServiceProvider provider : mServiceProviders) {
                         for (FingerprintSensorPropertiesInternal props
                                 : provider.getSensorProperties()) {
-                            provider.dumpProtoState(props.sensorId, proto);
+                            provider.dumpProtoState(props.sensorId, proto, false);
                         }
                     }
                     proto.flush();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 272e2b2..d3b5b5a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -128,7 +128,8 @@
 
     void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller);
 
-    void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto);
+    void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
+            boolean clearSchedulerBuffer);
 
     void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd);
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index 3b376fe..b4bb4f8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -27,6 +27,7 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.sensors.AcquisitionClient;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
@@ -91,4 +92,9 @@
             mCallback.onClientFinished(this, false /* success */);
         }
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_DETECT_INTERACTION;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
index 02d4ac3..ce1a318 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
@@ -23,6 +23,7 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
 import java.util.Map;
@@ -65,4 +66,9 @@
         mAuthenticatorIds.put(getTargetUserId(), authenticatorId);
         mCallback.onClientFinished(this, true /* success */);
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_GET_AUTHENTICATOR_ID;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 8a666f9..727184f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -627,9 +627,10 @@
     }
 
     @Override
-    public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
+    public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
+            boolean clearSchedulerBuffer) {
         if (mSensors.contains(sensorId)) {
-            mSensors.get(sensorId).dumpProtoState(sensorId, proto);
+            mSensors.get(sensorId).dumpProtoState(sensorId, proto, clearSchedulerBuffer);
         }
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index cd84cdf..ddcfcad 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -25,6 +25,7 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.HardwareAuthTokenUtils;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 import com.android.server.biometrics.sensors.LockoutCache;
@@ -76,4 +77,9 @@
         mLockoutResetDispatcher.notifyLockoutResetCallbacks(getSensorId());
         mCallback.onClientFinished(this, true /* success */);
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_RESET_LOCKOUT;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index 911f6b4..f0e7e1c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -481,12 +481,13 @@
         mTestHalEnabled = enabled;
     }
 
-    void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
+    void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
+            boolean clearSchedulerBuffer) {
         final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES);
 
         proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
         proto.write(SensorStateProto.MODALITY, SensorStateProto.FINGERPRINT);
-        proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null);
+        proto.write(SensorStateProto.SCHEDULER, mScheduler.dumpProtoState(clearSchedulerBuffer));
 
         for (UserInfo user : UserManager.get(mContext).getUsers()) {
             final int userId = user.getUserHandle().getIdentifier();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 6cc8687..acc575f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -714,12 +714,13 @@
     }
 
     @Override
-    public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
+    public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
+            boolean clearSchedulerBuffer) {
         final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES);
 
         proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
         proto.write(SensorStateProto.MODALITY, SensorStateProto.FINGERPRINT);
-        proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null);
+        proto.write(SensorStateProto.SCHEDULER, mScheduler.dumpProtoState(clearSchedulerBuffer));
 
         for (UserInfo user : UserManager.get(mContext).getUsers()) {
             final int userId = user.getUserHandle().getIdentifier();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index 55995ea..6318139 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -28,6 +28,7 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.sensors.AcquisitionClient;
 import com.android.server.biometrics.sensors.AuthenticationConsumer;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -123,4 +124,9 @@
             Slog.e(TAG, "Remote exception when sending onDetected", e);
         }
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_DETECT_INTERACTION;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
index f6ec4d9..11ffbb2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
@@ -26,6 +26,7 @@
 import android.os.SELinux;
 import android.util.Slog;
 
+import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
 import java.io.File;
@@ -121,4 +122,9 @@
             mCallback.onClientFinished(this, false /* success */);
         }
     }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_UPDATE_ACTIVE_USER;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
index 3e5b88c..8e84613 100644
--- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
@@ -50,7 +50,7 @@
     }
 
     @Override
-    public byte[] dumpSensorServiceStateProto() throws RemoteException {
+    public byte[] dumpSensorServiceStateProto(boolean clearSchedulerBuffer) throws RemoteException {
         return null;
     }
 
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index ba6cbcd..ab0360b 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -36,13 +36,17 @@
 import android.net.NetworkMonitorManager;
 import android.net.NetworkRequest;
 import android.net.NetworkState;
+import android.net.QosCallbackException;
+import android.net.QosFilter;
+import android.net.QosFilterParcelable;
+import android.net.QosSession;
 import android.net.TcpKeepalivePacketData;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.telephony.data.EpsBearerQosSessionAttributes;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
@@ -53,7 +57,6 @@
 import com.android.server.ConnectivityService;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Objects;
@@ -323,18 +326,20 @@
     private final ConnectivityService mConnService;
     private final Context mContext;
     private final Handler mHandler;
+    private final QosCallbackTracker mQosCallbackTracker;
 
     public NetworkAgentInfo(INetworkAgent na, Network net, NetworkInfo info,
             LinkProperties lp, NetworkCapabilities nc, int score, Context context,
             Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
             IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber,
-            int creatorUid) {
+            int creatorUid, QosCallbackTracker qosCallbackTracker) {
         Objects.requireNonNull(net);
         Objects.requireNonNull(info);
         Objects.requireNonNull(lp);
         Objects.requireNonNull(nc);
         Objects.requireNonNull(context);
         Objects.requireNonNull(config);
+        Objects.requireNonNull(qosCallbackTracker);
         networkAgent = na;
         network = net;
         networkInfo = info;
@@ -348,6 +353,7 @@
         networkAgentConfig = config;
         this.factorySerialNumber = factorySerialNumber;
         this.creatorUid = creatorUid;
+        mQosCallbackTracker = qosCallbackTracker;
     }
 
     private class AgentDeathMonitor implements IBinder.DeathRecipient {
@@ -533,6 +539,31 @@
         }
     }
 
+    /**
+     * Notify the NetworkAgent that the qos filter should be registered against the given qos
+     * callback id.
+     */
+    public void onQosFilterCallbackRegistered(final int qosCallbackId,
+            final QosFilter qosFilter) {
+        try {
+            networkAgent.onQosFilterCallbackRegistered(qosCallbackId,
+                    new QosFilterParcelable(qosFilter));
+        } catch (final RemoteException e) {
+            Log.e(TAG, "Error registering a qos callback id against a qos filter", e);
+        }
+    }
+
+    /**
+     * Notify the NetworkAgent that the given qos callback id should be unregistered.
+     */
+    public void onQosCallbackUnregistered(final int qosCallbackId) {
+        try {
+            networkAgent.onQosCallbackUnregistered(qosCallbackId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error unregistering a qos callback id", e);
+        }
+    }
+
     // TODO: consider moving out of NetworkAgentInfo into its own class
     private class NetworkAgentMessageHandler extends INetworkAgentRegistry.Stub {
         private final Handler mHandler;
@@ -583,16 +614,25 @@
 
         @Override
         public void sendUnderlyingNetworks(@Nullable List<Network> networks) {
-            final Bundle args = new Bundle();
-            if (networks instanceof ArrayList<?>) {
-                args.putParcelableArrayList(NetworkAgent.UNDERLYING_NETWORKS_KEY,
-                        (ArrayList<Network>) networks);
-            } else {
-                args.putParcelableArrayList(NetworkAgent.UNDERLYING_NETWORKS_KEY,
-                        networks == null ? null : new ArrayList<>(networks));
-            }
             mHandler.obtainMessage(NetworkAgent.EVENT_UNDERLYING_NETWORKS_CHANGED,
-                    new Pair<>(NetworkAgentInfo.this, args)).sendToTarget();
+                    new Pair<>(NetworkAgentInfo.this, networks)).sendToTarget();
+        }
+
+        @Override
+        public void sendEpsQosSessionAvailable(final int qosCallbackId, final QosSession session,
+                final EpsBearerQosSessionAttributes attributes) {
+            mQosCallbackTracker.sendEventQosSessionAvailable(qosCallbackId, session, attributes);
+        }
+
+        @Override
+        public void sendQosSessionLost(final int qosCallbackId, final QosSession session) {
+            mQosCallbackTracker.sendEventQosSessionLost(qosCallbackId, session);
+        }
+
+        @Override
+        public void sendQosCallbackError(final int qosCallbackId,
+                @QosCallbackException.ExceptionType final int exceptionType) {
+            mQosCallbackTracker.sendEventQosCallbackError(qosCallbackId, exceptionType);
         }
     }
 
diff --git a/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java b/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java
new file mode 100644
index 0000000..816bf2b
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java
@@ -0,0 +1,192 @@
+/*
+ * 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.server.connectivity;
+
+import static android.net.QosCallbackException.EX_TYPE_FILTER_NONE;
+
+import android.annotation.NonNull;
+import android.net.IQosCallback;
+import android.net.Network;
+import android.net.QosCallbackException;
+import android.net.QosFilter;
+import android.net.QosSession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.util.Slog;
+
+import java.util.Objects;
+
+/**
+ * Wraps callback related information and sends messages between network agent and the application.
+ * <p/>
+ * This is a satellite class of {@link com.android.server.ConnectivityService} and not meant
+ * to be used in other contexts.
+ *
+ * @hide
+ */
+class QosCallbackAgentConnection implements IBinder.DeathRecipient {
+    private static final String TAG = QosCallbackAgentConnection.class.getSimpleName();
+    private static final boolean DBG = false;
+
+    private final int mAgentCallbackId;
+    @NonNull private final QosCallbackTracker mQosCallbackTracker;
+    @NonNull private final IQosCallback mCallback;
+    @NonNull private final IBinder mBinder;
+    @NonNull private final QosFilter mFilter;
+    @NonNull private final NetworkAgentInfo mNetworkAgentInfo;
+
+    private final int mUid;
+
+    /**
+     * Gets the uid
+     * @return uid
+     */
+    int getUid() {
+        return mUid;
+    }
+
+    /**
+     * Gets the binder
+     * @return binder
+     */
+    @NonNull
+    IBinder getBinder() {
+        return mBinder;
+    }
+
+    /**
+     * Gets the callback id
+     *
+     * @return callback id
+     */
+    int getAgentCallbackId() {
+        return mAgentCallbackId;
+    }
+
+    /**
+     * Gets the network tied to the callback of this connection
+     *
+     * @return network
+     */
+    @NonNull
+    Network getNetwork() {
+        return mFilter.getNetwork();
+    }
+
+    QosCallbackAgentConnection(@NonNull final QosCallbackTracker qosCallbackTracker,
+            final int agentCallbackId,
+            @NonNull final IQosCallback callback,
+            @NonNull final QosFilter filter,
+            final int uid,
+            @NonNull final NetworkAgentInfo networkAgentInfo) {
+        Objects.requireNonNull(qosCallbackTracker, "qosCallbackTracker must be non-null");
+        Objects.requireNonNull(callback, "callback must be non-null");
+        Objects.requireNonNull(filter, "filter must be non-null");
+        Objects.requireNonNull(networkAgentInfo, "networkAgentInfo must be non-null");
+
+        mQosCallbackTracker = qosCallbackTracker;
+        mAgentCallbackId = agentCallbackId;
+        mCallback = callback;
+        mFilter = filter;
+        mUid = uid;
+        mBinder = mCallback.asBinder();
+        mNetworkAgentInfo = networkAgentInfo;
+    }
+
+    @Override
+    public void binderDied() {
+        logw("binderDied: binder died with callback id: " + mAgentCallbackId);
+        mQosCallbackTracker.unregisterCallback(mCallback);
+    }
+
+    void unlinkToDeathRecipient() {
+        mBinder.unlinkToDeath(this, 0);
+    }
+
+    // Returns false if the NetworkAgent was never notified.
+    boolean sendCmdRegisterCallback() {
+        final int exceptionType = mFilter.validate();
+        if (exceptionType != EX_TYPE_FILTER_NONE) {
+            try {
+                if (DBG) log("sendCmdRegisterCallback: filter validation failed");
+                mCallback.onError(exceptionType);
+            } catch (final RemoteException e) {
+                loge("sendCmdRegisterCallback:", e);
+            }
+            return false;
+        }
+
+        try {
+            mBinder.linkToDeath(this, 0);
+        } catch (final RemoteException e) {
+            loge("failed linking to death recipient", e);
+            return false;
+        }
+        mNetworkAgentInfo.onQosFilterCallbackRegistered(mAgentCallbackId, mFilter);
+        return true;
+    }
+
+    void sendCmdUnregisterCallback() {
+        if (DBG) log("sendCmdUnregisterCallback: unregistering");
+        mNetworkAgentInfo.onQosCallbackUnregistered(mAgentCallbackId);
+    }
+
+    void sendEventQosSessionAvailable(final QosSession session,
+            final EpsBearerQosSessionAttributes attributes) {
+        try {
+            if (DBG) log("sendEventQosSessionAvailable: sending...");
+            mCallback.onQosEpsBearerSessionAvailable(session, attributes);
+        } catch (final RemoteException e) {
+            loge("sendEventQosSessionAvailable: remote exception", e);
+        }
+    }
+
+    void sendEventQosSessionLost(@NonNull final QosSession session) {
+        try {
+            if (DBG) log("sendEventQosSessionLost: sending...");
+            mCallback.onQosSessionLost(session);
+        } catch (final RemoteException e) {
+            loge("sendEventQosSessionLost: remote exception", e);
+        }
+    }
+
+    void sendEventQosCallbackError(@QosCallbackException.ExceptionType final int exceptionType) {
+        try {
+            if (DBG) log("sendEventQosCallbackError: sending...");
+            mCallback.onError(exceptionType);
+        } catch (final RemoteException e) {
+            loge("sendEventQosCallbackError: remote exception", e);
+        }
+    }
+
+    private static void log(@NonNull final String msg) {
+        Slog.d(TAG, msg);
+    }
+
+    private static void logw(@NonNull final String msg) {
+        Slog.w(TAG, msg);
+    }
+
+    private static void loge(@NonNull final String msg, final Throwable t) {
+        Slog.e(TAG, msg, t);
+    }
+
+    private static void logwtf(@NonNull final String msg) {
+        Slog.wtf(TAG, msg);
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
new file mode 100644
index 0000000..87b4c16
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
@@ -0,0 +1,277 @@
+/*
+ * 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.server.connectivity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.IQosCallback;
+import android.net.Network;
+import android.net.QosCallbackException;
+import android.net.QosFilter;
+import android.net.QosSession;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.util.Slog;
+
+import com.android.internal.util.CollectionUtils;
+import com.android.server.ConnectivityService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tracks qos callbacks and handles the communication between the network agent and application.
+ * <p/>
+ * Any method prefixed by handle must be called from the
+ * {@link com.android.server.ConnectivityService} handler thread.
+ *
+ * @hide
+ */
+public class QosCallbackTracker {
+    private static final String TAG = QosCallbackTracker.class.getSimpleName();
+    private static final boolean DBG = true;
+
+    @NonNull
+    private final Handler mConnectivityServiceHandler;
+
+    @NonNull
+    private final ConnectivityService.PerUidCounter mNetworkRequestCounter;
+
+    /**
+     * Each agent gets a unique callback id that is used to proxy messages back to the original
+     * callback.
+     * <p/>
+     * Note: The fact that this is initialized to 0 is to ensure that the thread running
+     * {@link #handleRegisterCallback(IQosCallback, QosFilter, int, NetworkAgentInfo)} sees the
+     * initialized value. This would not necessarily be the case if the value was initialized to
+     * the non-default value.
+     * <p/>
+     * Note: The term previous does not apply to the first callback id that is assigned.
+     */
+    private int mPreviousAgentCallbackId = 0;
+
+    @NonNull
+    private final List<QosCallbackAgentConnection> mConnections = new ArrayList<>();
+
+    /**
+     *
+     * @param connectivityServiceHandler must be the same handler used with
+     *                {@link com.android.server.ConnectivityService}
+     * @param networkRequestCounter keeps track of the number of open requests under a given
+     *                              uid
+     */
+    public QosCallbackTracker(@NonNull final Handler connectivityServiceHandler,
+            final ConnectivityService.PerUidCounter networkRequestCounter) {
+        mConnectivityServiceHandler = connectivityServiceHandler;
+        mNetworkRequestCounter = networkRequestCounter;
+    }
+
+    /**
+     * Registers the callback with the tracker
+     *
+     * @param callback the callback to register
+     * @param filter the filter being registered alongside the callback
+     */
+    public void registerCallback(@NonNull final IQosCallback callback,
+            @NonNull final QosFilter filter, @NonNull final NetworkAgentInfo networkAgentInfo) {
+        final int uid = Binder.getCallingUid();
+
+        // Enforce that the number of requests under this uid has exceeded the allowed number
+        mNetworkRequestCounter.incrementCountOrThrow(uid);
+
+        mConnectivityServiceHandler.post(
+                () -> handleRegisterCallback(callback, filter, uid, networkAgentInfo));
+    }
+
+    private void handleRegisterCallback(@NonNull final IQosCallback callback,
+            @NonNull final QosFilter filter, final int uid,
+            @NonNull final NetworkAgentInfo networkAgentInfo) {
+        final QosCallbackAgentConnection ac =
+                handleRegisterCallbackInternal(callback, filter, uid, networkAgentInfo);
+        if (ac != null) {
+            if (DBG) log("handleRegisterCallback: added callback " + ac.getAgentCallbackId());
+            mConnections.add(ac);
+        } else {
+            mNetworkRequestCounter.decrementCount(uid);
+        }
+    }
+
+    private QosCallbackAgentConnection handleRegisterCallbackInternal(
+            @NonNull final IQosCallback callback,
+            @NonNull final QosFilter filter, final int uid,
+            @NonNull final NetworkAgentInfo networkAgentInfo) {
+        final IBinder binder = callback.asBinder();
+        if (CollectionUtils.any(mConnections, c -> c.getBinder().equals(binder))) {
+            // A duplicate registration would have only made this far due to a programming error.
+            logwtf("handleRegisterCallback: Callbacks can only be register once.");
+            return null;
+        }
+
+        mPreviousAgentCallbackId = mPreviousAgentCallbackId + 1;
+        final int newCallbackId = mPreviousAgentCallbackId;
+
+        final QosCallbackAgentConnection ac =
+                new QosCallbackAgentConnection(this, newCallbackId, callback,
+                        filter, uid, networkAgentInfo);
+
+        final int exceptionType = filter.validate();
+        if (exceptionType != QosCallbackException.EX_TYPE_FILTER_NONE) {
+            ac.sendEventQosCallbackError(exceptionType);
+            return null;
+        }
+
+        // Only add to the callback maps if the NetworkAgent successfully registered it
+        if (!ac.sendCmdRegisterCallback()) {
+            // There was an issue when registering the agent
+            if (DBG) log("handleRegisterCallback: error sending register callback");
+            mNetworkRequestCounter.decrementCount(uid);
+            return null;
+        }
+        return ac;
+    }
+
+    /**
+     * Unregisters callback
+     * @param callback callback to unregister
+     */
+    public void unregisterCallback(@NonNull final IQosCallback callback) {
+        mConnectivityServiceHandler.post(() -> handleUnregisterCallback(callback.asBinder(), true));
+    }
+
+    private void handleUnregisterCallback(@NonNull final IBinder binder,
+            final boolean sendToNetworkAgent) {
+        final QosCallbackAgentConnection agentConnection =
+                CollectionUtils.find(mConnections, c -> c.getBinder().equals(binder));
+        if (agentConnection == null) {
+            logw("handleUnregisterCallback: agentConnection is null");
+            return;
+        }
+
+        if (DBG) {
+            log("handleUnregisterCallback: unregister "
+                    + agentConnection.getAgentCallbackId());
+        }
+
+        mNetworkRequestCounter.decrementCount(agentConnection.getUid());
+        mConnections.remove(agentConnection);
+
+        if (sendToNetworkAgent) {
+            agentConnection.sendCmdUnregisterCallback();
+        }
+        agentConnection.unlinkToDeathRecipient();
+    }
+
+    /**
+     * Called when the NetworkAgent sends the qos session available event
+     *
+     * @param qosCallbackId the callback id that the qos session is now available to
+     * @param session the qos session that is now available
+     * @param attributes the qos attributes that are now available on the qos session
+     */
+    public void sendEventQosSessionAvailable(final int qosCallbackId,
+            final QosSession session,
+            final EpsBearerQosSessionAttributes attributes) {
+        runOnAgentConnection(qosCallbackId, "sendEventQosSessionAvailable: ",
+                ac -> ac.sendEventQosSessionAvailable(session, attributes));
+    }
+
+    /**
+     * Called when the NetworkAgent sends the qos session lost event
+     *
+     * @param qosCallbackId the callback id that lost the qos session
+     * @param session the corresponding qos session
+     */
+    public void sendEventQosSessionLost(final int qosCallbackId,
+            final QosSession session) {
+        runOnAgentConnection(qosCallbackId, "sendEventQosSessionLost: ",
+                ac -> ac.sendEventQosSessionLost(session));
+    }
+
+    /**
+     * Called when the NetworkAgent sends the qos session on error event
+     *
+     * @param qosCallbackId the callback id that should receive the exception
+     * @param exceptionType the type of exception that caused the callback to error
+     */
+    public void sendEventQosCallbackError(final int qosCallbackId,
+            @QosCallbackException.ExceptionType final int exceptionType) {
+        runOnAgentConnection(qosCallbackId, "sendEventQosCallbackError: ",
+                ac -> {
+                    ac.sendEventQosCallbackError(exceptionType);
+                    handleUnregisterCallback(ac.getBinder(), false);
+                });
+    }
+
+    /**
+     * Unregisters all callbacks associated to this network agent
+     *
+     * Note: Must be called on the connectivity service handler thread
+     *
+     * @param network the network that was released
+     */
+    public void handleNetworkReleased(@Nullable final Network network) {
+        final List<QosCallbackAgentConnection> connections =
+                CollectionUtils.filter(mConnections, ac -> ac.getNetwork().equals(network));
+
+        for (final QosCallbackAgentConnection agentConnection : connections) {
+            agentConnection.sendEventQosCallbackError(
+                    QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED);
+
+            // Call unregister workflow w\o sending anything to agent since it is disconnected.
+            handleUnregisterCallback(agentConnection.getBinder(), false);
+        }
+    }
+
+    private interface AgentConnectionAction {
+        void execute(@NonNull QosCallbackAgentConnection agentConnection);
+    }
+
+    @Nullable
+    private void runOnAgentConnection(final int qosCallbackId,
+            @NonNull final String logPrefix,
+            @NonNull final AgentConnectionAction action) {
+        mConnectivityServiceHandler.post(() -> {
+            final QosCallbackAgentConnection ac =
+                    CollectionUtils.find(mConnections,
+                            c -> c.getAgentCallbackId() == qosCallbackId);
+            if (ac == null) {
+                loge(logPrefix + ": " + qosCallbackId + " missing callback id");
+                return;
+            }
+
+            action.execute(ac);
+        });
+    }
+
+    private static void log(final String msg) {
+        Slog.d(TAG, msg);
+    }
+
+    private static void logw(final String msg) {
+        Slog.w(TAG, msg);
+    }
+
+    private static void loge(final String msg) {
+        Slog.e(TAG, msg);
+    }
+
+    private static void logwtf(final String msg) {
+        Slog.wtf(TAG, msg);
+    }
+}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 68708d36..d687221 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -24,6 +24,7 @@
 import android.view.DisplayAddress;
 
 import com.android.internal.BrightnessSynchronizer;
+import com.android.internal.R;
 import com.android.server.display.config.DisplayConfiguration;
 import com.android.server.display.config.DisplayQuirks;
 import com.android.server.display.config.HbmTiming;
@@ -64,6 +65,7 @@
     private static final String STABLE_ID_SUFFIX_FORMAT = "id_%d";
     private static final String NO_SUFFIX_FORMAT = "%d";
     private static final long STABLE_FLAG = 1L << 62;
+
     // Float.NaN (used as invalid for brightness) cannot be stored in config.xml
     // so -2 is used instead
     private static final float INVALID_BRIGHTNESS_IN_CONFIG = -2f;
@@ -75,6 +77,10 @@
     private float mBrightnessMinimum = Float.NaN;
     private float mBrightnessMaximum = Float.NaN;
     private float mBrightnessDefault = Float.NaN;
+    private float mBrightnessRampFastDecrease = Float.NaN;
+    private float mBrightnessRampFastIncrease = Float.NaN;
+    private float mBrightnessRampSlowDecrease = Float.NaN;
+    private float mBrightnessRampSlowIncrease = Float.NaN;
     private List<String> mQuirks;
     private boolean mIsHighBrightnessModeEnabled = false;
     private HighBrightnessModeData mHbmData;
@@ -177,6 +183,22 @@
         return mBrightnessDefault;
     }
 
+    public float getBrightnessRampFastDecrease() {
+        return mBrightnessRampFastDecrease;
+    }
+
+    public float getBrightnessRampFastIncrease() {
+        return mBrightnessRampFastIncrease;
+    }
+
+    public float getBrightnessRampSlowDecrease() {
+        return mBrightnessRampSlowDecrease;
+    }
+
+    public float getBrightnessRampSlowIncrease() {
+        return mBrightnessRampSlowIncrease;
+    }
+
     /**
      * @param quirkValue The quirk to test.
      * @return {@code true} if the specified quirk is present in this configuration,
@@ -210,6 +232,10 @@
                 + ", mQuirks=" + mQuirks
                 + ", isHbmEnabled=" + mIsHighBrightnessModeEnabled
                 + ", mHbmData=" + mHbmData
+                + ", mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease
+                + ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease
+                + ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease
+                + ", mBrightnessRampSlowIncrease=" + mBrightnessRampSlowIncrease
                 + "}";
         return str;
     }
@@ -265,6 +291,7 @@
                 loadBrightnessConstraintsFromConfigXml();
                 loadHighBrightnessModeData(config);
                 loadQuirks(config);
+                loadBrightnessRamps(config);
             } else {
                 Slog.w(TAG, "DisplayDeviceConfig file is null");
             }
@@ -278,6 +305,7 @@
         // If no ddc exists, use config.xml
         loadBrightnessDefaultFromConfigXml();
         loadBrightnessConstraintsFromConfigXml();
+        loadBrightnessRampsFromConfigXml();
     }
 
     private void initFromPmValues() {
@@ -397,6 +425,41 @@
         }
     }
 
+    private void loadBrightnessRamps(DisplayConfiguration config) {
+        // Priority 1: Value in the display device config (float)
+        // Priority 2: Value in the config.xml (int)
+        final BigDecimal fastDownDecimal = config.getScreenBrightnessRampFastDecrease();
+        final BigDecimal fastUpDecimal = config.getScreenBrightnessRampFastIncrease();
+        final BigDecimal slowDownDecimal = config.getScreenBrightnessRampSlowDecrease();
+        final BigDecimal slowUpDecimal = config.getScreenBrightnessRampSlowIncrease();
+
+        if (fastDownDecimal != null && fastUpDecimal != null && slowDownDecimal != null
+                && slowUpDecimal != null) {
+            mBrightnessRampFastDecrease = fastDownDecimal.floatValue();
+            mBrightnessRampFastIncrease = fastUpDecimal.floatValue();
+            mBrightnessRampSlowDecrease = slowDownDecimal.floatValue();
+            mBrightnessRampSlowIncrease = slowUpDecimal.floatValue();
+        } else {
+            if (fastDownDecimal != null || fastUpDecimal != null || slowDownDecimal != null
+                    || slowUpDecimal != null) {
+                Slog.w(TAG, "Per display brightness ramp values ignored because not all "
+                        + "values are present in display device config");
+            }
+            loadBrightnessRampsFromConfigXml();
+        }
+    }
+
+    private void loadBrightnessRampsFromConfigXml() {
+        mBrightnessRampFastIncrease = BrightnessSynchronizer.brightnessIntToFloat(
+                mContext.getResources().getInteger(R.integer.config_brightness_ramp_rate_fast));
+        mBrightnessRampSlowIncrease = BrightnessSynchronizer.brightnessIntToFloat(
+                mContext.getResources().getInteger(R.integer.config_brightness_ramp_rate_slow));
+        // config.xml uses the same values for both increasing and decreasing brightness
+        // transitions so we assign them to the same values here.
+        mBrightnessRampFastDecrease = mBrightnessRampFastIncrease;
+        mBrightnessRampSlowDecrease = mBrightnessRampSlowIncrease;
+    }
+
     /**
      * Container for high brightness mode configuration data.
      */
diff --git a/services/core/java/com/android/server/display/DisplayGroup.java b/services/core/java/com/android/server/display/DisplayGroup.java
index 2ba8758..1b5065a 100644
--- a/services/core/java/com/android/server/display/DisplayGroup.java
+++ b/services/core/java/com/android/server/display/DisplayGroup.java
@@ -21,7 +21,8 @@
 
 /**
  * Represents a collection of {@link LogicalDisplay}s which act in unison for certain behaviors and
- * operations.
+ * operations; particularly display-state.
+ *
  * @hide
  */
 public class DisplayGroup {
@@ -35,17 +36,44 @@
         mGroupId = groupId;
     }
 
+    /** Returns the identifier for the Group. */
     int getGroupId() {
         return mGroupId;
     }
 
-    void addDisplay(LogicalDisplay display) {
+    /**
+     * Adds the provided {@code display} to the Group
+     *
+     * @param display the {@link LogicalDisplay} to add to the Group
+     */
+    void addDisplayLocked(LogicalDisplay display) {
         if (!mDisplays.contains(display)) {
             mDisplays.add(display);
         }
     }
 
-    boolean removeDisplay(LogicalDisplay display) {
+    /**
+     * Removes the provided {@code display} from the Group.
+     *
+     * @param display The {@link LogicalDisplay} to remove from the Group.
+     * @return {@code true} if the {@code display} was removed; otherwise {@code false}
+     */
+    boolean removeDisplayLocked(LogicalDisplay display) {
         return mDisplays.remove(display);
     }
+
+    /** Returns {@code true} if there are no {@link LogicalDisplay LogicalDisplays} in the Group. */
+    boolean isEmptyLocked() {
+        return mDisplays.isEmpty();
+    }
+
+    /** Returns the number of {@link LogicalDisplay LogicalDisplays} in the Group. */
+    int getSizeLocked() {
+        return mDisplays.size();
+    }
+
+    /** Returns the ID of the {@link LogicalDisplay} at the provided {@code index}. */
+    int getIdLocked(int index) {
+        return mDisplays.get(index).getDisplayIdLocked();
+    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 55103ca..bb4c9dd 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -57,6 +57,7 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerGlobal;
 import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayGroupListener;
 import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
 import android.hardware.display.DisplayViewport;
 import android.hardware.display.DisplayedContentSample;
@@ -190,6 +191,7 @@
     private static final int MSG_UPDATE_VIEWPORT = 5;
     private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATION = 6;
     private static final int MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE = 7;
+    private static final int MSG_DELIVER_DISPLAY_GROUP_EVENT = 8;
 
     private final Context mContext;
     private final DisplayManagerHandler mHandler;
@@ -237,36 +239,54 @@
     private final CopyOnWriteArrayList<DisplayTransactionListener> mDisplayTransactionListeners =
             new CopyOnWriteArrayList<DisplayTransactionListener>();
 
+    /** List of all display group listeners. */
+    private final CopyOnWriteArrayList<DisplayGroupListener> mDisplayGroupListeners =
+            new CopyOnWriteArrayList<>();
+
     /** All {@link DisplayPowerController}s indexed by {@link LogicalDisplay} ID. */
     private final SparseArray<DisplayPowerController> mDisplayPowerControllers =
             new SparseArray<>();
 
     /** {@link DisplayBlanker} used by all {@link DisplayPowerController}s. */
     private final DisplayBlanker mDisplayBlanker = new DisplayBlanker() {
+        // Synchronized to avoid race conditions when updating multiple display states.
         @Override
-        public void requestDisplayState(int displayId, int state, float brightness) {
-            // TODO (b/168210494): Stop applying default display state to all displays.
-            if (displayId != Display.DEFAULT_DISPLAY) {
-                return;
-            }
-            final int[] displayIds;
+        public synchronized void requestDisplayState(int displayId, int state, float brightness) {
+            boolean allInactive = true;
+            boolean allOff = true;
+            final boolean stateChanged;
             synchronized (mSyncRoot) {
-                displayIds = mLogicalDisplayMapper.getDisplayIdsLocked();
+                final int index = mDisplayStates.indexOfKey(displayId);
+                final int newState = mDisplayStates.valueAt(index);
+                stateChanged = index == -1 || state != newState;
+                if (stateChanged) {
+                    final int size = mDisplayStates.size();
+                    for (int i = 0; i < size; i++) {
+                        final int displayState = i == index ? newState : state;
+                        if (displayState != Display.STATE_OFF) {
+                            allOff = false;
+                        }
+                        if (Display.isActiveState(displayState)) {
+                            allInactive = false;
+                        }
+                        if (!allOff && !allInactive) {
+                            break;
+                        }
+                    }
+                }
             }
 
             // The order of operations is important for legacy reasons.
             if (state == Display.STATE_OFF) {
-                for (int id : displayIds) {
-                    requestDisplayStateInternal(id, state, brightness);
-                }
+                requestDisplayStateInternal(displayId, state, brightness);
             }
 
-            mDisplayPowerCallbacks.onDisplayStateChange(state);
+            if (stateChanged) {
+                mDisplayPowerCallbacks.onDisplayStateChange(allInactive, allOff);
+            }
 
             if (state != Display.STATE_OFF) {
-                for (int id : displayIds) {
-                    requestDisplayStateInternal(id, state, brightness);
-                }
+                requestDisplayStateInternal(displayId, state, brightness);
             }
         }
     };
@@ -1152,7 +1172,7 @@
 
     private void handleLogicalDisplayRemovedLocked(@NonNull LogicalDisplay display) {
         final int displayId = display.getDisplayIdLocked();
-        mDisplayPowerControllers.delete(displayId);
+        mDisplayPowerControllers.removeReturnOld(displayId).stop();
         mDisplayStates.delete(displayId);
         mDisplayBrightnesses.delete(displayId);
         DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
@@ -1669,6 +1689,11 @@
         mHandler.sendMessage(msg);
     }
 
+    private void sendDisplayGroupEvent(int groupId, int event) {
+        Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_GROUP_EVENT, groupId, event);
+        mHandler.sendMessage(msg);
+    }
+
     private void sendDisplayEventFrameRateOverrideLocked(int displayId) {
         Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE,
                 displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
@@ -1713,6 +1738,35 @@
         mTempCallbacks.clear();
     }
 
+    // Runs on Handler thread.
+    // Delivers display group event notifications to callbacks.
+    private void deliverDisplayGroupEvent(int groupId, int event) {
+        if (DEBUG) {
+            Slog.d(TAG, "Delivering display group event: groupId=" + groupId + ", event="
+                    + event);
+        }
+
+        switch (event) {
+            case LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED:
+                for (DisplayGroupListener listener : mDisplayGroupListeners) {
+                    listener.onDisplayGroupAdded(groupId);
+                }
+                break;
+
+            case LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED:
+                for (DisplayGroupListener listener : mDisplayGroupListeners) {
+                    listener.onDisplayGroupChanged(groupId);
+                }
+                break;
+
+            case LogicalDisplayMapper.DISPLAY_GROUP_EVENT_REMOVED:
+                for (DisplayGroupListener listener : mDisplayGroupListeners) {
+                    listener.onDisplayGroupRemoved(groupId);
+                }
+                break;
+        }
+    }
+
     private IMediaProjectionManager getProjectionService() {
         if (mProjectionService == null) {
             IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE);
@@ -1935,6 +1989,11 @@
                     }
                     deliverDisplayEvent(msg.arg1, uids, msg.arg2);
                     break;
+
+                case MSG_DELIVER_DISPLAY_GROUP_EVENT:
+                    deliverDisplayGroupEvent(msg.arg1, msg.arg2);
+                    break;
+
             }
         }
     }
@@ -1966,6 +2025,11 @@
         }
 
         @Override
+        public void onDisplayGroupEventLocked(int groupId, int event) {
+            sendDisplayGroupEvent(groupId, event);
+        }
+
+        @Override
         public void onTraversalRequested() {
             synchronized (mSyncRoot) {
                 scheduleTraversalLocked(false);
@@ -2700,11 +2764,25 @@
         }
 
         @Override
-        public boolean requestPowerState(DisplayPowerRequest request,
+        public boolean requestPowerState(int groupId, DisplayPowerRequest request,
                 boolean waitForNegativeProximity) {
             synchronized (mSyncRoot) {
-                return mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY)
-                        .requestPowerState(request, waitForNegativeProximity);
+                final DisplayGroup displayGroup = mLogicalDisplayMapper.getDisplayGroupLocked(
+                        groupId);
+                if (displayGroup == null) {
+                    return true;
+                }
+
+                final int size = displayGroup.getSizeLocked();
+                boolean ready = true;
+                for (int i = 0; i < size; i++) {
+                    final DisplayPowerController displayPowerController =
+                            mDisplayPowerControllers.get(displayGroup.getIdLocked(i));
+                    ready &= displayPowerController.requestPowerState(request,
+                            waitForNegativeProximity);
+                }
+
+                return ready;
             }
         }
 
@@ -2717,10 +2795,13 @@
         }
 
         @Override
-        public int getDisplayGroupId(int displayId) {
-            synchronized (mSyncRoot) {
-                return mLogicalDisplayMapper.getDisplayGroupIdLocked(displayId);
-            }
+        public void registerDisplayGroupListener(DisplayGroupListener listener) {
+            mDisplayGroupListeners.add(listener);
+        }
+
+        @Override
+        public void unregisterDisplayGroupListener(DisplayGroupListener listener) {
+            mDisplayGroupListeners.remove(listener);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 811625b..cb73c09 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -331,8 +331,10 @@
     private BrightnessReason mBrightnessReasonTemp = new BrightnessReason();
 
     // Brightness animation ramp rates in brightness units per second
-    private final float mBrightnessRampRateSlow;
-    private final float mBrightnessRampRateFast;
+    private float mBrightnessRampRateFastDecrease;
+    private float mBrightnessRampRateFastIncrease;
+    private float mBrightnessRampRateSlowDecrease;
+    private float mBrightnessRampRateSlowIncrease;
 
 
     // Whether or not to skip the initial brightness ramps into STATE_ON.
@@ -459,10 +461,21 @@
         mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean(
                 com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing);
 
-        mBrightnessRampRateFast = BrightnessSynchronizer.brightnessIntToFloat(resources.getInteger(
-                com.android.internal.R.integer.config_brightness_ramp_rate_fast));
-        mBrightnessRampRateSlow = BrightnessSynchronizer.brightnessIntToFloat(resources.getInteger(
-                com.android.internal.R.integer.config_brightness_ramp_rate_slow));
+
+        DisplayDeviceConfig displayDeviceConfig = logicalDisplay
+                .getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig();
+        // TODO: (b/178183143) Ensure that the ddc is not null
+        if (displayDeviceConfig != null) {
+            mBrightnessRampRateFastDecrease = displayDeviceConfig.getBrightnessRampFastDecrease();
+            mBrightnessRampRateFastIncrease = displayDeviceConfig.getBrightnessRampFastIncrease();
+            mBrightnessRampRateSlowDecrease = displayDeviceConfig.getBrightnessRampSlowDecrease();
+            mBrightnessRampRateSlowIncrease = displayDeviceConfig.getBrightnessRampSlowIncrease();
+        } else {
+            mBrightnessRampRateFastDecrease = 1.0f;
+            mBrightnessRampRateFastIncrease = 1.0f;
+            mBrightnessRampRateSlowDecrease = 1.0f;
+            mBrightnessRampRateSlowIncrease = 1.0f;
+        }
         mSkipScreenOnBrightnessRamp = resources.getBoolean(
                 com.android.internal.R.bool.config_skipScreenOnBrightnessRamp);
 
@@ -683,6 +696,17 @@
         // TODO: b/175821789 - Support high brightness on multiple (folding) displays
     }
 
+    /**
+     * Unregisters all listeners and interrupts all running threads; halting future work.
+     *
+     * This method should be called when the DisplayPowerController is no longer in use; i.e. when
+     * the {@link #mDisplayId display} has been removed.
+     */
+    public void stop() {
+        mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
+        mPowerState.stop();
+    }
+
     private void sendUpdatePowerState() {
         synchronized (mLock) {
             sendUpdatePowerStateLocked();
@@ -772,7 +796,6 @@
         boolean mustInitialize = false;
         int brightnessAdjustmentFlags = 0;
         mBrightnessReasonTemp.set(null);
-
         synchronized (mLock) {
             mPendingUpdatePowerStateLocked = false;
             if (mPendingRequestLocked == null) {
@@ -1111,13 +1134,25 @@
             // user even when the display is all black.
             float animateValue = brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT
                     ? PowerManager.BRIGHTNESS_MIN : brightnessState;
-            if (isValidBrightnessValue(animateValue)) {
+            final float currentBrightness = mPowerState.getScreenBrightness();
+            if (isValidBrightnessValue(animateValue)
+                    && !BrightnessSynchronizer.floatEquals(animateValue, currentBrightness)) {
                 if (initialRampSkip || hasBrightnessBuckets
                         || wasOrWillBeInVr || !isDisplayContentVisible || brightnessIsTemporary) {
                     animateScreenBrightness(animateValue, SCREEN_ANIMATION_RATE_MINIMUM);
                 } else {
-                    animateScreenBrightness(animateValue,
-                            slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast);
+                    boolean isIncreasing = animateValue > currentBrightness;
+                    final float rampSpeed;
+                    if (isIncreasing && slowChange) {
+                        rampSpeed = mBrightnessRampRateSlowIncrease;
+                    } else if (isIncreasing && !slowChange) {
+                        rampSpeed = mBrightnessRampRateFastIncrease;
+                    } else if (!isIncreasing && slowChange) {
+                        rampSpeed = mBrightnessRampRateSlowDecrease;
+                    } else {
+                        rampSpeed = mBrightnessRampRateFastDecrease;
+                    }
+                    animateScreenBrightness(animateValue, rampSpeed);
                 }
             }
 
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index 54f30a9..51ba065 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -72,6 +72,8 @@
 
     private Runnable mCleanListener;
 
+    private volatile boolean mStopped;
+
     public DisplayPowerState(DisplayBlanker blanker, ColorFade colorFade, int displayId) {
         mHandler = new Handler(true /*async*/);
         mChoreographer = Choreographer.getInstance();
@@ -263,6 +265,18 @@
         }
     }
 
+    /**
+     * Interrupts all running threads; halting future work.
+     *
+     * This method should be called when the DisplayPowerState is no longer in use; i.e. when
+     * the {@link #mDisplayId display} has been removed.
+     */
+    public void stop() {
+        mHandler.removeCallbacksAndMessages(null);
+        mStopped = true;
+        mPhotonicModulator.interrupt();
+    }
+
     public void dump(PrintWriter pw) {
         pw.println();
         pw.println("Display Power State:");
@@ -427,7 +441,11 @@
                     if (!stateChanged && !backlightChanged) {
                         try {
                             mLock.wait();
-                        } catch (InterruptedException ex) { }
+                        } catch (InterruptedException ex) {
+                            if (mStopped) {
+                                return;
+                            }
+                        }
                         continue;
                     }
                     mActualState = state;
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index bb2fbed..ecb837d 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -17,7 +17,6 @@
 package com.android.server.display;
 
 import android.content.Context;
-import android.os.Process;
 import android.os.SystemProperties;
 import android.text.TextUtils;
 import android.util.IndentingPrintWriter;
@@ -55,6 +54,10 @@
     public static final int LOGICAL_DISPLAY_EVENT_SWAPPED = 4;
     public static final int LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED = 5;
 
+    public static final int DISPLAY_GROUP_EVENT_ADDED = 1;
+    public static final int DISPLAY_GROUP_EVENT_CHANGED = 2;
+    public static final int DISPLAY_GROUP_EVENT_REMOVED = 3;
+
     /**
      * Temporary display info, used for comparing display configurations.
      */
@@ -99,7 +102,7 @@
     private int mNextNonDefaultGroupId = DisplayGroup.DEFAULT + 1;
 
     /** A mapping from logical display id to display group. */
-    private final SparseArray<DisplayGroup> mDisplayGroups = new SparseArray<>();
+    private final SparseArray<DisplayGroup> mDisplayIdToGroupMap = new SparseArray<>();
 
     private final DisplayDeviceRepository mDisplayDeviceRepo;
     private final Listener mListener;
@@ -154,10 +157,6 @@
         return null;
     }
 
-    public int[] getDisplayIdsLocked() {
-        return getDisplayIdsLocked(Process.SYSTEM_UID);
-    }
-
     public int[] getDisplayIdsLocked(int callingUid) {
         final int count = mLogicalDisplays.size();
         int[] displayIds = new int[count];
@@ -182,13 +181,16 @@
         }
     }
 
-    public int getDisplayGroupIdLocked(int displayId) {
-        final DisplayGroup displayGroup = mDisplayGroups.get(displayId);
-        if (displayGroup != null) {
-            return displayGroup.getGroupId();
+    public DisplayGroup getDisplayGroupLocked(int groupId) {
+        final int size = mDisplayIdToGroupMap.size();
+        for (int i = 0; i < size; i++) {
+            final DisplayGroup displayGroup = mDisplayIdToGroupMap.valueAt(i);
+            if (displayGroup.getGroupId() == groupId) {
+                return displayGroup;
+            }
         }
 
-        return -1;
+        return null;
     }
 
     public void dumpLocked(PrintWriter pw) {
@@ -325,17 +327,31 @@
         mLogicalDisplays.put(displayId, display);
 
         final DisplayGroup displayGroup;
-        if (isDefault || (deviceInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0) {
+        final boolean addNewDisplayGroup =
+                isDefault || (deviceInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0;
+        if (addNewDisplayGroup) {
             final int groupId = assignDisplayGroupIdLocked(isDefault);
             displayGroup = new DisplayGroup(groupId);
         } else {
-            displayGroup = mDisplayGroups.get(Display.DEFAULT_DISPLAY);
+            displayGroup = mDisplayIdToGroupMap.get(Display.DEFAULT_DISPLAY);
         }
-        displayGroup.addDisplay(display);
-        mDisplayGroups.append(displayId, displayGroup);
+        displayGroup.addDisplayLocked(display);
+        mDisplayIdToGroupMap.append(displayId, displayGroup);
+
+        if (addNewDisplayGroup) {
+            // Group added events happen before Logical Display added events.
+            mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(),
+                    LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED);
+        }
 
         mListener.onLogicalDisplayEventLocked(display,
                 LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED);
+
+        if (!addNewDisplayGroup) {
+            // Group changed events happen after Logical Display added events.
+            mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(),
+                    LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED);
+        }
     }
 
     /**
@@ -352,31 +368,45 @@
             DisplayEventReceiver.FrameRateOverride[] frameRatesOverrides =
                     display.getFrameRateOverrides();
             display.updateLocked(mDisplayDeviceRepo);
+            final DisplayGroup changedDisplayGroup;
             if (!display.isValidLocked()) {
                 mLogicalDisplays.removeAt(i);
-                mDisplayGroups.removeReturnOld(displayId).removeDisplay(display);
+                final DisplayGroup displayGroup = mDisplayIdToGroupMap.removeReturnOld(displayId);
+                displayGroup.removeDisplayLocked(display);
 
                 mListener.onLogicalDisplayEventLocked(display,
                         LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED);
+
+                changedDisplayGroup = displayGroup;
             } else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) {
                 final int flags = display.getDisplayInfoLocked().flags;
-                final DisplayGroup defaultDisplayGroup = mDisplayGroups.get(
+                final DisplayGroup defaultDisplayGroup = mDisplayIdToGroupMap.get(
                         Display.DEFAULT_DISPLAY);
                 if ((flags & Display.FLAG_OWN_DISPLAY_GROUP) != 0) {
                     // The display should have its own DisplayGroup.
-                    if (defaultDisplayGroup.removeDisplay(display)) {
+                    if (defaultDisplayGroup.removeDisplayLocked(display)) {
                         final int groupId = assignDisplayGroupIdLocked(false);
                         final DisplayGroup displayGroup = new DisplayGroup(groupId);
-                        displayGroup.addDisplay(display);
-                        mDisplayGroups.append(display.getDisplayIdLocked(), displayGroup);
+                        displayGroup.addDisplayLocked(display);
+                        mDisplayIdToGroupMap.append(display.getDisplayIdLocked(), displayGroup);
+                        mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(),
+                                LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED);
+                        changedDisplayGroup = defaultDisplayGroup;
+                    } else {
+                        changedDisplayGroup = null;
                     }
                 } else {
                     // The display should be a part of the default DisplayGroup.
-                    final DisplayGroup displayGroup = mDisplayGroups.get(displayId);
+                    final DisplayGroup displayGroup = mDisplayIdToGroupMap.get(displayId);
                     if (displayGroup != defaultDisplayGroup) {
-                        displayGroup.removeDisplay(display);
-                        defaultDisplayGroup.addDisplay(display);
-                        mDisplayGroups.put(displayId, defaultDisplayGroup);
+                        displayGroup.removeDisplayLocked(display);
+                        defaultDisplayGroup.addDisplayLocked(display);
+                        mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(),
+                                LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED);
+                        mDisplayIdToGroupMap.put(displayId, defaultDisplayGroup);
+                        changedDisplayGroup = displayGroup;
+                    } else {
+                        changedDisplayGroup = null;
                     }
                 }
 
@@ -388,6 +418,7 @@
             } else if (!display.getPendingFrameRateOverrideUids().isEmpty()) {
                 mListener.onLogicalDisplayEventLocked(display,
                         LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
+                changedDisplayGroup = null;
             } else {
                 // While applications shouldn't know nor care about the non-overridden info, we
                 // still need to let WindowManager know so it can update its own internal state for
@@ -397,6 +428,15 @@
                     mListener.onLogicalDisplayEventLocked(display,
                             LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CHANGED);
                 }
+                changedDisplayGroup = null;
+            }
+
+            // CHANGED and REMOVED DisplayGroup events should always fire after Display events.
+            if (changedDisplayGroup != null) {
+                final int event = changedDisplayGroup.isEmptyLocked()
+                        ? LogicalDisplayMapper.DISPLAY_GROUP_EVENT_REMOVED
+                        : LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED;
+                mListener.onDisplayGroupEventLocked(changedDisplayGroup.getGroupId(), event);
             }
         }
     }
@@ -432,6 +472,7 @@
 
     public interface Listener {
         void onLogicalDisplayEventLocked(LogicalDisplay display, int event);
+        void onDisplayGroupEventLocked(int groupId, int event);
         void onTraversalRequested();
     }
 }
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index a0d9e8e..425e3ab 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -31,6 +31,8 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.security.FileIntegrityService;
+import com.android.server.security.VerityUtils;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -39,6 +41,7 @@
 import java.nio.ByteBuffer;
 import java.nio.NioUtils;
 import java.nio.channels.FileChannel;
+import java.util.Arrays;
 import java.util.Map;
 
 /** A service for managing system fonts. */
@@ -78,7 +81,17 @@
 
     private static class OtfFontFileParser implements UpdatableFontDir.FontFileParser {
         @Override
-        public long getVersion(File file) throws IOException {
+        public String getPostScriptName(File file) throws IOException {
+            ByteBuffer buffer = mmap(file);
+            try {
+                return FontFileUtil.getPostScriptName(buffer, 0);
+            } finally {
+                NioUtils.freeDirectBuffer(buffer);
+            }
+        }
+
+        @Override
+        public long getRevision(File file) throws IOException {
             ByteBuffer buffer = mmap(file);
             try {
                 return FontFileUtil.getRevision(buffer, 0);
@@ -95,18 +108,48 @@
         }
     }
 
+    private static class FsverityUtilImpl implements UpdatableFontDir.FsverityUtil {
+        @Override
+        public boolean hasFsverity(String filePath) {
+            return VerityUtils.hasFsverity(filePath);
+        }
+
+        @Override
+        public void setUpFsverity(String filePath, byte[] pkcs7Signature) throws IOException {
+            VerityUtils.setUpFsverity(filePath, pkcs7Signature);
+        }
+
+        @Override
+        public boolean rename(File src, File dest) {
+            // rename system call preserves fs-verity bit.
+            return src.renameTo(dest);
+        }
+    }
+
     @Nullable
     private final UpdatableFontDir mUpdatableFontDir;
 
     @GuardedBy("FontManagerService.this")
-    @Nullable SystemFontSettings mCurrentFontSettings = null;
+    @Nullable
+    private SystemFontSettings mCurrentFontSettings = null;
 
     private FontManagerService() {
-        mUpdatableFontDir = ENABLE_FONT_UPDATES
-                ? new UpdatableFontDir(new File(FONT_FILES_DIR), new OtfFontFileParser()) : null;
+        mUpdatableFontDir = createUpdatableFontDir();
     }
 
-    @NonNull private SystemFontSettings getCurrentFontSettings() {
+    @Nullable
+    private static UpdatableFontDir createUpdatableFontDir() {
+        if (!ENABLE_FONT_UPDATES) return null;
+        // If apk verity is supported, fs-verity should be available.
+        if (!FileIntegrityService.isApkVeritySupported()) return null;
+        return new UpdatableFontDir(new File(FONT_FILES_DIR),
+                Arrays.asList(new File(SystemFonts.SYSTEM_FONT_DIR),
+                        new File(SystemFonts.OEM_FONT_DIR)),
+                new OtfFontFileParser(), new FsverityUtilImpl());
+    }
+
+    @NonNull
+    private SystemFontSettings getCurrentFontSettings() {
         synchronized (FontManagerService.this) {
             if (mCurrentFontSettings == null) {
                 mCurrentFontSettings = SystemFontSettings.create(mUpdatableFontDir);
@@ -115,13 +158,14 @@
         }
     }
 
-    private boolean installFontFile(String name, FileDescriptor fd) {
+    // TODO(b/173619554): Expose as API.
+    private boolean installFontFile(FileDescriptor fd, byte[] pkcs7Signature) {
         if (mUpdatableFontDir == null) return false;
         synchronized (FontManagerService.this) {
             try {
-                mUpdatableFontDir.installFontFile(name, fd);
+                mUpdatableFontDir.installFontFile(fd, pkcs7Signature);
             } catch (IOException e) {
-                Slog.w(TAG, "Failed to install font file: " + name, e);
+                Slog.w(TAG, "Failed to install font file");
                 return false;
             }
             // Create updated font map in the next getSerializedSystemFontMap() call.
@@ -194,5 +238,5 @@
             }
             return null;
         }
-    };
+    }
 }
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index 7306471..8da579f 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -16,6 +16,7 @@
 
 package com.android.server.graphics.fonts;
 
+import android.annotation.Nullable;
 import android.os.FileUtils;
 import android.util.Base64;
 import android.util.Slog;
@@ -28,71 +29,158 @@
 import java.io.IOException;
 import java.security.SecureRandom;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 final class UpdatableFontDir {
 
     private static final String TAG = "UpdatableFontDir";
     private static final String RANDOM_DIR_PREFIX = "~~";
+    // TODO: Support .otf
+    private static final String ALLOWED_EXTENSION = ".ttf";
 
     /** Interface to mock font file access in tests. */
     interface FontFileParser {
-        long getVersion(File file) throws IOException;
+        String getPostScriptName(File file) throws IOException;
+
+        long getRevision(File file) throws IOException;
     }
 
-    /** Data class to hold font file path and version. */
-    static final class FontFileInfo {
-        final File mFile;
-        final long mVersion;
+    /** Interface to mock fs-verity in tests. */
+    interface FsverityUtil {
+        boolean hasFsverity(String path);
 
-        FontFileInfo(File file, long version) {
+        void setUpFsverity(String path, byte[] pkcs7Signature) throws IOException;
+
+        boolean rename(File src, File dest);
+    }
+
+    /** Data class to hold font file path and revision. */
+    private static final class FontFileInfo {
+        private final File mFile;
+        private final long mRevision;
+
+        FontFileInfo(File file, long revision) {
             mFile = file;
-            mVersion = version;
+            mRevision = revision;
+        }
+
+        public File getFile() {
+            return mFile;
+        }
+
+        /** Returns the unique randomized font dir containing this font file. */
+        public File getRandomizedFontDir() {
+            return mFile.getParentFile();
+        }
+
+        public long getRevision() {
+            return mRevision;
         }
     }
 
     /**
-     * Root directory for storing updated font files. Each font file is stored in a unique random
-     * dir. The font file path would be {@code mFilesDir/~~{randomStr}/{fontFileName}}.
+     * Root directory for storing updated font files. Each font file is stored in a unique
+     * randomized dir. The font file path would be {@code mFilesDir/~~{randomStr}/{fontFileName}}.
      */
     private final File mFilesDir;
+    private final List<File> mPreinstalledFontDirs;
     private final FontFileParser mParser;
+    private final FsverityUtil mFsverityUtil;
+    /**
+     * A mutable map containing mapping from font file name (e.g. "NotoColorEmoji.ttf") to {@link
+     * FontFileInfo}. All files in this map are validated, and have higher revision numbers than
+     * corresponding font files in {@link #mPreinstalledFontDirs}.
+     */
     @GuardedBy("UpdatableFontDir.this")
     private final Map<String, FontFileInfo> mFontFileInfoMap = new HashMap<>();
 
-    UpdatableFontDir(File filesDir, FontFileParser parser) {
+    UpdatableFontDir(File filesDir, List<File> preinstalledFontDirs, FontFileParser parser,
+            FsverityUtil fsverityUtil) {
         mFilesDir = filesDir;
+        mPreinstalledFontDirs = preinstalledFontDirs;
         mParser = parser;
+        mFsverityUtil = fsverityUtil;
         loadFontFileMap();
     }
 
     private void loadFontFileMap() {
+        // TODO: SIGBUS crash protection
         synchronized (UpdatableFontDir.this) {
+            boolean success = false;
             mFontFileInfoMap.clear();
-            File[] dirs = mFilesDir.listFiles();
-            if (dirs == null) return;
-            for (File dir : dirs) {
-                if (!dir.getName().startsWith(RANDOM_DIR_PREFIX)) continue;
-                File[] files = dir.listFiles();
-                if (files == null || files.length != 1) continue;
-                addFileToMapLocked(files[0], true);
+            try {
+                File[] dirs = mFilesDir.listFiles();
+                if (dirs == null) return;
+                for (File dir : dirs) {
+                    if (!dir.getName().startsWith(RANDOM_DIR_PREFIX)) return;
+                    File[] files = dir.listFiles();
+                    if (files == null || files.length != 1) return;
+                    FontFileInfo fontFileInfo = validateFontFile(files[0]);
+                    if (fontFileInfo == null) {
+                        Slog.w(TAG, "Broken file is found. Clearing files.");
+                        return;
+                    }
+                    addFileToMapLocked(fontFileInfo, true /* deleteOldFile */);
+                }
+                success = true;
+            } finally {
+                // Delete all files just in case if we find a problematic file.
+                if (!success) {
+                    mFontFileInfoMap.clear();
+                    FileUtils.deleteContents(mFilesDir);
+                }
             }
         }
     }
 
-    void installFontFile(String name, FileDescriptor fd) throws IOException {
-        // TODO: Validate name.
+    /**
+     * Installs a new font file, or updates an existing font file.
+     *
+     * <p>The new font will be immediately available for new Zygote-forked processes through
+     * {@link #getFontFileMap()}. Old font files will be kept until next system server reboot,
+     * because existing Zygote-forked processes have paths to old font files.
+     *
+     * @param fd             A file descriptor to the font file.
+     * @param pkcs7Signature A PKCS#7 detached signature to enable fs-verity for the font file.
+     */
+    void installFontFile(FileDescriptor fd, byte[] pkcs7Signature) throws IOException {
         synchronized (UpdatableFontDir.this) {
-            // TODO: proper error handling
             File newDir = getRandomDir(mFilesDir);
             if (!newDir.mkdir()) {
+                // TODO: Define and return an error code for API
                 throw new IOException("Failed to create a new dir");
             }
-            File newFontFile = new File(newDir, name);
-            try (FileOutputStream out = new FileOutputStream(newFontFile)) {
-                FileUtils.copy(fd, out.getFD());
+            boolean success = false;
+            try {
+                File tempNewFontFile = new File(newDir, "font.ttf");
+                try (FileOutputStream out = new FileOutputStream(tempNewFontFile)) {
+                    FileUtils.copy(fd, out.getFD());
+                }
+                // Do not parse font file before setting up fs-verity.
+                // setUpFsverity throws IOException if failed.
+                mFsverityUtil.setUpFsverity(tempNewFontFile.getAbsolutePath(), pkcs7Signature);
+                String postScriptName = mParser.getPostScriptName(tempNewFontFile);
+                File newFontFile = new File(newDir, postScriptName + ALLOWED_EXTENSION);
+                if (!mFsverityUtil.rename(tempNewFontFile, newFontFile)) {
+                    // TODO: Define and return an error code for API
+                    throw new IOException("Failed to rename");
+                }
+                FontFileInfo fontFileInfo = validateFontFile(newFontFile);
+                if (fontFileInfo == null) {
+                    // TODO: Define and return an error code for API
+                    throw new IllegalArgumentException("Invalid file");
+                }
+                if (!addFileToMapLocked(fontFileInfo, false)) {
+                    // TODO: Define and return an error code for API
+                    throw new IllegalArgumentException("Version downgrade");
+                }
+                success = true;
+            } finally {
+                if (!success) {
+                    FileUtils.deleteContentsAndDir(newDir);
+                }
             }
-            addFileToMapLocked(newFontFile, false);
         }
     }
 
@@ -114,27 +202,110 @@
         return dir;
     }
 
-    private void addFileToMapLocked(File file, boolean deleteOldFile) {
-        final long version;
+    /**
+     * Add the given {@link FontFileInfo} to {@link #mFontFileInfoMap} if its font revision is
+     * higher than the currently used font file (either in {@link #mFontFileInfoMap} or {@link
+     * #mPreinstalledFontDirs}).
+     */
+    private boolean addFileToMapLocked(FontFileInfo fontFileInfo, boolean deleteOldFile) {
+        String name = fontFileInfo.getFile().getName();
+        FontFileInfo existingInfo = mFontFileInfoMap.get(name);
+        final boolean shouldAddToMap;
+        if (existingInfo == null) {
+            // We got a new updatable font. We need to check if it's newer than preinstalled fonts.
+            // Note that getPreinstalledFontRevision() returns -1 if there is no preinstalled font
+            // with 'name'.
+            shouldAddToMap = getPreinstalledFontRevision(name) < fontFileInfo.getRevision();
+        } else {
+            shouldAddToMap = existingInfo.getRevision() < fontFileInfo.getRevision();
+        }
+        if (shouldAddToMap) {
+            if (deleteOldFile && existingInfo != null) {
+                FileUtils.deleteContentsAndDir(existingInfo.getRandomizedFontDir());
+            }
+            mFontFileInfoMap.put(name, fontFileInfo);
+            return true;
+        } else {
+            if (deleteOldFile) {
+                FileUtils.deleteContentsAndDir(fontFileInfo.getRandomizedFontDir());
+            }
+            return false;
+        }
+    }
+
+    private long getPreinstalledFontRevision(String name) {
+        long maxRevision = -1;
+        for (File dir : mPreinstalledFontDirs) {
+            File preinstalledFontFile = new File(dir, name);
+            if (!preinstalledFontFile.exists()) continue;
+            long revision = getFontRevision(preinstalledFontFile);
+            if (revision == -1) {
+                Slog.w(TAG, "Invalid preinstalled font file");
+                continue;
+            }
+            if (revision > maxRevision) {
+                maxRevision = revision;
+            }
+        }
+        return maxRevision;
+    }
+
+    /**
+     * Checks the fs-verity protection status of the given font file, validates the file name, and
+     * returns a {@link FontFileInfo} on success. This method does not check if the font revision
+     * is higher than the currently used font.
+     */
+    @Nullable
+    private FontFileInfo validateFontFile(File file) {
+        if (!mFsverityUtil.hasFsverity(file.getAbsolutePath())) {
+            Slog.w(TAG, "Font validation failed. Fs-verity is not enabled: " + file);
+            return null;
+        }
+        if (!validateFontFileName(file)) {
+            Slog.w(TAG, "Font validation failed. Could not validate font file name: " + file);
+            return null;
+        }
+        long revision = getFontRevision(file);
+        if (revision == -1) {
+            Slog.w(TAG, "Font validation failed. Could not read font revision: " + file);
+            return null;
+        }
+        return new FontFileInfo(file, revision);
+    }
+
+    /**
+     * Returns true if the font file's file name matches with the PostScript name metadata in the
+     * font file.
+     *
+     * <p>We check the font file names because apps use file name to look up fonts.
+     * <p>Because PostScript name does not include extension, the extension is appended for
+     * comparison. For example, if the file name is "NotoColorEmoji.ttf", the PostScript name should
+     * be "NotoColorEmoji".
+     */
+    private boolean validateFontFileName(File file) {
+        String fileName = file.getName();
+        String postScriptName = getPostScriptName(file);
+        return (postScriptName + ALLOWED_EXTENSION).equals(fileName);
+    }
+
+    /** Returns the PostScript name of the given font file, or null. */
+    @Nullable
+    private String getPostScriptName(File file) {
         try {
-            version = mParser.getVersion(file);
+            return mParser.getPostScriptName(file);
         } catch (IOException e) {
             Slog.e(TAG, "Failed to read font file", e);
-            return;
+            return null;
         }
-        if (version == -1) {
-            Slog.e(TAG, "Invalid font file");
-            return;
-        }
-        FontFileInfo info = mFontFileInfoMap.get(file.getName());
-        if (info == null) {
-            // TODO: check version of font in /system/fonts and /product/fonts
-            mFontFileInfoMap.put(file.getName(), new FontFileInfo(file, version));
-        } else if (info.mVersion < version) {
-            if (deleteOldFile) {
-                FileUtils.deleteContentsAndDir(info.mFile.getParentFile());
-            }
-            mFontFileInfoMap.put(file.getName(), new FontFileInfo(file, version));
+    }
+
+    /** Returns the non-negative font revision of the given font file, or -1. */
+    private long getFontRevision(File file) {
+        try {
+            return mParser.getRevision(file);
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to read font file", e);
+            return -1;
         }
     }
 
@@ -142,7 +313,7 @@
         Map<String, File> map = new HashMap<>();
         synchronized (UpdatableFontDir.this) {
             for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) {
-                map.put(entry.getKey(), entry.getValue().mFile);
+                map.put(entry.getKey(), entry.getValue().getFile());
             }
         }
         return map;
diff --git a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
new file mode 100644
index 0000000..8399f54
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
@@ -0,0 +1,249 @@
+/*
+ * 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.locksettings;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ParcelableException;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.service.resumeonreboot.IResumeOnRebootService;
+import android.service.resumeonreboot.ResumeOnRebootService;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/** @hide */
+public class ResumeOnRebootServiceProvider {
+
+    private static final String PROVIDER_PACKAGE = DeviceConfig.getString(
+            DeviceConfig.NAMESPACE_OTA, "resume_on_reboot_service_package", "");
+    private static final String PROVIDER_REQUIRED_PERMISSION =
+            Manifest.permission.BIND_RESUME_ON_REBOOT_SERVICE;
+    private static final String TAG = "ResumeOnRebootServiceProvider";
+
+    private final Context mContext;
+    private final PackageManager mPackageManager;
+
+    public ResumeOnRebootServiceProvider(Context context) {
+        this(context, context.getPackageManager());
+    }
+
+    @VisibleForTesting
+    public ResumeOnRebootServiceProvider(Context context, PackageManager packageManager) {
+        this.mContext = context;
+        this.mPackageManager = packageManager;
+    }
+
+    @Nullable
+    private ServiceInfo resolveService() {
+        Intent intent = new Intent();
+        intent.setAction(ResumeOnRebootService.SERVICE_INTERFACE);
+        if (PROVIDER_PACKAGE != null && !PROVIDER_PACKAGE.equals("")) {
+            intent.setPackage(PROVIDER_PACKAGE);
+        }
+
+        List<ResolveInfo> resolvedIntents =
+                mPackageManager.queryIntentServices(intent, PackageManager.MATCH_SYSTEM_ONLY);
+        for (ResolveInfo resolvedInfo : resolvedIntents) {
+            if (resolvedInfo.serviceInfo != null
+                    && PROVIDER_REQUIRED_PERMISSION.equals(resolvedInfo.serviceInfo.permission)) {
+                return resolvedInfo.serviceInfo;
+            }
+        }
+        return null;
+    }
+
+    /** Creates a new {@link ResumeOnRebootServiceConnection} */
+    @Nullable
+    public ResumeOnRebootServiceConnection getServiceConnection() {
+        ServiceInfo serviceInfo = resolveService();
+        if (serviceInfo == null) {
+            return null;
+        }
+        return new ResumeOnRebootServiceConnection(mContext, serviceInfo.getComponentName());
+    }
+
+    /**
+     * Connection class used for contacting the registered {@link IResumeOnRebootService}
+     */
+    public static class ResumeOnRebootServiceConnection {
+
+        private static final String TAG = "ResumeOnRebootServiceConnection";
+        private final Context mContext;
+        private final ComponentName mComponentName;
+        private IResumeOnRebootService mBinder;
+
+        private ResumeOnRebootServiceConnection(Context context,
+                @NonNull ComponentName componentName) {
+            mContext = context;
+            mComponentName = componentName;
+        }
+
+        /** Unbind from the service */
+        public void unbindService() {
+            mContext.unbindService(new ServiceConnection() {
+                @Override
+                public void onServiceConnected(ComponentName name, IBinder service) {
+                }
+
+                @Override
+                public void onServiceDisconnected(ComponentName name) {
+                    mBinder = null;
+
+                }
+            });
+        }
+
+        /** Bind to the service */
+        public void bindToService(long timeOut) throws TimeoutException {
+            if (mBinder == null || !mBinder.asBinder().isBinderAlive()) {
+                CountDownLatch connectionLatch = new CountDownLatch(1);
+                Intent intent = new Intent();
+                intent.setComponent(mComponentName);
+                final boolean success = mContext.bindServiceAsUser(intent, new ServiceConnection() {
+                            @Override
+                            public void onServiceConnected(ComponentName name, IBinder service) {
+                                mBinder = IResumeOnRebootService.Stub.asInterface(service);
+                                connectionLatch.countDown();
+                            }
+
+                            @Override
+                            public void onServiceDisconnected(ComponentName name) {
+                            }
+                        },
+                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+                        BackgroundThread.getHandler(), UserHandle.SYSTEM);
+
+                if (!success) {
+                    Slog.e(TAG, "Binding: " + mComponentName + " u" + UserHandle.SYSTEM
+                            + " failed.");
+                    return;
+                }
+                waitForLatch(connectionLatch, "serviceConnection", timeOut);
+            }
+        }
+
+        /** Wrap opaque blob */
+        public byte[] wrapBlob(byte[] unwrappedBlob, long lifeTimeInMillis,
+                long timeOutInMillis)
+                throws RemoteException, TimeoutException, IOException {
+            if (mBinder == null || !mBinder.asBinder().isBinderAlive()) {
+                throw new RemoteException("Service not bound");
+            }
+            CountDownLatch binderLatch = new CountDownLatch(1);
+            ResumeOnRebootServiceCallback
+                    resultCallback =
+                    new ResumeOnRebootServiceCallback(
+                            binderLatch);
+            mBinder.wrapSecret(unwrappedBlob, lifeTimeInMillis, new RemoteCallback(resultCallback));
+            waitForLatch(binderLatch, "wrapSecret", timeOutInMillis);
+            if (resultCallback.getResult().containsKey(ResumeOnRebootService.EXCEPTION_KEY)) {
+                throwTypedException(resultCallback.getResult().getParcelable(
+                        ResumeOnRebootService.EXCEPTION_KEY));
+            }
+            return resultCallback.mResult.getByteArray(ResumeOnRebootService.WRAPPED_BLOB_KEY);
+        }
+
+        /** Unwrap wrapped blob */
+        public byte[] unwrap(byte[] wrappedBlob, long timeOut)
+                throws RemoteException, TimeoutException, IOException {
+            if (mBinder == null || !mBinder.asBinder().isBinderAlive()) {
+                throw new RemoteException("Service not bound");
+            }
+            CountDownLatch binderLatch = new CountDownLatch(1);
+            ResumeOnRebootServiceCallback
+                    resultCallback =
+                    new ResumeOnRebootServiceCallback(
+                            binderLatch);
+            mBinder.unwrap(wrappedBlob, new RemoteCallback(resultCallback));
+            waitForLatch(binderLatch, "unWrapSecret", timeOut);
+            if (resultCallback.getResult().containsKey(ResumeOnRebootService.EXCEPTION_KEY)) {
+                throwTypedException(resultCallback.getResult().getParcelable(
+                        ResumeOnRebootService.EXCEPTION_KEY));
+            }
+            return resultCallback.getResult().getByteArray(
+                    ResumeOnRebootService.UNWRAPPED_BLOB_KEY);
+        }
+
+        private void throwTypedException(
+                ParcelableException exception)
+                throws IOException {
+            if (exception.getCause() instanceof IOException) {
+                exception.maybeRethrow(IOException.class);
+            } else if (exception.getCause() instanceof IllegalStateException) {
+                exception.maybeRethrow(IllegalStateException.class);
+            } else {
+                // This should not happen. Wrap the cause in IllegalStateException so that it
+                // doesn't disrupt the exception handling
+                throw new IllegalStateException(exception.getCause());
+            }
+        }
+
+        private void waitForLatch(CountDownLatch latch, String reason, long timeOut)
+                throws TimeoutException {
+            try {
+                if (!latch.await(timeOut, TimeUnit.SECONDS)) {
+                    throw new TimeoutException("Latch wait for " + reason + " elapsed");
+                }
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                throw new IllegalStateException("Latch wait for " + reason + " interrupted");
+            }
+        }
+    }
+
+    private static class ResumeOnRebootServiceCallback implements
+            RemoteCallback.OnResultListener {
+
+        private final CountDownLatch mResultLatch;
+        private Bundle mResult;
+
+        private ResumeOnRebootServiceCallback(CountDownLatch resultLatch) {
+            this.mResultLatch = resultLatch;
+        }
+
+        @Override
+        public void onResult(@Nullable Bundle result) {
+            this.mResult = result;
+            mResultLatch.countDown();
+        }
+
+        private Bundle getResult() {
+            return mResult;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 141fa6a..f92f3dc 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -39,11 +39,6 @@
     public abstract void resetUserState(int userId);
 
     /**
-     * @return true if the given uid is restricted from doing networking on metered networks.
-     */
-    public abstract boolean isUidRestrictedOnMeteredNetworks(int uid);
-
-    /**
      * Figure out if networking is blocked for a given set of conditions.
      *
      * This is used by ConnectivityService via passing stale copies of conditions, so it must not
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 5ed7a96..29eaf4f 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -72,6 +72,7 @@
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 import static android.net.NetworkPolicyManager.RULE_REJECT_RESTRICTED_MODE;
 import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
+import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED;
 import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
 import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground;
 import static android.net.NetworkPolicyManager.resolveNetworkId;
@@ -234,6 +235,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.ConcurrentUtils;
@@ -3483,13 +3485,27 @@
 
     @Override
     public void setSubscriptionOverride(int subId, int overrideMask, int overrideValue,
-            long timeoutMillis, String callingPackage) {
+            int[] networkTypes, long timeoutMillis, String callingPackage) {
         enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
 
-        // We can only override when carrier told us about plans
+        final ArraySet<Integer> allNetworksSet = new ArraySet<>();
+        addAll(allNetworksSet, TelephonyManager.getAllNetworkTypes());
+        final IntArray applicableNetworks = new IntArray();
+
+        // ensure all network types are valid
+        for (int networkType : networkTypes) {
+            if (allNetworksSet.contains(networkType)) {
+                applicableNetworks.add(networkType);
+            } else {
+                Log.d(TAG, "setSubscriptionOverride removing invalid network type: " + networkType);
+            }
+        }
+
+        // We can only override when carrier told us about plans. For the unmetered case,
+        // allow override without having plans defined.
         synchronized (mNetworkPoliciesSecondLock) {
             final SubscriptionPlan plan = getPrimarySubscriptionPlanLocked(subId);
-            if (plan == null
+            if (overrideMask != SUBSCRIPTION_OVERRIDE_UNMETERED && plan == null
                     || plan.getDataLimitBehavior() == SubscriptionPlan.LIMIT_BEHAVIOR_UNKNOWN) {
                 throw new IllegalStateException(
                         "Must provide valid SubscriptionPlan to enable overriding");
@@ -3501,11 +3517,16 @@
         final boolean overrideEnabled = Settings.Global.getInt(mContext.getContentResolver(),
                 NETPOLICY_OVERRIDE_ENABLED, 1) != 0;
         if (overrideEnabled || overrideValue == 0) {
-            mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE,
-                    overrideMask, overrideValue, subId));
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = subId;
+            args.arg2 = overrideMask;
+            args.arg3 = overrideValue;
+            args.arg4 = applicableNetworks.toArray();
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args));
             if (timeoutMillis > 0) {
-                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE,
-                        overrideMask, 0, subId), timeoutMillis);
+                args.arg3 = 0;
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args),
+                        timeoutMillis);
             }
         }
     }
@@ -4773,10 +4794,10 @@
     }
 
     private void dispatchSubscriptionOverride(INetworkPolicyListener listener, int subId,
-            int overrideMask, int overrideValue) {
+            int overrideMask, int overrideValue, int[] networkTypes) {
         if (listener != null) {
             try {
-                listener.onSubscriptionOverride(subId, overrideMask, overrideValue);
+                listener.onSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes);
             } catch (RemoteException ignored) {
             }
         }
@@ -4908,13 +4929,16 @@
                     return true;
                 }
                 case MSG_SUBSCRIPTION_OVERRIDE: {
-                    final int overrideMask = msg.arg1;
-                    final int overrideValue = msg.arg2;
-                    final int subId = (int) msg.obj;
+                    final SomeArgs args = (SomeArgs) msg.obj;
+                    final int subId = (int) args.arg1;
+                    final int overrideMask = (int) args.arg2;
+                    final int overrideValue = (int) args.arg3;
+                    final int[] networkTypes = (int[]) args.arg4;
                     final int length = mListeners.beginBroadcast();
                     for (int i = 0; i < length; i++) {
                         final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
-                        dispatchSubscriptionOverride(listener, subId, overrideMask, overrideValue);
+                        dispatchSubscriptionOverride(listener, subId, overrideMask, overrideValue,
+                                networkTypes);
                     }
                     mListeners.finishBroadcast();
                     return true;
@@ -5359,7 +5383,7 @@
     public boolean isUidNetworkingBlocked(int uid, boolean isNetworkMetered) {
         final long startTime = mStatLogger.getTime();
 
-        enforceAnyPermissionOf(OBSERVE_NETWORK_POLICY, PERMISSION_MAINLINE_NETWORK_STACK);
+        mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
         final int uidRules;
         final boolean isBackgroundRestricted;
         synchronized (mUidRulesFirstLock) {
@@ -5374,6 +5398,23 @@
         return ret;
     }
 
+    @Override
+    public boolean isUidRestrictedOnMeteredNetworks(int uid) {
+        mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
+        final int uidRules;
+        final boolean isBackgroundRestricted;
+        synchronized (mUidRulesFirstLock) {
+            uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
+            isBackgroundRestricted = mRestrictBackground;
+        }
+        //TODO(b/177490332): The logic here might not be correct because it doesn't consider
+        // RULE_REJECT_METERED condition. And it could be replaced by
+        // isUidNetworkingBlockedInternal().
+        return isBackgroundRestricted
+                && !hasRule(uidRules, RULE_ALLOW_METERED)
+                && !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED);
+    }
+
     private static boolean isSystem(int uid) {
         return uid < Process.FIRST_APPLICATION_UID;
     }
@@ -5442,22 +5483,6 @@
             }
         }
 
-        /**
-         * @return true if the given uid is restricted from doing networking on metered networks.
-         */
-        @Override
-        public boolean isUidRestrictedOnMeteredNetworks(int uid) {
-            final int uidRules;
-            final boolean isBackgroundRestricted;
-            synchronized (mUidRulesFirstLock) {
-                uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
-                isBackgroundRestricted = mRestrictBackground;
-            }
-            return isBackgroundRestricted
-                    && !hasRule(uidRules, RULE_ALLOW_METERED)
-                    && !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED);
-        }
-
         @Override
         public void onTempPowerSaveWhitelistChange(int appId, boolean added) {
             synchronized (mUidRulesFirstLock) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c397724..e604b5e 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6432,7 +6432,6 @@
         private final int mRank;
         private final int mCount;
         private final ManagedServiceInfo mListener;
-        private final long mWhen;
 
         CancelNotificationRunnable(final int callingUid, final int callingPid,
                 final String pkg, final String tag, final int id,
@@ -6452,7 +6451,6 @@
             this.mRank = rank;
             this.mCount = count;
             this.mListener = listener;
-            this.mWhen = System.currentTimeMillis();
         }
 
         @Override
@@ -6464,33 +6462,8 @@
             }
 
             synchronized (mNotificationLock) {
-                // Check to see if there is a notification in the enqueued list that hasn't had a
-                // chance to post yet.
-                List<NotificationRecord> enqueued = findEnqueuedNotificationsForCriteria(
-                        mPkg, mTag, mId, mUserId);
-                boolean repost = false;
-                if (enqueued.size() > 0) {
-                    // Found something, let's see what it was
-                    repost = true;
-                    // If all enqueues happened before this cancel then wait for them to happen,
-                    // otherwise we should let this cancel through so the next enqueue happens
-                    for (NotificationRecord r : enqueued) {
-                        if (r.mUpdateTimeMs > mWhen) {
-                            // At least one enqueue was posted after the cancel, so we're invalid
-                            Slog.i(TAG, "notification cancel ignored due to newer enqueued entry"
-                                    + "key=" + r.getSbn().getKey());
-                            return;
-                        }
-                    }
-                }
-                if (repost) {
-                    mHandler.post(this);
-                    return;
-                }
-
-                // Look for the notification in the posted list, since we already checked enqueued.
-                NotificationRecord r =
-                        findNotificationByListLocked(mNotificationList, mPkg, mTag, mId, mUserId);
+                // Look for the notification, searching both the posted and enqueued lists.
+                NotificationRecord r = findNotificationLocked(mPkg, mTag, mId, mUserId);
                 if (r != null) {
                     // The notification was found, check if it should be removed.
 
@@ -6513,10 +6486,6 @@
                     if ((r.getNotification().flags & mMustNotHaveFlags) != 0) {
                         return;
                     }
-                    if (r.getUpdateTimeMs() > mWhen) {
-                        // In this case, a post must have slipped by when this runnable reposted
-                        return;
-                    }
 
                     // Bubbled children get to stick around if the summary was manually cancelled
                     // (user removed) from systemui.
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index d7a1ba2..50fb176 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -24,11 +24,15 @@
 import static android.content.Intent.ACTION_USER_ADDED;
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.EXTRA_REASON;
+import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_DISABLED;
+import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_ENABLED;
 import static android.content.pm.PackageManager.SIGNATURE_MATCH;
 import static android.os.Trace.TRACE_TAG_RRO;
 import static android.os.Trace.traceBegin;
 import static android.os.Trace.traceEnd;
 
+import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -40,6 +44,7 @@
 import android.content.IntentFilter;
 import android.content.om.IOverlayManager;
 import android.content.om.OverlayInfo;
+import android.content.om.OverlayManagerTransaction;
 import android.content.om.OverlayableInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
@@ -49,6 +54,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Environment;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -65,7 +71,6 @@
 
 import com.android.internal.content.om.OverlayConfig;
 import com.android.server.FgThread;
-import com.android.server.IoThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
 import com.android.server.SystemService;
@@ -84,12 +89,15 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Consumer;
 
 /**
  * Service to manage asset overlays.
@@ -238,7 +246,14 @@
 
     private final OverlayActorEnforcer mActorEnforcer;
 
-    private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false);
+    private final Consumer<PackageAndUser> mPropagateOverlayChange = (pair) -> {
+        persistSettings();
+        FgThread.getHandler().post(() -> {
+            List<String> affectedTargets = updatePackageManager(pair.packageName, pair.userId);
+            updateActivityManager(affectedTargets, pair.userId);
+            broadcastActionOverlayChanged(pair.packageName, pair.userId);
+        });
+    };
 
     public OverlayManagerService(@NonNull final Context context) {
         super(context);
@@ -251,17 +266,19 @@
             IdmapManager im = new IdmapManager(IdmapDaemon.getInstance(), mPackageManager);
             mSettings = new OverlayManagerSettings();
             mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
-                    OverlayConfig.getSystemInstance(), getDefaultOverlayPackages(),
-                    new OverlayChangeListener());
+                    OverlayConfig.getSystemInstance(), getDefaultOverlayPackages());
             mActorEnforcer = new OverlayActorEnforcer(mPackageManager);
 
+            HandlerThread packageReceiverThread = new HandlerThread(TAG);
+            packageReceiverThread.start();
+
             final IntentFilter packageFilter = new IntentFilter();
             packageFilter.addAction(ACTION_PACKAGE_ADDED);
             packageFilter.addAction(ACTION_PACKAGE_CHANGED);
             packageFilter.addAction(ACTION_PACKAGE_REMOVED);
             packageFilter.addDataScheme("package");
             getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
-                    packageFilter, null, null);
+                    packageFilter, null, packageReceiverThread.getThreadHandler());
 
             final IntentFilter userFilter = new IntentFilter();
             userFilter.addAction(ACTION_USER_ADDED);
@@ -294,11 +311,11 @@
             for (int i = 0; i < userCount; i++) {
                 final UserInfo userInfo = users.get(i);
                 if (!userInfo.supportsSwitchTo() && userInfo.id != UserHandle.USER_SYSTEM) {
-                    // Initialize any users that can't be switched to, as there state would
+                    // Initialize any users that can't be switched to, as their state would
                     // never be setup in onSwitchUser(). We will switch to the system user right
                     // after this, and its state will be setup there.
                     final List<String> targets = mImpl.updateOverlaysForUser(users.get(i).id);
-                    updateOverlayPaths(users.get(i).id, targets);
+                    updatePackageManager(targets, users.get(i).id);
                 }
             }
         }
@@ -316,9 +333,10 @@
             // any asset changes to the rest of the system
             synchronized (mLock) {
                 final List<String> targets = mImpl.updateOverlaysForUser(newUserId);
-                updateAssets(newUserId, targets);
+                final List<String> affectedTargets = updatePackageManager(targets, newUserId);
+                updateActivityManager(affectedTargets, newUserId);
             }
-            schedulePersistSettings();
+            persistSettings();
         } finally {
             traceEnd(TRACE_TAG_RRO);
         }
@@ -402,10 +420,17 @@
                                 false);
                         if (pi != null && !pi.applicationInfo.isInstantApp()) {
                             mPackageManager.cachePackageInfo(packageName, userId, pi);
-                            if (pi.isOverlayPackage()) {
-                                mImpl.onOverlayPackageAdded(packageName, userId);
-                            } else {
-                                mImpl.onTargetPackageAdded(packageName, userId);
+
+                            try {
+                                if (pi.isOverlayPackage()) {
+                                    mImpl.onOverlayPackageAdded(packageName, userId)
+                                        .ifPresent(mPropagateOverlayChange);
+                                } else {
+                                    mImpl.onTargetPackageAdded(packageName, userId)
+                                        .ifPresent(mPropagateOverlayChange);
+                                }
+                            } catch (OperationFailedException e) {
+                                Slog.e(TAG, "onPackageAdded internal error", e);
                             }
                         }
                     }
@@ -425,10 +450,17 @@
                                 false);
                         if (pi != null && pi.applicationInfo.isInstantApp()) {
                             mPackageManager.cachePackageInfo(packageName, userId, pi);
-                            if (pi.isOverlayPackage()) {
-                                mImpl.onOverlayPackageChanged(packageName, userId);
-                            }  else {
-                                mImpl.onTargetPackageChanged(packageName, userId);
+
+                            try {
+                                if (pi.isOverlayPackage()) {
+                                    mImpl.onOverlayPackageChanged(packageName, userId)
+                                        .ifPresent(mPropagateOverlayChange);
+                                }  else {
+                                    mImpl.onTargetPackageChanged(packageName, userId)
+                                        .ifPresent(mPropagateOverlayChange);
+                                }
+                            } catch (OperationFailedException e) {
+                                Slog.e(TAG, "onPackageChanged internal error", e);
                             }
                         }
                     }
@@ -447,7 +479,12 @@
                         mPackageManager.forgetPackageInfo(packageName, userId);
                         final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
                         if (oi != null) {
-                            mImpl.onOverlayPackageReplacing(packageName, userId);
+                            try {
+                                mImpl.onOverlayPackageReplacing(packageName, userId)
+                                    .ifPresent(mPropagateOverlayChange);
+                            } catch (OperationFailedException e) {
+                                Slog.e(TAG, "onPackageReplacing internal error", e);
+                            }
                         }
                     }
                 }
@@ -466,10 +503,16 @@
                                 false);
                         if (pi != null && !pi.applicationInfo.isInstantApp()) {
                             mPackageManager.cachePackageInfo(packageName, userId, pi);
-                            if (pi.isOverlayPackage()) {
-                                mImpl.onOverlayPackageReplaced(packageName, userId);
-                            } else {
-                                mImpl.onTargetPackageReplaced(packageName, userId);
+                            try {
+                                if (pi.isOverlayPackage()) {
+                                    mImpl.onOverlayPackageReplaced(packageName, userId)
+                                        .ifPresent(mPropagateOverlayChange);
+                                } else {
+                                    mImpl.onTargetPackageReplaced(packageName, userId)
+                                        .ifPresent(mPropagateOverlayChange);
+                                }
+                            } catch (OperationFailedException e) {
+                                Slog.e(TAG, "onPackageReplaced internal error", e);
                             }
                         }
                     }
@@ -487,10 +530,17 @@
                     synchronized (mLock) {
                         mPackageManager.forgetPackageInfo(packageName, userId);
                         final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
-                        if (oi != null) {
-                            mImpl.onOverlayPackageRemoved(packageName, userId);
-                        } else {
-                            mImpl.onTargetPackageRemoved(packageName, userId);
+
+                        try {
+                            if (oi != null) {
+                                mImpl.onOverlayPackageRemoved(packageName, userId)
+                                    .ifPresent(mPropagateOverlayChange);
+                            } else {
+                                mImpl.onTargetPackageRemoved(packageName, userId)
+                                    .ifPresent(mPropagateOverlayChange);
+                            }
+                        } catch (OperationFailedException e) {
+                            Slog.e(TAG, "onPackageRemoved internal error", e);
                         }
                     }
                 }
@@ -513,7 +563,7 @@
                             synchronized (mLock) {
                                 targets = mImpl.updateOverlaysForUser(userId);
                             }
-                            updateOverlayPaths(userId, targets);
+                            updatePackageManager(targets, userId);
                         } finally {
                             traceEnd(TRACE_TAG_RRO);
                         }
@@ -608,7 +658,13 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
-                        return mImpl.setEnabled(packageName, enable, realUserId);
+                        try {
+                            mImpl.setEnabled(packageName, enable, realUserId)
+                                .ifPresent(mPropagateOverlayChange);
+                            return true;
+                        } catch (OperationFailedException e) {
+                            return false;
+                        }
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -633,8 +689,14 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
-                        return mImpl.setEnabledExclusive(packageName, false /* withinCategory */,
-                                realUserId);
+                        try {
+                            mImpl.setEnabledExclusive(packageName,
+                                    false /* withinCategory */, realUserId)
+                                .ifPresent(mPropagateOverlayChange);
+                            return true;
+                        } catch (OperationFailedException e) {
+                            return false;
+                        }
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -660,8 +722,14 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
-                        return mImpl.setEnabledExclusive(packageName, true /* withinCategory */,
-                                realUserId);
+                        try {
+                            mImpl.setEnabledExclusive(packageName,
+                                    true /* withinCategory */, realUserId)
+                                .ifPresent(mPropagateOverlayChange);
+                            return true;
+                        } catch (OperationFailedException e) {
+                            return false;
+                        }
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -687,7 +755,13 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
-                        return mImpl.setPriority(packageName, parentPackageName, realUserId);
+                        try {
+                            mImpl.setPriority(packageName, parentPackageName, realUserId)
+                                .ifPresent(mPropagateOverlayChange);
+                            return true;
+                        } catch (OperationFailedException e) {
+                            return false;
+                        }
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -711,7 +785,13 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
-                        return mImpl.setHighestPriority(packageName, realUserId);
+                        try {
+                            mImpl.setHighestPriority(packageName, realUserId)
+                                .ifPresent(mPropagateOverlayChange);
+                            return true;
+                        } catch (OperationFailedException e) {
+                            return false;
+                        }
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -735,7 +815,13 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
-                        return mImpl.setLowestPriority(packageName, realUserId);
+                        try {
+                            mImpl.setLowestPriority(packageName, realUserId)
+                                .ifPresent(mPropagateOverlayChange);
+                            return true;
+                        } catch (OperationFailedException e) {
+                            return false;
+                        }
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -784,6 +870,129 @@
         }
 
         @Override
+        public void commit(@NonNull final OverlayManagerTransaction transaction)
+                throws RemoteException {
+            try {
+                traceBegin(TRACE_TAG_RRO, "OMS#commit " + transaction);
+                try {
+                    executeAllRequests(transaction);
+                } catch (Exception e) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        restoreSettings();
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                    Slog.d(TAG, "commit failed: " + e.getMessage(), e);
+                    throw new SecurityException("commit failed"
+                            + (DEBUG ? ": " + e.getMessage() : ""));
+                }
+            } finally {
+                traceEnd(TRACE_TAG_RRO);
+            }
+        }
+
+        private Optional<PackageAndUser> executeRequest(
+                @NonNull final OverlayManagerTransaction.Request request) throws Exception {
+            final int realUserId = handleIncomingUser(request.userId, request.typeToString());
+            enforceActor(request.packageName, request.typeToString(), realUserId);
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                switch (request.type) {
+                    case TYPE_SET_ENABLED:
+                        Optional<PackageAndUser> opt1 =
+                                mImpl.setEnabled(request.packageName, true, request.userId);
+                        Optional<PackageAndUser> opt2 =
+                                mImpl.setHighestPriority(request.packageName, request.userId);
+                        // Both setEnabled and setHighestPriority affected the same
+                        // target package and user: if both return non-empty
+                        // Optionals, they are identical
+                        return opt1.isPresent() ? opt1 : opt2;
+                    case TYPE_SET_DISABLED:
+                        return mImpl.setEnabled(request.packageName, false, request.userId);
+                    default:
+                        throw new IllegalArgumentException("unsupported request: " + request);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        private void executeAllRequests(@NonNull final OverlayManagerTransaction transaction)
+                throws Exception {
+            if (DEBUG) {
+                Slog.d(TAG, "commit " + transaction);
+            }
+            if (transaction == null) {
+                throw new IllegalArgumentException("null transaction");
+            }
+
+            // map: userId -> set<package-name>: target packages of overlays in
+            // this transaction
+            SparseArray<Set<String>> transactionTargets = new SparseArray<>();
+
+            // map: userId -> set<package-name>: packages that need to reload
+            // their resources due to changes to the overlays in this
+            // transaction
+            SparseArray<List<String>> affectedPackagesToUpdate = new SparseArray<>();
+
+            synchronized (mLock) {
+
+                // execute the requests (as calling user)
+                for (final OverlayManagerTransaction.Request request : transaction) {
+                    executeRequest(request).ifPresent(target -> {
+                        Set<String> userTargets = transactionTargets.get(target.userId);
+                        if (userTargets == null) {
+                            userTargets = new ArraySet<String>();
+                            transactionTargets.put(target.userId, userTargets);
+                        }
+                        userTargets.add(target.packageName);
+                    });
+                }
+
+                // past the point of no return: the entire transaction has been
+                // processed successfully, we can no longer fail: continue as
+                // system_server
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    persistSettings();
+
+                    // inform the package manager about the new paths
+                    for (int index = 0; index < transactionTargets.size(); index++) {
+                        final int userId = transactionTargets.keyAt(index);
+                        final List<String> affectedTargets =
+                                updatePackageManager(transactionTargets.valueAt(index), userId);
+                        affectedPackagesToUpdate.put(userId, affectedTargets);
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            } // synchronized (mLock)
+
+            FgThread.getHandler().post(() -> {
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    // schedule apps to refresh
+                    for (int index = 0; index < affectedPackagesToUpdate.size(); index++) {
+                        final int userId = affectedPackagesToUpdate.keyAt(index);
+                        updateActivityManager(affectedPackagesToUpdate.valueAt(index), userId);
+                    }
+
+                    // broadcast the ACTION_OVERLAY_CHANGED intents
+                    for (int index = 0; index < transactionTargets.size(); index++) {
+                        final int userId = transactionTargets.keyAt(index);
+                        for (String pkg: transactionTargets.valueAt(index)) {
+                            broadcastActionOverlayChanged(pkg, userId);
+                        }
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            });
+        }
+
+        @Override
         public void onShellCommand(@NonNull final FileDescriptor in,
                 @NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
                 @NonNull final String[] args, @NonNull final ShellCallback callback,
@@ -904,162 +1113,7 @@
         }
     };
 
-    private final class OverlayChangeListener
-            implements OverlayManagerServiceImpl.OverlayChangeListener {
-        @Override
-        public void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) {
-            schedulePersistSettings();
-            FgThread.getHandler().post(() -> {
-                updateAssets(userId, targetPackageName);
-
-                final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
-                        Uri.fromParts("package", targetPackageName, null));
-                intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-
-                if (DEBUG) {
-                    Slog.d(TAG, "send broadcast " + intent);
-                }
-
-                try {
-                    ActivityManager.getService().broadcastIntentWithFeature(null, null, intent,
-                            null, null, 0, null, null, null, android.app.AppOpsManager.OP_NONE,
-                            null, false, false, userId);
-                } catch (RemoteException e) {
-                    // Intentionally left empty.
-                }
-            });
-        }
-    }
-
-    /**
-     * Updates the target packages' set of enabled overlays in PackageManager.
-     */
-    private ArrayList<String> updateOverlayPaths(int userId, List<String> targetPackageNames) {
-        try {
-            traceBegin(TRACE_TAG_RRO, "OMS#updateOverlayPaths " + targetPackageNames);
-            if (DEBUG) {
-                Slog.d(TAG, "Updating overlay assets");
-            }
-            final PackageManagerInternal pm =
-                    LocalServices.getService(PackageManagerInternal.class);
-            final boolean updateFrameworkRes = targetPackageNames.contains("android");
-            if (updateFrameworkRes) {
-                targetPackageNames = pm.getTargetPackageNames(userId);
-            }
-
-            final Map<String, List<String>> pendingChanges =
-                    new ArrayMap<>(targetPackageNames.size());
-            synchronized (mLock) {
-                final List<String> frameworkOverlays =
-                        mImpl.getEnabledOverlayPackageNames("android", userId);
-                final int n = targetPackageNames.size();
-                for (int i = 0; i < n; i++) {
-                    final String targetPackageName = targetPackageNames.get(i);
-                    List<String> list = new ArrayList<>();
-                    if (!"android".equals(targetPackageName)) {
-                        list.addAll(frameworkOverlays);
-                    }
-                    list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
-                    pendingChanges.put(targetPackageName, list);
-                }
-            }
-
-            final HashSet<String> updatedPackages = new HashSet<>();
-            final int n = targetPackageNames.size();
-            for (int i = 0; i < n; i++) {
-                final String targetPackageName = targetPackageNames.get(i);
-                if (DEBUG) {
-                    Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
-                            + TextUtils.join(",", pendingChanges.get(targetPackageName))
-                            + "] userId=" + userId);
-                }
-
-                if (!pm.setEnabledOverlayPackages(
-                        userId, targetPackageName, pendingChanges.get(targetPackageName),
-                        updatedPackages)) {
-                    Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
-                            targetPackageName, userId));
-                }
-            }
-            return new ArrayList<>(updatedPackages);
-        } finally {
-            traceEnd(TRACE_TAG_RRO);
-        }
-    }
-
-    private void updateAssets(final int userId, final String targetPackageName) {
-        updateAssets(userId, Collections.singletonList(targetPackageName));
-    }
-
-    private void updateAssets(final int userId, List<String> targetPackageNames) {
-        final IActivityManager am = ActivityManager.getService();
-        try {
-            final ArrayList<String> updatedPaths = updateOverlayPaths(userId, targetPackageNames);
-            am.scheduleApplicationInfoChanged(updatedPaths, userId);
-        } catch (RemoteException e) {
-            // Intentionally left empty.
-        }
-    }
-
-    private void schedulePersistSettings() {
-        if (mPersistSettingsScheduled.getAndSet(true)) {
-            return;
-        }
-        IoThread.getHandler().post(() -> {
-            mPersistSettingsScheduled.set(false);
-            if (DEBUG) {
-                Slog.d(TAG, "Writing overlay settings");
-            }
-            synchronized (mLock) {
-                FileOutputStream stream = null;
-                try {
-                    stream = mSettingsFile.startWrite();
-                    mSettings.persist(stream);
-                    mSettingsFile.finishWrite(stream);
-                } catch (IOException | XmlPullParserException e) {
-                    mSettingsFile.failWrite(stream);
-                    Slog.e(TAG, "failed to persist overlay state", e);
-                }
-            }
-        });
-    }
-
-    private void restoreSettings() {
-        try {
-            traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings");
-            synchronized (mLock) {
-                if (!mSettingsFile.getBaseFile().exists()) {
-                    return;
-                }
-                try (FileInputStream stream = mSettingsFile.openRead()) {
-                    mSettings.restore(stream);
-
-                    // We might have data for dying users if the device was
-                    // restarted before we received USER_REMOVED. Remove data for
-                    // users that will not exist after the system is ready.
-
-                    final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/);
-                    final int[] liveUserIds = new int[liveUsers.size()];
-                    for (int i = 0; i < liveUsers.size(); i++) {
-                        liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier();
-                    }
-                    Arrays.sort(liveUserIds);
-
-                    for (int userId : mSettings.getUsers()) {
-                        if (Arrays.binarySearch(liveUserIds, userId) < 0) {
-                            mSettings.removeUser(userId);
-                        }
-                    }
-                } catch (IOException | XmlPullParserException e) {
-                    Slog.e(TAG, "failed to restore overlay state", e);
-                }
-            }
-        } finally {
-            traceEnd(TRACE_TAG_RRO);
-        }
-    }
-
-    private static final class PackageManagerHelperImpl implements PackageManagerHelper  {
+    private static final class PackageManagerHelperImpl implements PackageManagerHelper {
 
         private final Context mContext;
         private final IPackageManager mPackageManager;
@@ -1269,4 +1323,144 @@
             }
         }
     }
+
+    // Helper methods to update other parts of the system or read/write
+    // settings: these methods should never call into each other!
+
+    private void broadcastActionOverlayChanged(@NonNull final String targetPackageName,
+            final int userId) {
+        final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
+                Uri.fromParts("package", targetPackageName, null));
+        intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        try {
+            ActivityManager.getService().broadcastIntent(null, intent, null, null, 0, null, null,
+                    null, android.app.AppOpsManager.OP_NONE, null, false, false, userId);
+        } catch (RemoteException e) {
+            // Intentionally left empty.
+        }
+    }
+
+    /**
+     * Tell the activity manager to tell a set of packages to reload their
+     * resources.
+     */
+    private void updateActivityManager(List<String> targetPackageNames, final int userId) {
+        final IActivityManager am = ActivityManager.getService();
+        try {
+            am.scheduleApplicationInfoChanged(targetPackageNames, userId);
+        } catch (RemoteException e) {
+            // Intentionally left empty.
+        }
+    }
+
+    private ArrayList<String> updatePackageManager(String targetPackageNames, final int userId) {
+        return updatePackageManager(Collections.singletonList(targetPackageNames), userId);
+    }
+
+    /**
+     * Updates the target packages' set of enabled overlays in PackageManager.
+     * @return the package names of affected targets (a superset of
+     *         targetPackageNames: the target themserlves and shared libraries)
+     */
+    private ArrayList<String> updatePackageManager(@NonNull Collection<String> targetPackageNames,
+            final int userId) {
+        try {
+            traceBegin(TRACE_TAG_RRO, "OMS#updatePackageManager " + targetPackageNames);
+            if (DEBUG) {
+                Slog.d(TAG, "Update package manager about changed overlays");
+            }
+            final PackageManagerInternal pm =
+                    LocalServices.getService(PackageManagerInternal.class);
+            final boolean updateFrameworkRes = targetPackageNames.contains("android");
+            if (updateFrameworkRes) {
+                targetPackageNames = pm.getTargetPackageNames(userId);
+            }
+
+            final Map<String, List<String>> pendingChanges =
+                    new ArrayMap<>(targetPackageNames.size());
+            synchronized (mLock) {
+                final List<String> frameworkOverlays =
+                        mImpl.getEnabledOverlayPackageNames("android", userId);
+                for (final String targetPackageName : targetPackageNames) {
+                    List<String> list = new ArrayList<>();
+                    if (!"android".equals(targetPackageName)) {
+                        list.addAll(frameworkOverlays);
+                    }
+                    list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
+                    pendingChanges.put(targetPackageName, list);
+                }
+            }
+
+            final HashSet<String> updatedPackages = new HashSet<>();
+            for (final String targetPackageName : targetPackageNames) {
+                if (DEBUG) {
+                    Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
+                            + TextUtils.join(",", pendingChanges.get(targetPackageName))
+                            + "] userId=" + userId);
+                }
+
+                if (!pm.setEnabledOverlayPackages(
+                        userId, targetPackageName, pendingChanges.get(targetPackageName),
+                        updatedPackages)) {
+                    Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
+                            targetPackageName, userId));
+                }
+            }
+            return new ArrayList<>(updatedPackages);
+        } finally {
+            traceEnd(TRACE_TAG_RRO);
+        }
+    }
+
+    private void persistSettings() {
+        if (DEBUG) {
+            Slog.d(TAG, "Writing overlay settings");
+        }
+        synchronized (mLock) {
+            FileOutputStream stream = null;
+            try {
+                stream = mSettingsFile.startWrite();
+                mSettings.persist(stream);
+                mSettingsFile.finishWrite(stream);
+            } catch (IOException | XmlPullParserException e) {
+                mSettingsFile.failWrite(stream);
+                Slog.e(TAG, "failed to persist overlay state", e);
+            }
+        }
+    }
+
+    private void restoreSettings() {
+        try {
+            traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings");
+            synchronized (mLock) {
+                if (!mSettingsFile.getBaseFile().exists()) {
+                    return;
+                }
+                try (FileInputStream stream = mSettingsFile.openRead()) {
+                    mSettings.restore(stream);
+
+                    // We might have data for dying users if the device was
+                    // restarted before we received USER_REMOVED. Remove data for
+                    // users that will not exist after the system is ready.
+
+                    final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/);
+                    final int[] liveUserIds = new int[liveUsers.size()];
+                    for (int i = 0; i < liveUsers.size(); i++) {
+                        liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier();
+                    }
+                    Arrays.sort(liveUserIds);
+
+                    for (int userId : mSettings.getUsers()) {
+                        if (Arrays.binarySearch(liveUserIds, userId) < 0) {
+                            mSettings.removeUser(userId);
+                        }
+                    }
+                } catch (IOException | XmlPullParserException e) {
+                    Slog.e(TAG, "failed to restore overlay state", e);
+                }
+            }
+        } finally {
+            traceEnd(TRACE_TAG_RRO);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 05a4a38..e60411bb 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -45,6 +45,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 
 /**
@@ -71,7 +72,6 @@
     private final OverlayManagerSettings mSettings;
     private final OverlayConfig mOverlayConfig;
     private final String[] mDefaultOverlays;
-    private final OverlayChangeListener mListener;
 
     /**
      * Helper method to merge the overlay manager's (as read from overlays.xml)
@@ -114,14 +114,12 @@
             @NonNull final IdmapManager idmapManager,
             @NonNull final OverlayManagerSettings settings,
             @NonNull final OverlayConfig overlayConfig,
-            @NonNull final String[] defaultOverlays,
-            @NonNull final OverlayChangeListener listener) {
+            @NonNull final String[] defaultOverlays) {
         mPackageManager = packageManager;
         mIdmapManager = idmapManager;
         mSettings = settings;
         mOverlayConfig = overlayConfig;
         mDefaultOverlays = defaultOverlays;
-        mListener = listener;
     }
 
     /**
@@ -259,52 +257,58 @@
         mSettings.removeUser(userId);
     }
 
-    void onTargetPackageAdded(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onTargetPackageAdded(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId);
         }
 
-        updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+        return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
     }
 
-    void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onTargetPackageChanged(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId);
         }
 
-        updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+        return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
     }
 
-    void onTargetPackageReplacing(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onTargetPackageReplacing(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "onTargetPackageReplacing packageName=" + packageName + " userId="
                     + userId);
         }
 
-        updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+        return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
     }
 
-    void onTargetPackageReplaced(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onTargetPackageReplaced(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "onTargetPackageReplaced packageName=" + packageName + " userId=" + userId);
         }
 
-        updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+        return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
     }
 
-    void onTargetPackageRemoved(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onTargetPackageRemoved(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
         }
 
-        updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+        return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
     }
 
     /**
      * Update the state of any overlays for this target.
      */
-    private void updateAndRefreshOverlaysForTarget(@NonNull final String targetPackageName,
-            final int userId, final int flags) {
+    private Optional<PackageAndUser> updateAndRefreshOverlaysForTarget(
+            @NonNull final String targetPackageName, final int userId, final int flags)
+            throws OperationFailedException {
         final List<OverlayInfo> targetOverlays = mSettings.getOverlaysForTarget(targetPackageName,
                 userId);
 
@@ -364,11 +368,13 @@
         }
 
         if (modified) {
-            mListener.onOverlaysChanged(targetPackageName, userId);
+            return Optional.of(new PackageAndUser(targetPackageName, userId));
         }
+        return Optional.empty();
     }
 
-    void onOverlayPackageAdded(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onOverlayPackageAdded(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId);
         }
@@ -376,8 +382,7 @@
         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
         if (overlayPackage == null) {
             Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found");
-            onOverlayPackageRemoved(packageName, userId);
-            return;
+            return onOverlayPackageRemoved(packageName, userId);
         }
 
         mSettings.init(packageName, userId, overlayPackage.overlayTarget,
@@ -389,15 +394,17 @@
                 overlayPackage.overlayCategory);
         try {
             if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) {
-                mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+                return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
             }
+            return Optional.empty();
         } catch (OverlayManagerSettings.BadKeyException e) {
-            Slog.e(TAG, "failed to update settings", e);
             mSettings.remove(packageName, userId);
+            throw new OperationFailedException("failed to update settings", e);
         }
     }
 
-    void onOverlayPackageChanged(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onOverlayPackageChanged(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "onOverlayPackageChanged packageName=" + packageName + " userId=" + userId);
         }
@@ -405,14 +412,16 @@
         try {
             final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
             if (updateState(oi.targetPackageName, packageName, userId, 0)) {
-                mListener.onOverlaysChanged(oi.targetPackageName, userId);
+                return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
             }
+            return Optional.empty();
         } catch (OverlayManagerSettings.BadKeyException e) {
-            Slog.e(TAG, "failed to update settings", e);
+            throw new OperationFailedException("failed to update settings", e);
         }
     }
 
-    void onOverlayPackageReplacing(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onOverlayPackageReplacing(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "onOverlayPackageReplacing packageName=" + packageName + " userId="
                     + userId);
@@ -423,14 +432,16 @@
             if (updateState(oi.targetPackageName, packageName, userId,
                         FLAG_OVERLAY_IS_BEING_REPLACED)) {
                 removeIdmapIfPossible(oi);
-                mListener.onOverlaysChanged(oi.targetPackageName, userId);
+                return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
             }
+            return Optional.empty();
         } catch (OverlayManagerSettings.BadKeyException e) {
-            Slog.e(TAG, "failed to update settings", e);
+            throw new OperationFailedException("failed to update settings", e);
         }
     }
 
-    void onOverlayPackageReplaced(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onOverlayPackageReplaced(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "onOverlayPackageReplaced packageName=" + packageName + " userId="
                     + userId);
@@ -439,16 +450,12 @@
         final PackageInfo pkg = mPackageManager.getPackageInfo(packageName, userId);
         if (pkg == null) {
             Slog.w(TAG, "overlay package " + packageName + " was replaced, but couldn't be found");
-            onOverlayPackageRemoved(packageName, userId);
-            return;
+            return onOverlayPackageRemoved(packageName, userId);
         }
 
         try {
             final OverlayInfo oldOi = mSettings.getOverlayInfo(packageName, userId);
             if (mustReinitializeOverlay(pkg, oldOi)) {
-                if (oldOi != null && !oldOi.targetPackageName.equals(pkg.overlayTarget)) {
-                    mListener.onOverlaysChanged(pkg.overlayTarget, userId);
-                }
                 mSettings.init(packageName, userId, pkg.overlayTarget, pkg.targetOverlayableName,
                         pkg.applicationInfo.getBaseCodePath(),
                         isPackageConfiguredMutable(pkg.packageName),
@@ -457,22 +464,25 @@
             }
 
             if (updateState(pkg.overlayTarget, packageName, userId, 0)) {
-                mListener.onOverlaysChanged(pkg.overlayTarget, userId);
+                return Optional.of(new PackageAndUser(pkg.overlayTarget, userId));
             }
+            return Optional.empty();
         } catch (OverlayManagerSettings.BadKeyException e) {
-            Slog.e(TAG, "failed to update settings", e);
+            throw new OperationFailedException("failed to update settings", e);
         }
     }
 
-    void onOverlayPackageRemoved(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onOverlayPackageRemoved(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         try {
             final OverlayInfo overlayInfo = mSettings.getOverlayInfo(packageName, userId);
             if (mSettings.remove(packageName, userId)) {
                 removeIdmapIfPossible(overlayInfo);
-                mListener.onOverlaysChanged(overlayInfo.targetPackageName, userId);
+                return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId));
             }
+            return Optional.empty();
         } catch (OverlayManagerSettings.BadKeyException e) {
-            Slog.e(TAG, "failed to remove overlay", e);
+            throw new OperationFailedException("failed to remove overlay", e);
         }
     }
 
@@ -493,8 +503,8 @@
         return mSettings.getOverlaysForUser(userId);
     }
 
-    boolean setEnabled(@NonNull final String packageName, final boolean enable,
-            final int userId) {
+    Optional<PackageAndUser> setEnabled(@NonNull final String packageName, final boolean enable,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d",
                         packageName, enable, userId));
@@ -502,30 +512,33 @@
 
         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
         if (overlayPackage == null) {
-            return false;
+            throw new OperationFailedException(
+                    String.format("failed to find overlay package %s for user %d",
+                        packageName, userId));
         }
 
         try {
             final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
             if (!oi.isMutable) {
                 // Ignore immutable overlays.
-                return false;
+                throw new OperationFailedException(
+                        "cannot enable immutable overlay packages in runtime");
             }
 
             boolean modified = mSettings.setEnabled(packageName, userId, enable);
             modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0);
 
             if (modified) {
-                mListener.onOverlaysChanged(oi.targetPackageName, userId);
+                return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
             }
-            return true;
+            return Optional.empty();
         } catch (OverlayManagerSettings.BadKeyException e) {
-            return false;
+            throw new OperationFailedException("failed to update settings", e);
         }
     }
 
-    boolean setEnabledExclusive(@NonNull final String packageName, boolean withinCategory,
-            final int userId) {
+    Optional<PackageAndUser> setEnabledExclusive(@NonNull final String packageName,
+            boolean withinCategory, final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, String.format("setEnabledExclusive packageName=%s"
                     + " withinCategory=%s userId=%d", packageName, withinCategory, userId));
@@ -533,7 +546,8 @@
 
         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
         if (overlayPackage == null) {
-            return false;
+            throw new OperationFailedException(String.format(
+                        "failed to find overlay package %s for user %d", packageName, userId));
         }
 
         try {
@@ -576,11 +590,11 @@
             modified |= updateState(targetPackageName, packageName, userId, 0);
 
             if (modified) {
-                mListener.onOverlaysChanged(targetPackageName, userId);
+                return Optional.of(new PackageAndUser(targetPackageName, userId));
             }
-            return true;
+            return Optional.empty();
         } catch (OverlayManagerSettings.BadKeyException e) {
-            return false;
+            throw new OperationFailedException("failed to update settings", e);
         }
     }
 
@@ -596,66 +610,75 @@
         return mOverlayConfig.isEnabled(packageName);
     }
 
-    boolean setPriority(@NonNull final String packageName,
-            @NonNull final String newParentPackageName, final int userId) {
+    Optional<PackageAndUser> setPriority(@NonNull final String packageName,
+            @NonNull final String newParentPackageName, final int userId)
+            throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "setPriority packageName=" + packageName + " newParentPackageName="
                     + newParentPackageName + " userId=" + userId);
         }
 
         if (!isPackageConfiguredMutable(packageName)) {
-            return false;
+            throw new OperationFailedException(String.format(
+                        "overlay package %s user %d is not updatable", packageName, userId));
         }
 
         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
         if (overlayPackage == null) {
-            return false;
+            throw new OperationFailedException(String.format(
+                        "failed to find overlay package %s for user %d", packageName, userId));
         }
 
         if (mSettings.setPriority(packageName, newParentPackageName, userId)) {
-            mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+            return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
         }
-        return true;
+        return Optional.empty();
     }
 
-    boolean setHighestPriority(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> setHighestPriority(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId);
         }
 
         if (!isPackageConfiguredMutable(packageName)) {
-            return false;
+            throw new OperationFailedException(String.format(
+                        "overlay package %s user %d is not updatable", packageName, userId));
         }
 
         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
         if (overlayPackage == null) {
-            return false;
+            throw new OperationFailedException(String.format(
+                        "failed to find overlay package %s for user %d", packageName, userId));
         }
 
         if (mSettings.setHighestPriority(packageName, userId)) {
-            mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+            return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
         }
-        return true;
+        return Optional.empty();
     }
 
-    boolean setLowestPriority(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> setLowestPriority(@NonNull final String packageName, final int userId)
+            throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId);
         }
 
         if (!isPackageConfiguredMutable(packageName)) {
-            return false;
+            throw new OperationFailedException(String.format(
+                        "overlay package %s user %d is not updatable", packageName, userId));
         }
 
         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
         if (overlayPackage == null) {
-            return false;
+            throw new OperationFailedException(String.format(
+                        "failed to find overlay package %s for user %d", packageName, userId));
         }
 
         if (mSettings.setLowestPriority(packageName, userId)) {
-            mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+            return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
         }
-        return true;
+        return Optional.empty();
     }
 
     void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) {
@@ -797,12 +820,13 @@
         mIdmapManager.removeIdmap(oi, oi.userId);
     }
 
-    interface OverlayChangeListener {
+    static final class OperationFailedException extends Exception {
+        OperationFailedException(@NonNull final String message) {
+            super(message);
+        }
 
-        /**
-         * An event triggered by changes made to overlay state or settings as well as changes that
-         * add or remove target packages of overlays.
-         **/
-        void onOverlaysChanged(@NonNull String targetPackage, int userId);
+        OperationFailedException(@NonNull final String message, @NonNull Throwable cause) {
+            super(message, cause);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/om/PackageAndUser.java b/services/core/java/com/android/server/om/PackageAndUser.java
new file mode 100644
index 0000000..5c38ba7
--- /dev/null
+++ b/services/core/java/com/android/server/om/PackageAndUser.java
@@ -0,0 +1,57 @@
+/*
+ * 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.server.om;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+
+final class PackageAndUser {
+    public final @NonNull String packageName;
+    public final @UserIdInt int userId;
+
+    PackageAndUser(@NonNull String packageName, @UserIdInt int userId) {
+        this.packageName = packageName;
+        this.userId = userId;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof PackageAndUser)) {
+            return false;
+        }
+        PackageAndUser other = (PackageAndUser) obj;
+        return packageName.equals(other.packageName) && userId == other.userId;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + packageName.hashCode();
+        result = prime * result + userId;
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("PackageAndUser{packageName=%s, userId=%d}", packageName, userId);
+    }
+}
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index ef0f0ee..4ff75fa 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -336,6 +336,13 @@
 
         @Override
         public void binderDied() {
+            try {
+                // Allow a small amount of time for any error or finished callbacks to be made.
+                // This ensures that the listener does not receive an erroneous runtime error
+                // callback.
+                Thread.sleep(1000);
+            } catch (InterruptedException ignored) {
+            }
             synchronized (mLock) {
                 if (!mDone) {
                     // If we have not gotten a "done" callback this must be a crash.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e7a04ef..bdff19b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -21423,7 +21423,7 @@
                 mPermissionManager.onPackageInstalled(pkg,
                         PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT, userId);
                 if (applyUserRestrictions) {
-                    mSettings.writeRuntimePermissionsForUserLPr(userId, false);
+                    mSettings.writePermissionStateForUserLPr(userId, false);
                 }
             }
 
@@ -27429,7 +27429,7 @@
         public void writePermissionSettings(int[] userIds, boolean async) {
             synchronized (mLock) {
                 for (int userId : userIds) {
-                    mSettings.writeRuntimePermissionsForUserLPr(userId, !async);
+                    mSettings.writePermissionStateForUserLPr(userId, !async);
                 }
             }
         }
@@ -28069,13 +28069,12 @@
 
     /**
      * Temporary method that wraps mSettings.writeLPr() and calls mPermissionManager's
-     * writeLegacyPermissionsTEMP() and writeLegacyPermissionStateTEMP() beforehand.
+     * writeLegacyPermissionsTEMP() beforehand.
      *
      * TODO(zhanghai): This should be removed once we finish migration of permission storage.
      */
     private void writeSettingsLPrTEMP() {
         mPermissionManager.writeLegacyPermissionsTEMP(mSettings.mPermissions);
-        mPermissionManager.writeLegacyPermissionStateTEMP();
         mSettings.writeLPr();
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 9eae117..446342a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2732,7 +2732,7 @@
     private int removeUserOrSetEphemeral(IUserManager um, @UserIdInt int userId)
             throws RemoteException {
         Slog.i(TAG, "Removing " + userId + " or set as ephemeral if in use.");
-        int result = um.removeUserOrSetEphemeral(userId);
+        int result = um.removeUserOrSetEphemeral(userId, /* evenWhenDisallowed= */ false);
         switch (result) {
             case UserManager.REMOVE_RESULT_REMOVED:
                 getOutPrintWriter().printf("Success: user %d removed\n", userId);
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 83f6c52..ade087b 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -356,7 +356,7 @@
             proto.write(PackageProto.UserPermissionsProto.ID, user.id);
 
             runtimePermissionStates = dataProvider.getLegacyPermissionState(appId)
-                    .getRuntimePermissionStates(user.id);
+                    .getPermissionStates(user.id);
             for (LegacyPermissionState.PermissionState permission : runtimePermissionStates) {
                 if (permission.isGranted()) {
                     proto.write(PackageProto.UserPermissionsProto.GRANTED_PERMISSIONS,
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 50c1065..3416147 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2313,7 +2313,8 @@
     }
 
     void readInstallPermissionsLPr(TypedXmlPullParser parser,
-            LegacyPermissionState permissionsState) throws IOException, XmlPullParserException {
+            LegacyPermissionState permissionsState, List<UserInfo> users)
+            throws IOException, XmlPullParserException {
         int outerDepth = parser.getDepth();
         int type;
         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -2328,8 +2329,10 @@
                 String name = parser.getAttributeValue(null, ATTR_NAME);
                 final boolean granted = parser.getAttributeBoolean(null, ATTR_GRANTED, true);
                 final int flags = parser.getAttributeIntHex(null, ATTR_FLAGS, 0);
-                permissionsState.putInstallPermissionState(new PermissionState(name, granted,
-                        flags));
+                for (final UserInfo user : users) {
+                    permissionsState.putPermissionState(new PermissionState(name, false, granted,
+                            flags), user.id);
+                }
             } else {
                 Slog.w(PackageManagerService.TAG, "Unknown element under <permissions>: "
                         + parser.getName());
@@ -2338,25 +2341,6 @@
         }
     }
 
-    void writePermissionsLPr(TypedXmlSerializer serializer, Collection<PermissionState> permissionStates)
-            throws IOException {
-        if (permissionStates.isEmpty()) {
-            return;
-        }
-
-        serializer.startTag(null, TAG_PERMISSIONS);
-
-        for (PermissionState permissionState : permissionStates) {
-            serializer.startTag(null, TAG_ITEM);
-            serializer.attributeInterned(null, ATTR_NAME, permissionState.getName());
-            serializer.attributeBoolean(null, ATTR_GRANTED, permissionState.isGranted());
-            serializer.attributeIntHex(null, ATTR_FLAGS, permissionState.getFlags());
-            serializer.endTag(null, TAG_ITEM);
-        }
-
-        serializer.endTag(null, TAG_PERMISSIONS);
-    }
-
     void readUsesStaticLibLPw(TypedXmlPullParser parser, PackageSetting outPs)
             throws IOException, XmlPullParserException {
         String libName = parser.getAttributeValue(null, ATTR_NAME);
@@ -2572,8 +2556,6 @@
                 serializer.attribute(null, ATTR_NAME, usr.name);
                 serializer.attributeInt(null, "userId", usr.userId);
                 usr.signatures.writeXml(serializer, "sigs", mPastSignatures);
-                writePermissionsLPr(serializer, usr.getLegacyPermissionState()
-                        .getInstallPermissionStates());
                 serializer.endTag(null, "shared-user");
             }
 
@@ -2898,12 +2880,6 @@
 
         writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
 
-        // If this is a shared user, the permissions will be written there.
-        if (pkg.sharedUser == null) {
-            writePermissionsLPr(serializer, pkg.getLegacyPermissionState()
-                    .getInstallPermissionStates());
-        }
-
         serializer.endTag(null, "updated-package");
     }
 
@@ -2991,9 +2967,6 @@
                     serializer, "install-initiator-sigs", mPastSignatures);
         }
 
-        writePermissionsLPr(serializer,
-                pkg.getLegacyPermissionState().getInstallPermissionStates());
-
         writeSigningKeySetLPr(serializer, pkg.keySetData);
         writeUpgradeKeySetsLPr(serializer, pkg.keySetData);
         writeKeySetAliasesLPr(serializer, pkg.keySetData);
@@ -3097,13 +3070,13 @@
 
                 String tagName = parser.getName();
                 if (tagName.equals("package")) {
-                    readPackageLPw(parser);
+                    readPackageLPw(parser, users);
                 } else if (tagName.equals("permissions")) {
                     mPermissions.readPermissions(parser);
                 } else if (tagName.equals("permission-trees")) {
                     mPermissions.readPermissionTrees(parser);
                 } else if (tagName.equals("shared-user")) {
-                    readSharedUserLPw(parser);
+                    readSharedUserLPw(parser, users);
                 } else if (tagName.equals("preferred-packages")) {
                     // no longer used.
                 } else if (tagName.equals("preferred-activities")) {
@@ -3121,7 +3094,7 @@
                 } else if (tagName.equals(TAG_DEFAULT_BROWSER)) {
                     readDefaultAppsLPw(parser, 0);
                 } else if (tagName.equals("updated-package")) {
-                    readDisabledSysPackageLPw(parser);
+                    readDisabledSysPackageLPw(parser, users);
                 } else if (tagName.equals("renamed-package")) {
                     String nname = parser.getAttributeValue(null, "new");
                     String oname = parser.getAttributeValue(null, "old");
@@ -3627,7 +3600,7 @@
         }
     }
 
-    private void readDisabledSysPackageLPw(TypedXmlPullParser parser)
+    private void readDisabledSysPackageLPw(TypedXmlPullParser parser, List<UserInfo> users)
             throws XmlPullParserException, IOException {
         String name = parser.getAttributeValue(null, ATTR_NAME);
         String realName = parser.getAttributeValue(null, "realName");
@@ -3676,7 +3649,7 @@
             }
 
             if (parser.getName().equals(TAG_PERMISSIONS)) {
-                readInstallPermissionsLPr(parser, ps.getLegacyPermissionState());
+                readInstallPermissionsLPr(parser, ps.getLegacyPermissionState(), users);
             } else if (parser.getName().equals(TAG_USES_STATIC_LIB)) {
                 readUsesStaticLibLPw(parser, ps);
             } else {
@@ -3693,7 +3666,7 @@
     private static int PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE = 1<<28;
     private static int PRE_M_APP_INFO_FLAG_PRIVILEGED = 1<<30;
 
-    private void readPackageLPw(TypedXmlPullParser parser)
+    private void readPackageLPw(TypedXmlPullParser parser, List<UserInfo> users)
             throws XmlPullParserException, IOException {
         String name = null;
         String realName = null;
@@ -3935,7 +3908,7 @@
                     packageSetting.signatures.readXml(parser, mPastSignatures);
                 } else if (tagName.equals(TAG_PERMISSIONS)) {
                     readInstallPermissionsLPr(parser,
-                            packageSetting.getLegacyPermissionState());
+                            packageSetting.getLegacyPermissionState(), users);
                     packageSetting.installPermissionsFixed = true;
                 } else if (tagName.equals("proper-signing-keyset")) {
                     long id = parser.getAttributeLong(null, "identifier");
@@ -4112,7 +4085,7 @@
         }
     }
 
-    private void readSharedUserLPw(TypedXmlPullParser parser)
+    private void readSharedUserLPw(TypedXmlPullParser parser, List<UserInfo> users)
             throws XmlPullParserException, IOException {
         String name = null;
         int pkgFlags = 0;
@@ -4156,7 +4129,7 @@
                 if (tagName.equals("sigs")) {
                     su.signatures.readXml(parser, mPastSignatures);
                 } else if (tagName.equals("perms")) {
-                    readInstallPermissionsLPr(parser, su.getLegacyPermissionState());
+                    readInstallPermissionsLPr(parser, su.getLegacyPermissionState(), users);
                 } else {
                     PackageManagerService.reportSettingsProblem(Log.WARN,
                             "Unknown element under <shared-user>: " + parser.getName());
@@ -4979,7 +4952,7 @@
                 dumpGidsLPr(pw, prefix + "    ", mPermissionDataProvider.getGidsForUid(
                         UserHandle.getUid(user.id, ps.appId)));
                 dumpRuntimePermissionsLPr(pw, prefix + "    ", permissionNames, permissionsState
-                        .getRuntimePermissionStates(user.id), dumpAll);
+                        .getPermissionStates(user.id), dumpAll);
             }
 
             String harmfulAppWarning = ps.getHarmfulAppWarning(user.id);
@@ -5154,7 +5127,7 @@
                     final int[] gids = mPermissionDataProvider.getGidsForUid(UserHandle.getUid(
                             userId, su.userId));
                     final Collection<PermissionState> permissions =
-                            permissionsState.getRuntimePermissionStates(userId);
+                            permissionsState.getPermissionStates(userId);
                     if (!ArrayUtils.isEmpty(gids) || !permissions.isEmpty()) {
                         pw.print(prefix); pw.print("User "); pw.print(userId); pw.println(": ");
                         dumpGidsLPr(pw, prefix + "  ", gids);
@@ -5215,9 +5188,19 @@
 
     void dumpRuntimePermissionsLPr(PrintWriter pw, String prefix, ArraySet<String> permissionNames,
             Collection<PermissionState> permissionStates, boolean dumpAll) {
-        if (!permissionStates.isEmpty() || dumpAll) {
+        boolean hasRuntimePermissions = false;
+        for (PermissionState permissionState : permissionStates) {
+            if (permissionState.isRuntime()) {
+                hasRuntimePermissions = true;
+                break;
+            }
+        }
+        if (hasRuntimePermissions || dumpAll) {
             pw.print(prefix); pw.println("runtime permissions:");
             for (PermissionState permissionState : permissionStates) {
+                if (!permissionState.isRuntime()) {
+                    continue;
+                }
                 if (permissionNames != null
                         && !permissionNames.contains(permissionState.getName())) {
                     continue;
@@ -5256,11 +5239,21 @@
 
     void dumpInstallPermissionsLPr(PrintWriter pw, String prefix, ArraySet<String> permissionNames,
             LegacyPermissionState permissionsState) {
-        Collection<PermissionState> permissionStates =
-                permissionsState.getInstallPermissionStates();
-        if (!permissionStates.isEmpty()) {
+        Collection<PermissionState> permissionStates = permissionsState.getPermissionStates(
+                UserHandle.USER_SYSTEM);
+        boolean hasInstallPermissions = false;
+        for (PermissionState permissionState : permissionStates) {
+            if (!permissionState.isRuntime()) {
+                hasInstallPermissions = true;
+                break;
+            }
+        }
+        if (hasInstallPermissions) {
             pw.print(prefix); pw.println("install permissions:");
             for (PermissionState permissionState : permissionStates) {
+                if (permissionState.isRuntime()) {
+                    continue;
+                }
                 if (permissionNames != null
                         && !permissionNames.contains(permissionState.getName())) {
                     continue;
@@ -5295,7 +5288,7 @@
         }
     }
 
-    public void writeRuntimePermissionsForUserLPr(int userId, boolean sync) {
+    public void writePermissionStateForUserLPr(int userId, boolean sync) {
         if (sync) {
             mRuntimePermissionsPersistence.writeStateForUserSyncLPr(userId);
         } else {
@@ -5530,7 +5523,7 @@
         private List<RuntimePermissionsState.PermissionState> getPermissionsFromPermissionsState(
                 @NonNull LegacyPermissionState permissionsState, @UserIdInt int userId) {
             Collection<PermissionState> permissionStates =
-                    permissionsState.getRuntimePermissionStates(userId);
+                    permissionsState.getPermissionStates(userId);
             List<RuntimePermissionsState.PermissionState> permissions = new ArrayList<>();
             for (PermissionState permissionState : permissionStates) {
                 RuntimePermissionsState.PermissionState permission =
@@ -5590,6 +5583,7 @@
                 if (permissions != null) {
                     readPermissionsStateLpr(permissions, packageSetting.getLegacyPermissionState(),
                             userId);
+                    packageSetting.installPermissionsFixed = true;
                 } else if (packageSetting.sharedUser == null && !isUpgradeToR) {
                     Slog.w(TAG, "Missing permission state for package: " + packageName);
                     packageSetting.getLegacyPermissionState().setMissing(true, userId);
@@ -5624,7 +5618,7 @@
                 String name = permission.getName();
                 boolean granted = permission.isGranted();
                 int flags = permission.getFlags();
-                permissionsState.putRuntimePermissionState(new PermissionState(name, granted,
+                permissionsState.putPermissionState(new PermissionState(name, true, granted,
                         flags), userId);
             }
         }
@@ -5646,7 +5640,7 @@
 
             try {
                 final TypedXmlPullParser parser = Xml.resolvePullParser(in);
-                parseRuntimePermissionsLPr(parser, userId);
+                parseLegacyRuntimePermissionsLPr(parser, userId);
 
             } catch (XmlPullParserException | IOException e) {
                 throw new IllegalStateException("Failed parsing permissions file: "
@@ -5659,7 +5653,7 @@
         // Private internals
 
         @GuardedBy("Settings.this.mLock")
-        private void parseRuntimePermissionsLPr(TypedXmlPullParser parser, int userId)
+        private void parseLegacyRuntimePermissionsLPr(TypedXmlPullParser parser, int userId)
                 throws IOException, XmlPullParserException {
             final int outerDepth = parser.getDepth();
             int type;
@@ -5687,7 +5681,7 @@
                             XmlUtils.skipCurrentTag(parser);
                             continue;
                         }
-                        parsePermissionsLPr(parser, ps.getLegacyPermissionState(), userId);
+                        parseLegacyPermissionsLPr(parser, ps.getLegacyPermissionState(), userId);
                     } break;
 
                     case TAG_SHARED_USER: {
@@ -5698,13 +5692,13 @@
                             XmlUtils.skipCurrentTag(parser);
                             continue;
                         }
-                        parsePermissionsLPr(parser, sus.getLegacyPermissionState(), userId);
+                        parseLegacyPermissionsLPr(parser, sus.getLegacyPermissionState(), userId);
                     } break;
                 }
             }
         }
 
-        private void parsePermissionsLPr(TypedXmlPullParser parser,
+        private void parseLegacyPermissionsLPr(TypedXmlPullParser parser,
                 LegacyPermissionState permissionsState, int userId)
                 throws IOException, XmlPullParserException {
             final int outerDepth = parser.getDepth();
@@ -5722,7 +5716,7 @@
                                 parser.getAttributeBoolean(null, ATTR_GRANTED, true);
                         final int flags =
                                 parser.getAttributeIntHex(null, ATTR_FLAGS, 0);
-                        permissionsState.putRuntimePermissionState(new PermissionState(name,
+                        permissionsState.putPermissionState(new PermissionState(name, true,
                                 granted, flags), userId);
                     }
                     break;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 19a94b3..314510b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3843,7 +3843,6 @@
      */
     @Override
     public boolean removeUser(@UserIdInt int userId) {
-        Slog.i(LOG_TAG, "removeUser u" + userId, new Exception());
         checkManageOrCreateUsersPermission("Only the system can remove users");
 
         final String restriction = getUserRemovalRestriction(userId);
@@ -3968,13 +3967,16 @@
     }
 
     @Override
-    public @UserManager.RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId) {
-        Slog.i(LOG_TAG, "removeUserOrSetEphemeral u" + userId);
+    public @UserManager.RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId,
+            boolean evenWhenDisallowed) {
         checkManageOrCreateUsersPermission("Only the system can remove users");
-        final String restriction = getUserRemovalRestriction(userId);
-        if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) {
-            Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
-            return UserManager.REMOVE_RESULT_ERROR;
+
+        if (!evenWhenDisallowed) {
+            final String restriction = getUserRemovalRestriction(userId);
+            if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) {
+                Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
+                return UserManager.REMOVE_RESULT_ERROR;
+            }
         }
         if (userId == UserHandle.USER_SYSTEM) {
             Slog.e(LOG_TAG, "System user cannot be removed.");
@@ -4003,7 +4005,7 @@
                 final int currentUser = ActivityManager.getCurrentUser();
                 if (currentUser != userId) {
                     // Attempt to remove the user. This will fail if the user is the current user
-                    if (removeUser(userId)) {
+                    if (removeUserUnchecked(userId)) {
                         return UserManager.REMOVE_RESULT_REMOVED;
                     }
                 }
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index 252ba60..6e6d7c3 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -327,17 +327,17 @@
         final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
 
         // Check whether all allowlisted packages are indeed on the system.
-        final String notPresentFmt = "%s is whitelisted but not present.";
-        final String notSystemFmt = "%s is whitelisted and present but not a system package.";
-        final String overlayPackageFmt = "%s is whitelisted but it's auto-generated RRO package.";
+        final String notPresentFmt = "%s is allowlisted but not present.";
+        final String notSystemFmt = "%s is allowlisted and present but not a system package.";
+        final String overlayFmt = "%s is allowlisted unnecessarily since it's a static overlay.";
         for (String pkgName : allWhitelistedPackages) {
             final AndroidPackage pkg = pmInt.getPackage(pkgName);
             if (pkg == null) {
                 warnings.add(String.format(notPresentFmt, pkgName));
             } else if (!pkg.isSystem()) {
                 warnings.add(String.format(notSystemFmt, pkgName));
-            } else if (isAutoGeneratedRRO(pkg)) {
-                warnings.add(String.format(overlayPackageFmt, pkgName));
+            } else if (shouldUseOverlayTargetName(pkg)) {
+                warnings.add(String.format(overlayFmt, pkgName));
             }
         }
         return warnings;
@@ -363,7 +363,7 @@
             if (!pkg.isSystem()) return;
             final String pkgName = pkg.getManifestPackageName();
             if (!allWhitelistedPackages.contains(pkgName)
-                    && !isAutoGeneratedRRO(pmInt.getPackage(pkgName))) {
+                    && !shouldUseOverlayTargetName(pmInt.getPackage(pkgName))) {
                 errors.add(String.format(logMessageFmt, pkgName));
             }
         });
@@ -413,22 +413,13 @@
     }
 
     /**
-     * Whether package name has auto-generated RRO package name suffix.
+     * Returns whether the package is a static overlay, whose installation should depend on the
+     * allowlisting of the overlay's target's package name, rather than of its own package name.
+     *
+     * @param pkg A package (which need not be an overlay)
      */
-    @VisibleForTesting
-    static boolean hasAutoGeneratedRROSuffix(String name) {
-        return name.endsWith(".auto_generated_rro_product__")
-                // TODO(b/172956245): temporary workaround until OEMs can customize name
-                || name.endsWith("carui.rro") || name.endsWith("carui.overlayable.rro")
-                || name.endsWith(".auto_generated_rro_vendor__");
-    }
-
-    /**
-     * Whether the package is auto-generated RRO package.
-     */
-    private static boolean isAutoGeneratedRRO(AndroidPackage pkg) {
-        return pkg.isOverlay()
-                && (hasAutoGeneratedRROSuffix(pkg.getManifestPackageName()));
+    private static boolean shouldUseOverlayTargetName(AndroidPackage pkg) {
+        return pkg.isOverlayIsStatic();
     }
 
     /** See {@link #isEnforceMode()}. */
@@ -542,18 +533,8 @@
     static boolean shouldInstallPackage(AndroidPackage sysPkg,
             @NonNull ArrayMap<String, Long> userTypeWhitelist,
             @NonNull Set<String> userWhitelist, boolean implicitlyWhitelist) {
-        final String pkgName;
-        if (isAutoGeneratedRRO(sysPkg)) {
-            pkgName = sysPkg.getOverlayTarget();
-            if (DEBUG) {
-                Slog.i(TAG, "shouldInstallPackage(): " + sysPkg.getManifestPackageName()
-                        + " is auto-generated RRO package, will look for overlay system package: "
-                        + pkgName);
-            }
-        } else {
-            pkgName = sysPkg.getManifestPackageName();
-        }
-
+        final String pkgName = shouldUseOverlayTargetName(sysPkg) ?
+                sysPkg.getOverlayTarget() : sysPkg.getManifestPackageName();
         return (implicitlyWhitelist && !userTypeWhitelist.containsKey(pkgName))
                 || userWhitelist.contains(pkgName);
     }
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java
index 72d7628..92f22a4 100644
--- a/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
-import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -105,28 +104,14 @@
     }
 
     /**
-     * Put a install permission state.
-     *
-     * @param permissionState the permission state
-     */
-    public void putInstallPermissionState(@NonNull PermissionState permissionState) {
-        putPermissionState(permissionState, UserHandle.USER_ALL);
-    }
-
-    /**
-     * Put a runtime permission state for a user.
+     * Put a permission state for a user.
      *
      * @param permissionState the permission state
      * @param userId the user ID
      */
-    public void putRuntimePermissionState(@NonNull PermissionState permissionState,
+    public void putPermissionState(@NonNull PermissionState permissionState,
             @UserIdInt int userId) {
         checkUserId(userId);
-        putPermissionState(permissionState, userId);
-    }
-
-    private void putPermissionState(@NonNull PermissionState permissionState,
-            @UserIdInt int userId) {
         UserState userState = mUserStates.get(userId);
         if (userState == null) {
             userState = new UserState();
@@ -157,29 +142,14 @@
     }
 
     /**
-     * Get all the install permission states.
-     *
-     * @return the install permission states
-     */
-    @NonNull
-    public Collection<PermissionState> getInstallPermissionStates() {
-        return getPermissionStates(UserHandle.USER_ALL);
-    }
-
-    /**
      * Get all the runtime permission states for a user.
      *
      * @param userId the user ID
      * @return the runtime permission states
      */
     @NonNull
-    public Collection<PermissionState> getRuntimePermissionStates(@UserIdInt int userId) {
+    public Collection<PermissionState> getPermissionStates(@UserIdInt int userId) {
         checkUserId(userId);
-        return getPermissionStates(userId);
-    }
-
-    @NonNull
-    private Collection<PermissionState> getPermissionStates(@UserIdInt int userId) {
         final UserState userState = mUserStates.get(userId);
         if (userState == null) {
             return Collections.emptyList();
@@ -265,6 +235,8 @@
         @NonNull
         private final String mName;
 
+        private final boolean mRuntime;
+
         private final boolean mGranted;
 
         private final int mFlags;
@@ -273,17 +245,20 @@
          * Create a new instance of this class.
          *
          * @param name the name of the permission
+         * @param runtime whether the permission is runtime
          * @param granted whether the permission is granted
          * @param flags the permission flags
          */
-        public PermissionState(@NonNull String name, boolean granted, int flags) {
+        public PermissionState(@NonNull String name, boolean runtime, boolean granted, int flags) {
             mName = name;
+            mRuntime = runtime;
             mGranted = granted;
             mFlags = flags;
         }
 
         private PermissionState(@NonNull PermissionState other) {
             mName = other.mName;
+            mRuntime = other.mRuntime;
             mGranted = other.mGranted;
             mFlags = other.mFlags;
         }
@@ -299,6 +274,15 @@
         }
 
         /**
+         * Get whether the permission is a runtime permission.
+         *
+         * @return whether the permission is a runtime permission.
+         */
+        public boolean isRuntime() {
+            return mRuntime;
+        }
+
+        /**
          * Get whether the permission is granted.
          *
          * @return whether the permission is granted
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 8f42289..2a646b5 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -3965,8 +3965,7 @@
             if (!usedPermissions.contains(permissionState.getName())) {
                 Permission bp = mRegistry.getPermission(permissionState.getName());
                 if (bp != null) {
-                    if (uidState.removePermissionState(bp.getName())
-                            && permissionState.isRuntime()) {
+                    if (uidState.removePermissionState(bp.getName()) && bp.isRuntime()) {
                         runtimePermissionChanged = true;
                     }
                 }
@@ -4573,9 +4572,7 @@
                     uidState.reset();
                     uidState.setMissing(legacyState.isMissing(userId));
                     readLegacyPermissionStatesLocked(uidState,
-                            legacyState.getInstallPermissionStates());
-                    readLegacyPermissionStatesLocked(uidState,
-                            legacyState.getRuntimePermissionStates(userId));
+                            legacyState.getPermissionStates(userId));
                 }
             }
         });
@@ -4634,12 +4631,9 @@
 
                         final LegacyPermissionState.PermissionState legacyPermissionState =
                                 new LegacyPermissionState.PermissionState(permissionState.getName(),
+                                        permissionState.getPermission().isRuntime(),
                                         permissionState.isGranted(), permissionState.getFlags());
-                        if (permissionState.isRuntime()) {
-                            legacyState.putRuntimePermissionState(legacyPermissionState, userId);
-                        } else {
-                            legacyState.putInstallPermissionState(legacyPermissionState);
-                        }
+                        legacyState.putPermissionState(legacyPermissionState, userId);
                     }
                 }
             }
@@ -4904,12 +4898,9 @@
 
                     final LegacyPermissionState.PermissionState legacyPermissionState =
                             new LegacyPermissionState.PermissionState(permissionState.getName(),
+                                    permissionState.getPermission().isRuntime(),
                                     permissionState.isGranted(), permissionState.getFlags());
-                    if (permissionState.isRuntime()) {
-                        legacyState.putRuntimePermissionState(legacyPermissionState, userId);
-                    } else if (userId == UserHandle.USER_SYSTEM) {
-                        legacyState.putInstallPermissionState(legacyPermissionState);
-                    }
+                    legacyState.putPermissionState(legacyPermissionState, userId);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionState.java b/services/core/java/com/android/server/pm/permission/PermissionState.java
index 12f29d0..9ad8a05 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionState.java
@@ -32,9 +32,6 @@
     private final Object mLock = new Object();
 
     @GuardedBy("mLock")
-    private boolean mRuntime;
-
-    @GuardedBy("mLock")
     private boolean mGranted;
 
     @GuardedBy("mLock")
@@ -66,12 +63,6 @@
         return mPermission.computeGids(userId);
     }
 
-    public boolean isRuntime() {
-        synchronized (mLock) {
-            return mPermission.isRuntime();
-        }
-    }
-
     public boolean isGranted() {
         synchronized (mLock) {
             return mGranted;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 607c165..69484b1 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -447,8 +447,25 @@
     volatile int mPowerKeyPressCounter;
     volatile boolean mEndCallKeyHandled;
     volatile boolean mCameraGestureTriggeredDuringGoingToSleep;
-    volatile boolean mGoingToSleep;
-    volatile boolean mRequestedOrGoingToSleep;
+
+    /**
+     * {@code true} if the device is entering a low-power state; {@code false otherwise}.
+     *
+     * <p>This differs from {@link #mRequestedOrSleepingDefaultDisplay} which tracks the power state
+     * of the {@link #mDefaultDisplay default display} versus the power state of the entire device.
+     */
+    volatile boolean mDeviceGoingToSleep;
+
+    /**
+     * {@code true} if the {@link #mDefaultDisplay default display} is entering or was requested to
+     * enter a low-power state; {@code false otherwise}.
+     *
+     * <p>This differs from {@link #mDeviceGoingToSleep} which tracks the power state of the entire
+     * device versus the power state of the {@link #mDefaultDisplay default display}.
+     */
+    // TODO(b/178103325): Track sleep/requested sleep for every display.
+    volatile boolean mRequestedOrSleepingDefaultDisplay;
+
     volatile boolean mRecentsVisible;
     volatile boolean mNavBarVirtualKeyHapticFeedbackEnabled = true;
     volatile boolean mPictureInPictureVisible;
@@ -916,7 +933,7 @@
         if (gestureService != null) {
             gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive,
                     mTmpBoolean);
-            if (mTmpBoolean.value && mRequestedOrGoingToSleep) {
+            if (mTmpBoolean.value && mRequestedOrSleepingDefaultDisplay) {
                 mCameraGestureTriggeredDuringGoingToSleep = true;
             }
         }
@@ -1063,13 +1080,14 @@
                 case SHORT_PRESS_POWER_NOTHING:
                     break;
                 case SHORT_PRESS_POWER_GO_TO_SLEEP:
-                    goToSleepFromPowerButton(eventTime, 0);
+                    sleepDefaultDisplayFromPowerButton(eventTime, 0);
                     break;
                 case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
-                    goToSleepFromPowerButton(eventTime, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
+                    sleepDefaultDisplayFromPowerButton(eventTime,
+                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                     break;
                 case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
-                    if (goToSleepFromPowerButton(eventTime,
+                    if (sleepDefaultDisplayFromPowerButton(eventTime,
                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE)) {
                         launchHomeFromHotKey(DEFAULT_DISPLAY);
                     }
@@ -1097,11 +1115,11 @@
     }
 
     /**
-     * Sends the device to sleep as a result of a power button press.
+     * Sends the default display to sleep as a result of a power button press.
      *
-     * @return True if the was device was sent to sleep, false if sleep was suppressed.
+     * @return True if the device was sent to sleep, false if the device did not sleep.
      */
-    private boolean goToSleepFromPowerButton(long eventTime, int flags) {
+    private boolean sleepDefaultDisplayFromPowerButton(long eventTime, int flags) {
         // Before we actually go to sleep, we check the last wakeup reason.
         // If the device very recently woke up from a gesture (like user lifting their device)
         // then ignore the sleep instruction. This is because users have developed
@@ -1121,12 +1139,12 @@
             }
         }
 
-        goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags);
+        sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags);
         return true;
     }
 
-    private void goToSleep(long eventTime, int reason, int flags) {
-        mRequestedOrGoingToSleep = true;
+    private void sleepDefaultDisplay(long eventTime, int reason, int flags) {
+        mRequestedOrSleepingDefaultDisplay = true;
         mPowerManager.goToSleep(eventTime, reason, flags);
     }
 
@@ -1163,7 +1181,8 @@
                             Settings.Global.THEATER_MODE_ON, 1);
 
                     if (mGoToSleepOnButtonPressTheaterMode && interactive) {
-                        goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
+                        sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
+                                0);
                     }
                 }
                 break;
@@ -1271,7 +1290,7 @@
             case SHORT_PRESS_SLEEP_GO_TO_SLEEP:
             case SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME:
                 Slog.i(TAG, "sleepRelease() calling goToSleep(GO_TO_SLEEP_REASON_SLEEP_BUTTON)");
-                goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
+                sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
                 break;
         }
     }
@@ -1897,8 +1916,8 @@
 
         // Match current screen state.
         if (!mPowerManager.isInteractive()) {
-            startedGoingToSleep(WindowManagerPolicy.OFF_BECAUSE_OF_USER);
-            finishedGoingToSleep(WindowManagerPolicy.OFF_BECAUSE_OF_USER);
+            startedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
+            finishedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
         }
 
         mWindowManagerInternal.registerAppTransitionListener(new AppTransitionListener() {
@@ -3686,7 +3705,7 @@
                             }
                             if ((mEndcallBehavior
                                     & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
-                                goToSleep(event.getEventTime(),
+                                sleepDefaultDisplay(event.getEventTime(),
                                         PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
                                 isWakeKey = false;
                             }
@@ -3729,10 +3748,12 @@
                 // Any activity on the power button stops the accessibility shortcut
                 result &= ~ACTION_PASS_TO_USER;
                 isWakeKey = false; // wake-up will be handled separately
+                final boolean isDefaultDisplayOn = Display.isOnState(mDefaultDisplay.getState());
+                final boolean interactiveAndOn = interactive && isDefaultDisplayOn;
                 if (down) {
-                    interceptPowerKeyDown(event, interactive);
+                    interceptPowerKeyDown(event, interactiveAndOn);
                 } else {
-                    interceptPowerKeyUp(event, interactive, canceled);
+                    interceptPowerKeyUp(event, interactiveAndOn, canceled);
                 }
                 break;
             }
@@ -4234,32 +4255,34 @@
 
     // Called on the PowerManager's Notifier thread.
     @Override
-    public void startedGoingToSleep(int why) {
+    public void startedGoingToSleep(@PowerManager.GoToSleepReason int pmSleepReason) {
         if (DEBUG_WAKEUP) {
             Slog.i(TAG, "Started going to sleep... (why="
-                    + WindowManagerPolicyConstants.offReasonToString(why) + ")");
+                    + WindowManagerPolicyConstants.offReasonToString(
+                            WindowManagerPolicyConstants.translateSleepReasonToOffReason(
+                                    pmSleepReason)) + ")");
         }
 
-        mGoingToSleep = true;
-        mRequestedOrGoingToSleep = true;
+        mDeviceGoingToSleep = true;
 
         if (mKeyguardDelegate != null) {
-            mKeyguardDelegate.onStartedGoingToSleep(why);
+            mKeyguardDelegate.onStartedGoingToSleep(pmSleepReason);
         }
     }
 
     // Called on the PowerManager's Notifier thread.
     @Override
-    public void finishedGoingToSleep(int why) {
+    public void finishedGoingToSleep(@PowerManager.GoToSleepReason int pmSleepReason) {
         EventLogTags.writeScreenToggled(0);
         if (DEBUG_WAKEUP) {
             Slog.i(TAG, "Finished going to sleep... (why="
-                    + WindowManagerPolicyConstants.offReasonToString(why) + ")");
+                    + WindowManagerPolicyConstants.offReasonToString(
+                            WindowManagerPolicyConstants.translateSleepReasonToOffReason(
+                                    pmSleepReason)) + ")");
         }
         MetricsLogger.histogram(mContext, "screen_timeout", mLockScreenTimeout / 1000);
 
-        mGoingToSleep = false;
-        mRequestedOrGoingToSleep = false;
+        mDeviceGoingToSleep = false;
         mDefaultDisplayPolicy.setAwake(false);
 
         // We must get this work done here because the power manager will drop
@@ -4271,7 +4294,7 @@
         mDefaultDisplayRotation.updateOrientationListener();
 
         if (mKeyguardDelegate != null) {
-            mKeyguardDelegate.onFinishedGoingToSleep(why,
+            mKeyguardDelegate.onFinishedGoingToSleep(pmSleepReason,
                     mCameraGestureTriggeredDuringGoingToSleep);
         }
         if (mDisplayFoldController != null) {
@@ -4282,11 +4305,13 @@
 
     // Called on the PowerManager's Notifier thread.
     @Override
-    public void startedWakingUp(@OnReason int why) {
+    public void startedWakingUp(@PowerManager.WakeReason int pmWakeReason) {
         EventLogTags.writeScreenToggled(1);
         if (DEBUG_WAKEUP) {
             Slog.i(TAG, "Started waking up... (why="
-                    + WindowManagerPolicyConstants.onReasonToString(why) + ")");
+                    + WindowManagerPolicyConstants.onReasonToString(
+                            WindowManagerPolicyConstants.translateWakeReasonToOnReason(
+                                    pmWakeReason)) + ")");
         }
 
         mDefaultDisplayPolicy.setAwake(true);
@@ -4302,16 +4327,18 @@
         mDefaultDisplayRotation.updateOrientationListener();
 
         if (mKeyguardDelegate != null) {
-            mKeyguardDelegate.onStartedWakingUp();
+            mKeyguardDelegate.onStartedWakingUp(pmWakeReason);
         }
     }
 
     // Called on the PowerManager's Notifier thread.
     @Override
-    public void finishedWakingUp(@OnReason int why) {
+    public void finishedWakingUp(@PowerManager.WakeReason int pmWakeReason) {
         if (DEBUG_WAKEUP) {
             Slog.i(TAG, "Finished waking up... (why="
-                    + WindowManagerPolicyConstants.onReasonToString(why) + ")");
+                    + WindowManagerPolicyConstants.onReasonToString(
+                            WindowManagerPolicyConstants.translateWakeReasonToOnReason(
+                                    pmWakeReason)) + ")");
         }
 
         if (mKeyguardDelegate != null) {
@@ -4405,6 +4432,7 @@
 
         if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turned off...");
 
+        mRequestedOrSleepingDefaultDisplay = false;
         updateScreenOffSleepToken(true);
         mDefaultDisplayPolicy.screenTurnedOff();
         synchronized (mLock) {
@@ -4471,6 +4499,7 @@
             return;
         }
 
+        mRequestedOrSleepingDefaultDisplay = true;
         mWindowManagerFuncs.screenTurningOff(screenOffListener);
         synchronized (mLock) {
             if (mKeyguardDelegate != null) {
@@ -4556,7 +4585,7 @@
 
     @Override
     public boolean okToAnimate() {
-        return mDefaultDisplayPolicy.isAwake() && !mGoingToSleep;
+        return mDefaultDisplayPolicy.isAwake() && !mDeviceGoingToSleep;
     }
 
     /** {@inheritDoc} */
@@ -4730,8 +4759,8 @@
                 mKeyguardDelegate.onBootCompleted();
             }
         }
-        startedWakingUp(ON_BECAUSE_OF_UNKNOWN);
-        finishedWakingUp(ON_BECAUSE_OF_UNKNOWN);
+        startedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
+        finishedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
         screenTurningOn(DEFAULT_DISPLAY, null);
         screenTurnedOn(DEFAULT_DISPLAY);
     }
@@ -4932,7 +4961,7 @@
                     mWindowManagerFuncs.lockDeviceNow();
                     break;
                 case LID_BEHAVIOR_SLEEP:
-                    goToSleep(SystemClock.uptimeMillis(),
+                    sleepDefaultDisplay(SystemClock.uptimeMillis(),
                             PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH,
                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                     break;
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 1553966..e9d6440 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -73,6 +73,7 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -803,29 +804,35 @@
 
     /**
      * Called when the device has started waking up.
+     *
+     * @param pmWakeReason One of PowerManager.WAKE_REASON_*, detailing the specific reason we're
+     * waking up, such as WAKE_REASON_POWER_BUTTON or WAKE_REASON_GESTURE.
      */
-    void startedWakingUp(@OnReason int reason);
+    void startedWakingUp(@PowerManager.WakeReason int pmWakeReason);
 
     /**
      * Called when the device has finished waking up.
+     *
+     * @param pmWakeReason One of PowerManager.WAKE_REASON_*, detailing the specific reason we're
+     * waking up, such as WAKE_REASON_POWER_BUTTON or WAKE_REASON_GESTURE.
      */
-    void finishedWakingUp(@OnReason int reason);
+    void finishedWakingUp(@PowerManager.WakeReason int pmWakeReason);
 
     /**
      * Called when the device has started going to sleep.
      *
-     * @param why {@link #OFF_BECAUSE_OF_USER}, {@link #OFF_BECAUSE_OF_ADMIN},
-     * or {@link #OFF_BECAUSE_OF_TIMEOUT}.
+     * @param pmSleepReason One of PowerManager.GO_TO_SLEEP_REASON_*, detailing the specific reason
+     * we're going to sleep, such as GO_TO_SLEEP_REASON_POWER_BUTTON or GO_TO_SLEEP_REASON_TIMEOUT.
      */
-    public void startedGoingToSleep(int why);
+    public void startedGoingToSleep(@PowerManager.GoToSleepReason int pmSleepReason);
 
     /**
      * Called when the device has finished going to sleep.
      *
-     * @param why {@link #OFF_BECAUSE_OF_USER}, {@link #OFF_BECAUSE_OF_ADMIN},
-     * or {@link #OFF_BECAUSE_OF_TIMEOUT}.
+     * @param pmSleepReason One of PowerManager.GO_TO_SLEEP_REASON_*, detailing the specific reason
+     * we're going to sleep, such as GO_TO_SLEEP_REASON_POWER_BUTTON or GO_TO_SLEEP_REASON_TIMEOUT.
      */
-    public void finishedGoingToSleep(int why);
+    public void finishedGoingToSleep(@PowerManager.GoToSleepReason int pmSleepReason);
 
     /**
      * Called when the display is about to turn on to show content.
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 9b67efe..c2a1c79 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -15,6 +15,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Log;
@@ -176,7 +177,7 @@
                 // This is used to hide the scrim once keyguard displays.
                 if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE
                         || mKeyguardState.interactiveState == INTERACTIVE_STATE_WAKING) {
-                    mKeyguardService.onStartedWakingUp();
+                    mKeyguardService.onStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
                 }
                 if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE) {
                     mKeyguardService.onFinishedWakingUp();
@@ -291,10 +292,10 @@
         mKeyguardState.dreaming = false;
     }
 
-    public void onStartedWakingUp() {
+    public void onStartedWakingUp(@PowerManager.WakeReason int pmWakeReason) {
         if (mKeyguardService != null) {
             if (DEBUG) Log.v(TAG, "onStartedWakingUp()");
-            mKeyguardService.onStartedWakingUp();
+            mKeyguardService.onStartedWakingUp(pmWakeReason);
         }
         mKeyguardState.interactiveState = INTERACTIVE_STATE_WAKING;
     }
@@ -345,17 +346,19 @@
         mKeyguardState.screenState = SCREEN_STATE_ON;
     }
 
-    public void onStartedGoingToSleep(int why) {
+    public void onStartedGoingToSleep(@PowerManager.GoToSleepReason int pmSleepReason) {
         if (mKeyguardService != null) {
-            mKeyguardService.onStartedGoingToSleep(why);
+            mKeyguardService.onStartedGoingToSleep(pmSleepReason);
         }
-        mKeyguardState.offReason = why;
+        mKeyguardState.offReason =
+                WindowManagerPolicyConstants.translateSleepReasonToOffReason(pmSleepReason);
         mKeyguardState.interactiveState = INTERACTIVE_STATE_GOING_TO_SLEEP;
     }
 
-    public void onFinishedGoingToSleep(int why, boolean cameraGestureTriggered) {
+    public void onFinishedGoingToSleep(
+            @PowerManager.GoToSleepReason int pmSleepReason, boolean cameraGestureTriggered) {
         if (mKeyguardService != null) {
-            mKeyguardService.onFinishedGoingToSleep(why, cameraGestureTriggered);
+            mKeyguardService.onFinishedGoingToSleep(pmSleepReason, cameraGestureTriggered);
         }
         mKeyguardState.interactiveState = INTERACTIVE_STATE_SLEEP;
     }
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
index 4e84868..0872b3a 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.util.Slog;
 
@@ -101,27 +102,28 @@
     }
 
     @Override
-    public void onStartedGoingToSleep(int reason) {
+    public void onStartedGoingToSleep(@PowerManager.GoToSleepReason int pmSleepReason) {
         try {
-            mService.onStartedGoingToSleep(reason);
+            mService.onStartedGoingToSleep(pmSleepReason);
         } catch (RemoteException e) {
             Slog.w(TAG , "Remote Exception", e);
         }
     }
 
     @Override
-    public void onFinishedGoingToSleep(int reason, boolean cameraGestureTriggered) {
+    public void onFinishedGoingToSleep(
+            @PowerManager.GoToSleepReason int pmSleepReason, boolean cameraGestureTriggered) {
         try {
-            mService.onFinishedGoingToSleep(reason, cameraGestureTriggered);
+            mService.onFinishedGoingToSleep(pmSleepReason, cameraGestureTriggered);
         } catch (RemoteException e) {
             Slog.w(TAG , "Remote Exception", e);
         }
     }
 
     @Override
-    public void onStartedWakingUp() {
+    public void onStartedWakingUp(@PowerManager.WakeReason int pmWakeReason) {
         try {
-            mService.onStartedWakingUp();
+            mService.onStartedWakingUp(pmWakeReason);
         } catch (RemoteException e) {
             Slog.w(TAG , "Remote Exception", e);
         }
diff --git a/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java b/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java
new file mode 100644
index 0000000..8ebeea3
--- /dev/null
+++ b/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java
@@ -0,0 +1,280 @@
+/*
+ * 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.server.power;
+
+import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
+import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
+import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
+import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
+
+import static com.android.server.power.DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener.DISPLAY_GROUP_ADDED;
+import static com.android.server.power.DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener.DISPLAY_GROUP_CHANGED;
+import static com.android.server.power.DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener.DISPLAY_GROUP_REMOVED;
+
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.Handler;
+import android.os.PowerManagerInternal;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.display.DisplayGroup;
+
+/**
+ * Responsible for creating {@link DisplayPowerRequest}s and associating them with
+ * {@link com.android.server.display.DisplayGroup}s.
+ *
+ * Each {@link com.android.server.display.DisplayGroup} has a single {@link DisplayPowerRequest}
+ * which is used to request power state changes to every display in the group.
+ */
+public class DisplayGroupPowerStateMapper {
+
+    private static final String TAG = "DisplayPowerRequestMapper";
+
+    /** Lock obtained from {@link PowerManagerService}. */
+    private final Object mLock;
+
+    /** Listener to inform of changes to display groups. */
+    private final DisplayGroupPowerChangeListener mListener;
+
+    /** A mapping from DisplayGroup Id to DisplayGroup information. */
+    @GuardedBy("mLock")
+    private final SparseArray<DisplayGroupInfo> mDisplayGroupInfos = new SparseArray<>();
+
+    /** A cached array of DisplayGroup Ids. */
+    @GuardedBy("mLock")
+    private int[] mDisplayGroupIds;
+
+    private final DisplayManagerInternal.DisplayGroupListener mDisplayGroupListener =
+            new DisplayManagerInternal.DisplayGroupListener() {
+                @Override
+                public void onDisplayGroupAdded(int groupId) {
+                    synchronized (mLock) {
+                        if (mDisplayGroupInfos.contains(groupId)) {
+                            Slog.e(TAG, "Tried to add already existing group:" + groupId);
+                            return;
+                        }
+                        // For now, only the default group supports sandman.
+                        final boolean supportsSandman = groupId == DisplayGroup.DEFAULT;
+                        final DisplayGroupInfo displayGroupInfo = new DisplayGroupInfo(
+                                new DisplayPowerRequest(),
+                                getGlobalWakefulnessLocked(), /* ready= */ false,
+                                supportsSandman);
+                        mDisplayGroupInfos.append(groupId, displayGroupInfo);
+                        mDisplayGroupIds = ArrayUtils.appendInt(mDisplayGroupIds, groupId);
+                        mListener.onDisplayGroupEventLocked(DISPLAY_GROUP_ADDED, groupId);
+                    }
+                }
+
+                @Override
+                public void onDisplayGroupRemoved(int groupId) {
+                    synchronized (mLock) {
+                        if (!mDisplayGroupInfos.contains(groupId)) {
+                            Slog.e(TAG, "Tried to remove non-existent group:" + groupId);
+                            return;
+                        }
+                        mDisplayGroupInfos.delete(groupId);
+                        mDisplayGroupIds = ArrayUtils.removeInt(mDisplayGroupIds, groupId);
+                        mListener.onDisplayGroupEventLocked(DISPLAY_GROUP_REMOVED, groupId);
+                    }
+                }
+
+                @Override
+                public void onDisplayGroupChanged(int groupId) {
+                    synchronized (mLock) {
+                        mListener.onDisplayGroupEventLocked(DISPLAY_GROUP_CHANGED, groupId);
+                    }
+                }
+            };
+
+    DisplayGroupPowerStateMapper(Object lock, DisplayManagerInternal displayManagerInternal,
+            DisplayGroupPowerChangeListener listener) {
+        mLock = lock;
+        mListener = listener;
+        displayManagerInternal.registerDisplayGroupListener(mDisplayGroupListener);
+
+        final DisplayGroupInfo displayGroupInfo = new DisplayGroupInfo(
+                new DisplayPowerRequest(), WAKEFULNESS_AWAKE, /* ready= */
+                false, /* supportsSandman= */ true);
+        mDisplayGroupInfos.append(DisplayGroup.DEFAULT, displayGroupInfo);
+        mDisplayGroupIds = new int[]{DisplayGroup.DEFAULT};
+    }
+
+    DisplayPowerRequest getPowerRequestLocked(int groupId) {
+        return mDisplayGroupInfos.get(groupId).displayPowerRequest;
+    }
+
+    int[] getDisplayGroupIdsLocked() {
+        return mDisplayGroupIds;
+    }
+
+    int getWakefulnessLocked(int groupId) {
+        return mDisplayGroupInfos.get(groupId).wakefulness;
+    }
+
+    void setLastPowerOnTimeLocked(int groupId, long eventTime) {
+        mDisplayGroupInfos.get(groupId).lastPowerOnTime = eventTime;
+    }
+
+    long getLastPowerOnTimeLocked(int groupId) {
+        return mDisplayGroupInfos.get(groupId).lastPowerOnTime;
+    }
+
+    /**
+     * Returns the amalgamated wakefulness of all {@link DisplayGroup DisplayGroups}.
+     *
+     * <p>This will be the highest wakeful state of all {@link DisplayGroup DisplayGroups}; ordered
+     * from highest to lowest:
+     * <ol>
+     *     <li>{@link PowerManagerInternal#WAKEFULNESS_AWAKE}
+     *     <li>{@link PowerManagerInternal#WAKEFULNESS_DREAMING}
+     *     <li>{@link PowerManagerInternal#WAKEFULNESS_DOZING}
+     *     <li>{@link PowerManagerInternal#WAKEFULNESS_ASLEEP}
+     * </ol>
+     */
+    int getGlobalWakefulnessLocked() {
+        final int size = mDisplayGroupInfos.size();
+        int deviceWakefulness = WAKEFULNESS_ASLEEP;
+        for (int i = 0; i < size; i++) {
+            final int wakefulness = mDisplayGroupInfos.valueAt(i).wakefulness;
+            if (wakefulness == WAKEFULNESS_AWAKE) {
+                return WAKEFULNESS_AWAKE;
+            } else if (wakefulness == WAKEFULNESS_DREAMING
+                    && (deviceWakefulness == WAKEFULNESS_ASLEEP
+                    || deviceWakefulness == WAKEFULNESS_DOZING)) {
+                deviceWakefulness = WAKEFULNESS_DREAMING;
+            } else if (wakefulness == WAKEFULNESS_DOZING
+                    && deviceWakefulness == WAKEFULNESS_ASLEEP) {
+                deviceWakefulness = WAKEFULNESS_DOZING;
+            }
+        }
+
+        return deviceWakefulness;
+    }
+
+    /**
+     * Sets the {@code wakefulness} value for the {@link DisplayGroup} specified by the provided
+     * {@code groupId}.
+     *
+     * @return {@code true} if the wakefulness value was changed; {@code false} otherwise.
+     */
+    boolean setWakefulnessLocked(int groupId, int wakefulness) {
+        final DisplayGroupInfo displayGroupInfo = mDisplayGroupInfos.get(groupId);
+        if (displayGroupInfo.wakefulness != wakefulness) {
+            displayGroupInfo.wakefulness = wakefulness;
+            return true;
+        }
+
+        return false;
+    }
+
+    boolean isSandmanSummoned(int groupId) {
+        return mDisplayGroupInfos.get(groupId).sandmanSummoned;
+    }
+
+    boolean isSandmanSupported(int groupId) {
+        return mDisplayGroupInfos.get(groupId).supportsSandman;
+    }
+
+    /**
+     * Sets whether or not the sandman is summoned for the given {@code groupId}.
+     *
+     * @param groupId         Signifies the DisplayGroup for which to summon or unsummon the
+     *                        sandman.
+     * @param sandmanSummoned {@code true} to summon the sandman; {@code false} to unsummon.
+     */
+    void setSandmanSummoned(int groupId, boolean sandmanSummoned) {
+        final DisplayGroupInfo displayGroupInfo = mDisplayGroupInfos.get(groupId);
+        displayGroupInfo.sandmanSummoned = displayGroupInfo.supportsSandman && sandmanSummoned;
+    }
+
+    /**
+     * Returns {@code true} if every display in the specified group has its requested state matching
+     * its actual state.
+     *
+     * @param groupId The identifier for the display group to check for readiness.
+     */
+    boolean isReady(int groupId) {
+        return mDisplayGroupInfos.get(groupId).ready;
+    }
+
+    /** Returns {@code true} if every display has its requested state matching its actual state. */
+    boolean areAllDisplaysReadyLocked() {
+        final int size = mDisplayGroupInfos.size();
+        for (int i = 0; i < size; i++) {
+            if (!mDisplayGroupInfos.valueAt(i).ready) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Sets whether the displays specified by the provided {@code groupId} are all ready.
+     *
+     * <p>A display is ready if its reported
+     * {@link DisplayManagerInternal.DisplayPowerCallbacks#onStateChanged() actual state} matches
+     * its {@link DisplayManagerInternal#requestPowerState requested state}.
+     *
+     * @param groupId The identifier for the display group.
+     * @param ready   {@code true} if every display in the group is ready; otherwise {@code false}.
+     * @return {@code true} if the ready state changed; otherwise {@code false}.
+     */
+    boolean setDisplayGroupReadyLocked(int groupId, boolean ready) {
+        final DisplayGroupInfo displayGroupInfo = mDisplayGroupInfos.get(groupId);
+        if (displayGroupInfo.ready != ready) {
+            displayGroupInfo.ready = ready;
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Interface through which an interested party may be informed of {@link DisplayGroup} events.
+     */
+    interface DisplayGroupPowerChangeListener {
+        int DISPLAY_GROUP_ADDED = 0;
+        int DISPLAY_GROUP_REMOVED = 1;
+        int DISPLAY_GROUP_CHANGED = 2;
+
+        void onDisplayGroupEventLocked(int event, int groupId);
+    }
+
+    private static final class DisplayGroupInfo {
+        final DisplayPowerRequest displayPowerRequest;
+        int wakefulness;
+        boolean ready;
+        long lastPowerOnTime;
+        boolean sandmanSummoned;
+
+        /** {@code true} if this DisplayGroup supports dreaming; otherwise {@code false}. */
+        boolean supportsSandman;
+
+        DisplayGroupInfo(DisplayPowerRequest displayPowerRequest, int wakefulness, boolean ready,
+                boolean supportsSandman) {
+            this.displayPowerRequest = displayPowerRequest;
+            this.wakefulness = wakefulness;
+            this.ready = ready;
+            this.supportsSandman = supportsSandman;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java b/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java
deleted file mode 100644
index 6477552..0000000
--- a/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.power;
-
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManagerInternal;
-import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
-import android.os.Handler;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-import android.view.Display;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.server.display.DisplayGroup;
-
-/**
- * Responsible for creating {@link DisplayPowerRequest}s and associating them with
- * {@link com.android.server.display.DisplayGroup}s.
- *
- * Each {@link com.android.server.display.DisplayGroup} has a single {@link DisplayPowerRequest}
- * which is used to request power state changes to every display in the group.
- */
-class DisplayPowerRequestMapper {
-
-    private final Object mLock = new Object();
-
-    /** A mapping from LogicalDisplay Id to DisplayGroup Id. */
-    @GuardedBy("mLock")
-    private final SparseIntArray mDisplayGroupIds = new SparseIntArray();
-
-    /** A mapping from DisplayGroup Id to DisplayPowerRequest. */
-    @GuardedBy("mLock")
-    private final SparseArray<DisplayPowerRequest> mDisplayPowerRequests = new SparseArray<>();
-
-    private final DisplayManagerInternal mDisplayManagerInternal;
-
-    private final DisplayManager.DisplayListener mDisplayListener =
-            new DisplayManager.DisplayListener() {
-
-                @Override
-                public void onDisplayAdded(int displayId) {
-                    synchronized (mLock) {
-                        if (mDisplayGroupIds.indexOfKey(displayId) >= 0) {
-                            return;
-                        }
-                        final int displayGroupId = mDisplayManagerInternal.getDisplayGroupId(
-                                displayId);
-                        if (!mDisplayPowerRequests.contains(displayGroupId)) {
-                            // A new DisplayGroup was created; create a new DisplayPowerRequest.
-                            mDisplayPowerRequests.append(displayGroupId, new DisplayPowerRequest());
-                        }
-                        mDisplayGroupIds.append(displayId, displayGroupId);
-                    }
-                }
-
-                @Override
-                public void onDisplayRemoved(int displayId) {
-                    synchronized (mLock) {
-                        final int index = mDisplayGroupIds.indexOfKey(displayId);
-                        if (index < 0) {
-                            return;
-                        }
-                        final int displayGroupId = mDisplayGroupIds.valueAt(index);
-                        mDisplayGroupIds.removeAt(index);
-
-                        if (mDisplayGroupIds.indexOfValue(displayGroupId) < 0) {
-                            // The DisplayGroup no longer exists; delete the DisplayPowerRequest.
-                            mDisplayPowerRequests.delete(displayGroupId);
-                        }
-                    }
-                }
-
-                @Override
-                public void onDisplayChanged(int displayId) {
-                    synchronized (mLock) {
-                        final int newDisplayGroupId = mDisplayManagerInternal.getDisplayGroupId(
-                                displayId);
-                        final int oldDisplayGroupId = mDisplayGroupIds.get(displayId);
-
-                        if (!mDisplayPowerRequests.contains(newDisplayGroupId)) {
-                            // A new DisplayGroup was created; create a new DisplayPowerRequest.
-                            mDisplayPowerRequests.append(newDisplayGroupId,
-                                    new DisplayPowerRequest());
-                        }
-                        mDisplayGroupIds.put(displayId, newDisplayGroupId);
-
-                        if (mDisplayGroupIds.indexOfValue(oldDisplayGroupId) < 0) {
-                            // The DisplayGroup no longer exists; delete the DisplayPowerRequest.
-                            mDisplayPowerRequests.delete(oldDisplayGroupId);
-                        }
-                    }
-                }
-            };
-
-    DisplayPowerRequestMapper(DisplayManager displayManager,
-            DisplayManagerInternal displayManagerInternal, Handler handler) {
-        mDisplayManagerInternal = displayManagerInternal;
-        displayManager.registerDisplayListener(mDisplayListener, handler);
-        mDisplayPowerRequests.append(DisplayGroup.DEFAULT, new DisplayPowerRequest());
-        mDisplayGroupIds.append(Display.DEFAULT_DISPLAY, DisplayGroup.DEFAULT);
-    }
-
-    DisplayPowerRequest get(int displayId) {
-        synchronized (mLock) {
-            return mDisplayPowerRequests.get(mDisplayGroupIds.get(displayId));
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index d4375eb..b8e0156 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -36,7 +36,6 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
-import android.os.PowerManager.WakeReason;
 import android.os.PowerManagerInternal;
 import android.os.Process;
 import android.os.RemoteException;
@@ -49,7 +48,7 @@
 import android.telephony.TelephonyManager;
 import android.util.EventLog;
 import android.util.Slog;
-import android.view.WindowManagerPolicyConstants.OnReason;
+import android.view.WindowManagerPolicyConstants;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
@@ -455,13 +454,7 @@
         synchronized (mLock) {
             if (mInteractive) {
                 // Waking up...
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        final int why = translateOnReason(mInteractiveChangeReason);
-                        mPolicy.startedWakingUp(why);
-                    }
-                });
+                mHandler.post(() -> mPolicy.startedWakingUp(mInteractiveChangeReason));
 
                 // Send interactive broadcast.
                 mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;
@@ -470,13 +463,7 @@
             } else {
                 // Going to sleep...
                 // Tell the policy that we started going to sleep.
-                final int why = translateOffReason(mInteractiveChangeReason);
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        mPolicy.startedGoingToSleep(why);
-                    }
-                });
+                mHandler.post(() -> mPolicy.startedGoingToSleep(mInteractiveChangeReason));
             }
         }
     }
@@ -492,20 +479,17 @@
                     (int) (SystemClock.uptimeMillis() - mInteractiveChangeStartTime);
             if (mInteractive) {
                 // Finished waking up...
-                final int why = translateOnReason(mInteractiveChangeReason);
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        LogMaker log = new LogMaker(MetricsEvent.SCREEN);
-                        log.setType(MetricsEvent.TYPE_OPEN);
-                        log.setSubtype(why);
-                        log.setLatency(interactiveChangeLatency);
-                        log.addTaggedData(
-                                MetricsEvent.FIELD_SCREEN_WAKE_REASON, mInteractiveChangeReason);
-                        MetricsLogger.action(log);
-                        EventLogTags.writePowerScreenState(1, 0, 0, 0, interactiveChangeLatency);
-                        mPolicy.finishedWakingUp(why);
-                    }
+                mHandler.post(() -> {
+                    LogMaker log = new LogMaker(MetricsEvent.SCREEN);
+                    log.setType(MetricsEvent.TYPE_OPEN);
+                    log.setSubtype(WindowManagerPolicyConstants.translateWakeReasonToOnReason(
+                            mInteractiveChangeReason));
+                    log.setLatency(interactiveChangeLatency);
+                    log.addTaggedData(
+                            MetricsEvent.FIELD_SCREEN_WAKE_REASON, mInteractiveChangeReason);
+                    MetricsLogger.action(log);
+                    EventLogTags.writePowerScreenState(1, 0, 0, 0, interactiveChangeLatency);
+                    mPolicy.finishedWakingUp(mInteractiveChangeReason);
                 });
             } else {
                 // Finished going to sleep...
@@ -521,20 +505,19 @@
                 }
 
                 // Tell the policy we finished going to sleep.
-                final int why = translateOffReason(mInteractiveChangeReason);
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        LogMaker log = new LogMaker(MetricsEvent.SCREEN);
-                        log.setType(MetricsEvent.TYPE_CLOSE);
-                        log.setSubtype(why);
-                        log.setLatency(interactiveChangeLatency);
-                        log.addTaggedData(
-                                MetricsEvent.FIELD_SCREEN_SLEEP_REASON, mInteractiveChangeReason);
-                        MetricsLogger.action(log);
-                        EventLogTags.writePowerScreenState(0, why, 0, 0, interactiveChangeLatency);
-                        mPolicy.finishedGoingToSleep(why);
-                    }
+                final int offReason = WindowManagerPolicyConstants.translateSleepReasonToOffReason(
+                        mInteractiveChangeReason);
+                mHandler.post(() -> {
+                    LogMaker log = new LogMaker(MetricsEvent.SCREEN);
+                    log.setType(MetricsEvent.TYPE_CLOSE);
+                    log.setSubtype(offReason);
+                    log.setLatency(interactiveChangeLatency);
+                    log.addTaggedData(
+                            MetricsEvent.FIELD_SCREEN_SLEEP_REASON, mInteractiveChangeReason);
+                    MetricsLogger.action(log);
+                    EventLogTags.writePowerScreenState(
+                            0, offReason, 0, 0, interactiveChangeLatency);
+                    mPolicy.finishedGoingToSleep(mInteractiveChangeReason);
                 });
 
                 // Send non-interactive broadcast.
@@ -545,35 +528,6 @@
         }
     }
 
-    private static int translateOffReason(int reason) {
-        switch (reason) {
-            case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
-                return WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN;
-            case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
-            case PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE:
-                return WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT;
-            default:
-                return WindowManagerPolicy.OFF_BECAUSE_OF_USER;
-        }
-    }
-
-    private static @OnReason int translateOnReason(@WakeReason int reason) {
-        switch (reason) {
-            case PowerManager.WAKE_REASON_POWER_BUTTON:
-            case PowerManager.WAKE_REASON_PLUGGED_IN:
-            case PowerManager.WAKE_REASON_GESTURE:
-            case PowerManager.WAKE_REASON_CAMERA_LAUNCH:
-            case PowerManager.WAKE_REASON_WAKE_KEY:
-            case PowerManager.WAKE_REASON_WAKE_MOTION:
-            case PowerManager.WAKE_REASON_LID:
-                return WindowManagerPolicy.ON_BECAUSE_OF_USER;
-            case PowerManager.WAKE_REASON_APPLICATION:
-                return WindowManagerPolicy.ON_BECAUSE_OF_APPLICATION;
-            default:
-                return WindowManagerPolicy.ON_BECAUSE_OF_UNKNOWN;
-        }
-    }
-
     /**
      * Called when there has been user activity.
      */
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 084dc32..f14ff3c 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -18,6 +18,10 @@
 
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT;
+import static android.os.PowerManager.GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED;
+import static android.os.PowerManager.GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF;
+import static android.os.PowerManager.WAKE_REASON_DISPLAY_GROUP_ADDED;
+import static android.os.PowerManager.WAKE_REASON_DISPLAY_GROUP_TURNED_ON;
 import static android.os.PowerManagerInternal.MODE_DEVICE_IDLE;
 import static android.os.PowerManagerInternal.MODE_DISPLAY_INACTIVE;
 import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
@@ -42,7 +46,6 @@
 import android.hardware.SensorManager;
 import android.hardware.SystemSensorManager;
 import android.hardware.display.AmbientDisplayConfiguration;
-import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
 import android.hardware.power.Boost;
@@ -107,6 +110,7 @@
 import com.android.server.UserspaceRebootLogger;
 import com.android.server.Watchdog;
 import com.android.server.am.BatteryStatsService;
+import com.android.server.display.DisplayGroup;
 import com.android.server.lights.LightsManager;
 import com.android.server.lights.LogicalLight;
 import com.android.server.policy.WindowManagerPolicy;
@@ -176,6 +180,8 @@
     private static final int DIRTY_VR_MODE_CHANGED = 1 << 13;
     // Dirty bit: attentive timer may have timed out
     private static final int DIRTY_ATTENTIVE = 1 << 14;
+    // Dirty bit: display group power state has changed
+    private static final int DIRTY_DISPLAY_GROUP_POWER_UPDATED = 1 << 15;
 
     // Summarizes the state of all active wakelocks.
     private static final int WAKE_LOCK_CPU = 1 << 0;
@@ -297,10 +303,6 @@
     private int mWakefulnessRaw;
     private boolean mWakefulnessChanging;
 
-    // True if the sandman has just been summoned for the first time since entering the
-    // dreaming or dozing state.  Indicates whether a new dream should begin.
-    private boolean mSandmanSummoned;
-
     // True if MSG_SANDMAN has been scheduled.
     private boolean mSandmanScheduled;
 
@@ -351,11 +353,7 @@
 
     // Manages the desired power state of displays. The actual state may lag behind the
     // requested because it is updated asynchronously by the display power controller.
-    private DisplayPowerRequestMapper mDisplayPowerRequestMapper;
-
-    // True if the display power state has been fully applied, which means the display
-    // is actually on or actually off or whatever was requested.
-    private boolean mDisplayReady;
+    private DisplayGroupPowerStateMapper mDisplayGroupPowerStateMapper;
 
     // The suspend blocker used to keep the CPU alive when an application has acquired
     // a wake lock.
@@ -625,6 +623,39 @@
     // but the DreamService has not yet been told to start (it's an async process).
     private boolean mDozeStartInProgress;
 
+    private final class DisplayGroupPowerChangeListener implements
+            DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener {
+        @Override
+        public void onDisplayGroupEventLocked(int event, int groupId) {
+            final int oldWakefulness = getWakefulnessLocked();
+            final int newWakefulness = mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked();
+            if (oldWakefulness != newWakefulness) {
+                final int reason;
+                switch (newWakefulness) {
+                    case WAKEFULNESS_AWAKE:
+                        reason = event == DISPLAY_GROUP_ADDED ? WAKE_REASON_DISPLAY_GROUP_ADDED
+                                : WAKE_REASON_DISPLAY_GROUP_TURNED_ON;
+                        break;
+                    case WAKEFULNESS_DOZING:
+                        reason = event == DISPLAY_GROUP_REMOVED
+                                ? GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED
+                                : GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF;
+                        break;
+                    default:
+                        reason = 0;
+                }
+
+                setGlobalWakefulnessLocked(
+                        mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked(),
+                        mClock.uptimeMillis(), reason, Process.SYSTEM_UID, Process.SYSTEM_UID,
+                        mContext.getOpPackageName(), "groupId: " + groupId);
+            }
+
+            mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
+            updatePowerStateLocked();
+        }
+    }
+
     private final class ForegroundProfileObserver extends SynchronousUserSwitchObserver {
         @Override
         public void onUserSwitching(@UserIdInt int newUserId) throws RemoteException {
@@ -868,6 +899,12 @@
         void invalidateIsInteractiveCaches() {
             PowerManager.invalidateIsInteractiveCaches();
         }
+
+        DisplayGroupPowerStateMapper createDisplayPowerRequestMapper(Object lock,
+                DisplayManagerInternal displayManagerInternal,
+                DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener listener) {
+            return new DisplayGroupPowerStateMapper(lock, displayManagerInternal, listener);
+        }
     }
 
     final Constants mConstants;
@@ -1037,7 +1074,7 @@
                         now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
 
                 if (sQuiescent) {
-                    goToSleepNoUpdateLocked(mClock.uptimeMillis(),
+                    sleepDisplayGroupNoUpdateLocked(DisplayGroup.DEFAULT, mClock.uptimeMillis(),
                             PowerManager.GO_TO_SLEEP_REASON_QUIESCENT,
                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
                 }
@@ -1055,8 +1092,8 @@
             mPolicy = getLocalService(WindowManagerPolicy.class);
             mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class);
             mAttentionDetector.systemReady(mContext);
-            mDisplayPowerRequestMapper = new DisplayPowerRequestMapper(mContext.getSystemService(
-                    DisplayManager.class), mDisplayManagerInternal, mHandler);
+            mDisplayGroupPowerStateMapper = mInjector.createDisplayPowerRequestMapper(mLock,
+                    mDisplayManagerInternal, new DisplayGroupPowerChangeListener());
 
             SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper());
 
@@ -1373,9 +1410,11 @@
                 opPackageName = wakeLock.mPackageName;
                 opUid = wakeLock.mOwnerUid;
             }
-            wakeUpNoUpdateLocked(mClock.uptimeMillis(),
-                    PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag,
-                    opUid, opPackageName, opUid);
+            for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+                wakeDisplayGroupNoUpdateLocked(id, mClock.uptimeMillis(),
+                        PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag,
+                        opUid, opPackageName, opUid);
+            }
         }
     }
 
@@ -1664,74 +1703,69 @@
         }
     }
 
-    private void wakeUpInternal(long eventTime, @WakeReason int reason, String details, int uid,
-            String opPackageName, int opUid) {
+    private void wakeDisplayGroup(int groupId, long eventTime, @WakeReason int reason,
+            String details, int uid, String opPackageName, int opUid) {
         synchronized (mLock) {
-            if (wakeUpNoUpdateLocked(eventTime, reason, details, uid, opPackageName, opUid)) {
+            if (wakeDisplayGroupNoUpdateLocked(groupId, eventTime, reason, details, uid,
+                    opPackageName, opUid)) {
                 updatePowerStateLocked();
             }
         }
     }
 
-    private boolean wakeUpNoUpdateLocked(long eventTime, @WakeReason int reason, String details,
-            int reasonUid, String opPackageName, int opUid) {
+    private boolean wakeDisplayGroupNoUpdateLocked(int groupId, long eventTime,
+            @WakeReason int reason, String details, int uid, String opPackageName, int opUid) {
         if (DEBUG_SPEW) {
-            Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime + ", uid=" + reasonUid);
+            Slog.d(TAG, "wakeDisplayGroupNoUpdateLocked: eventTime=" + eventTime
+                    +", groupId=" + groupId + ", uid=" + uid);
         }
 
-        if (eventTime < mLastSleepTime || getWakefulnessLocked() == WAKEFULNESS_AWAKE
-                || mForceSuspendActive || !mSystemReady) {
+        if (eventTime < mLastSleepTime || mForceSuspendActive || !mSystemReady) {
             return false;
         }
 
-        Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0);
+        final int currentState = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId);
+        if (currentState == WAKEFULNESS_AWAKE) {
+            return false;
+        }
 
-        Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp");
+        Trace.traceBegin(Trace.TRACE_TAG_POWER, "powerOnDisplay");
         try {
-            Slog.i(TAG, "Waking up from "
-                    + PowerManagerInternal.wakefulnessToString(getWakefulnessLocked())
-                    + " (uid=" + reasonUid
+            Slog.i(TAG, "Powering on display group from"
+                    + PowerManagerInternal.wakefulnessToString(currentState)
+                    + " (groupId=" + groupId
+                    + ", uid=" + uid
                     + ", reason=" + PowerManager.wakeReasonToString(reason)
                     + ", details=" + details
                     + ")...");
+            Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId);
 
-            mLastWakeTime = eventTime;
-            mLastWakeReason = reason;
-            setWakefulnessLocked(WAKEFULNESS_AWAKE, reason, eventTime);
-
-            mNotifier.onWakeUp(reason, details, reasonUid, opPackageName, opUid);
-            userActivityNoUpdateLocked(
-                    eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid);
-
-            if (sQuiescent) {
-                mDirty |= DIRTY_QUIESCENT;
-            }
+            setWakefulnessLocked(groupId, WAKEFULNESS_AWAKE, eventTime, uid, reason, opUid,
+                    opPackageName, details);
+            mDisplayGroupPowerStateMapper.setLastPowerOnTimeLocked(groupId, eventTime);
+            mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
         }
+
         return true;
     }
 
-    private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
+    private void sleepDisplayGroup(int groupId, long eventTime, int reason, int flags,
+            int uid) {
         synchronized (mLock) {
-            if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
+            if (sleepDisplayGroupNoUpdateLocked(groupId, eventTime, reason, flags, uid)) {
                 updatePowerStateLocked();
             }
         }
     }
 
-    /**
-     * Puts the system in doze.
-     *
-     * This method is called goToSleep for historical reasons but actually attempts to DOZE,
-     * and only tucks itself in to SLEEP if requested with the flag
-     * {@link PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE}.
-     */
-    @SuppressWarnings("deprecation")
-    private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) {
+    private boolean sleepDisplayGroupNoUpdateLocked(int groupId, long eventTime, int reason,
+            int flags, int uid) {
         if (DEBUG_SPEW) {
-            Slog.d(TAG, "goToSleepNoUpdateLocked: eventTime=" + eventTime
-                    + ", reason=" + reason + ", flags=" + flags + ", uid=" + uid);
+            Slog.d(TAG, "powerOffDisplayGroupNoUpdateLocked: eventTime=" + eventTime
+                    + ", groupId=" + groupId + ", reason=" + reason + ", flags=" + flags
+                    + ", uid=" + uid);
         }
 
         if (eventTime < mLastWakeTime
@@ -1742,55 +1776,44 @@
             return false;
         }
 
-        Trace.traceBegin(Trace.TRACE_TAG_POWER, "goToSleep");
+        final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId);
+        if (!PowerManagerInternal.isInteractive(wakefulness)) {
+            return false;
+        }
+
+        Trace.traceBegin(Trace.TRACE_TAG_POWER, "powerOffDisplay");
         try {
             reason = Math.min(PowerManager.GO_TO_SLEEP_REASON_MAX,
                     Math.max(reason, PowerManager.GO_TO_SLEEP_REASON_MIN));
-            Slog.i(TAG, "Going to sleep due to " + PowerManager.sleepReasonToString(reason)
-                    + " (uid " + uid + ")...");
+            Slog.i(TAG, "Powering off display group due to "
+                    + PowerManager.sleepReasonToString(reason) + " (groupId= " + groupId
+                    + ", uid= " + uid + ")...");
 
-            mLastSleepTime = eventTime;
-            mLastSleepReason = reason;
-            mSandmanSummoned = true;
-            mDozeStartInProgress = true;
-            setWakefulnessLocked(WAKEFULNESS_DOZING, reason, eventTime);
-
-            // Report the number of wake locks that will be cleared by going to sleep.
-            int numWakeLocksCleared = 0;
-            final int numWakeLocks = mWakeLocks.size();
-            for (int i = 0; i < numWakeLocks; i++) {
-                final WakeLock wakeLock = mWakeLocks.get(i);
-                switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
-                    case PowerManager.FULL_WAKE_LOCK:
-                    case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
-                    case PowerManager.SCREEN_DIM_WAKE_LOCK:
-                        numWakeLocksCleared += 1;
-                        break;
-                }
-            }
-            EventLogTags.writePowerSleepRequested(numWakeLocksCleared);
-
-            // Skip dozing if requested.
+            mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, true);
+            setWakefulnessLocked(groupId, WAKEFULNESS_DOZING, eventTime, uid, reason,
+                    /* opUid= */ 0, /* opPackageName= */ null, /* details= */ null);
             if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {
-                reallyGoToSleepNoUpdateLocked(eventTime, uid);
+                reallySleepDisplayGroupNoUpdateLocked(groupId, eventTime, uid);
             }
+            mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
         }
         return true;
     }
 
-    private void napInternal(long eventTime, int uid) {
+    private void dreamDisplayGroup(int groupId, long eventTime, int uid) {
         synchronized (mLock) {
-            if (napNoUpdateLocked(eventTime, uid)) {
+            if (dreamDisplayGroupNoUpdateLocked(groupId, eventTime, uid)) {
                 updatePowerStateLocked();
             }
         }
     }
 
-    private boolean napNoUpdateLocked(long eventTime, int uid) {
+    private boolean dreamDisplayGroupNoUpdateLocked(int groupId, long eventTime, int uid) {
         if (DEBUG_SPEW) {
-            Slog.d(TAG, "napNoUpdateLocked: eventTime=" + eventTime + ", uid=" + uid);
+            Slog.d(TAG, "dreamDisplayGroupNoUpdateLocked: eventTime=" + eventTime
+                    + ", uid=" + uid);
         }
 
         if (eventTime < mLastWakeTime || getWakefulnessLocked() != WAKEFULNESS_AWAKE
@@ -1798,36 +1821,42 @@
             return false;
         }
 
-        Trace.traceBegin(Trace.TRACE_TAG_POWER, "nap");
+        Trace.traceBegin(Trace.TRACE_TAG_POWER, "napDisplayGroup");
         try {
-            Slog.i(TAG, "Nap time (uid " + uid +")...");
+            Slog.i(TAG, "Napping display group (groupId=" + groupId + ", uid=" + uid + ")...");
 
-            mSandmanSummoned = true;
-            setWakefulnessLocked(WAKEFULNESS_DREAMING, 0, eventTime);
+            mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, true);
+            setWakefulnessLocked(groupId, WAKEFULNESS_DREAMING, eventTime, uid, /* reason= */
+                    0, /* opUid= */ 0, /* opPackageName= */ null, /* details= */ null);
+            mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
+
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
         }
         return true;
     }
 
-    // Done dozing, drop everything and go to sleep.
-    private boolean reallyGoToSleepNoUpdateLocked(long eventTime, int uid) {
+    private boolean reallySleepDisplayGroupNoUpdateLocked(int groupId, long eventTime, int uid) {
         if (DEBUG_SPEW) {
-            Slog.d(TAG, "reallyGoToSleepNoUpdateLocked: eventTime=" + eventTime
+            Slog.d(TAG, "reallySleepDisplayGroupNoUpdateLocked: eventTime=" + eventTime
                     + ", uid=" + uid);
         }
 
         if (eventTime < mLastWakeTime || getWakefulnessLocked() == WAKEFULNESS_ASLEEP
-                || !mBootCompleted || !mSystemReady) {
+                || !mBootCompleted || !mSystemReady
+                || mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId)
+                == WAKEFULNESS_ASLEEP) {
             return false;
         }
 
-        Trace.traceBegin(Trace.TRACE_TAG_POWER, "reallyGoToSleep");
+        Trace.traceBegin(Trace.TRACE_TAG_POWER, "reallySleepDisplayGroup");
         try {
-            Slog.i(TAG, "Sleeping (uid " + uid +")...");
+            Slog.i(TAG, "Sleeping display group (groupId=" + groupId + ", uid=" + uid +")...");
 
-            setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
-                    eventTime);
+            setWakefulnessLocked(groupId, WAKEFULNESS_ASLEEP, eventTime, uid,
+                    PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,  /* opUid= */ 0,
+                    /* opPackageName= */ null, /* details= */ null);
+            mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
         }
@@ -1835,8 +1864,62 @@
     }
 
     @VisibleForTesting
-    void setWakefulnessLocked(int wakefulness, int reason, long eventTime) {
-        if (getWakefulnessLocked() != wakefulness) {
+    void setWakefulnessLocked(int groupId, int wakefulness, long eventTime, int uid, int reason,
+            int opUid, String opPackageName, String details) {
+        if (mDisplayGroupPowerStateMapper.setWakefulnessLocked(groupId, wakefulness)) {
+            setGlobalWakefulnessLocked(mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked(),
+                    eventTime, reason, uid, opUid, opPackageName, details);
+        }
+    }
+
+    private void setGlobalWakefulnessLocked(int wakefulness, long eventTime, int reason, int uid,
+            int opUid, String opPackageName, String details) {
+        if (getWakefulnessLocked() == wakefulness) {
+            return;
+        }
+
+        // Phase 1: Handle pre-wakefulness change bookkeeping.
+        final String traceMethodName;
+        switch (wakefulness) {
+            case WAKEFULNESS_ASLEEP:
+                traceMethodName = "reallyGoToSleep";
+                Slog.i(TAG, "Sleeping (uid " + uid + ")...");
+                break;
+
+            case WAKEFULNESS_AWAKE:
+                traceMethodName = "wakeUp";
+                Slog.i(TAG, "Waking up from "
+                        + PowerManagerInternal.wakefulnessToString(getWakefulnessLocked())
+                        + " (uid=" + uid
+                        + ", reason=" + PowerManager.wakeReasonToString(reason)
+                        + ", details=" + details
+                        + ")...");
+                mLastWakeTime = eventTime;
+                mLastWakeReason = reason;
+                break;
+
+            case WAKEFULNESS_DREAMING:
+                traceMethodName = "nap";
+                Slog.i(TAG, "Nap time (uid " + uid + ")...");
+                break;
+
+            case WAKEFULNESS_DOZING:
+                traceMethodName = "goToSleep";
+                Slog.i(TAG, "Going to sleep due to " + PowerManager.sleepReasonToString(reason)
+                        + " (uid " + uid + ")...");
+
+                mLastSleepTime = eventTime;
+                mLastSleepReason = reason;
+                mDozeStartInProgress = true;
+                break;
+
+            default:
+                throw new IllegalArgumentException("Unexpected wakefulness: " + wakefulness);
+        }
+
+        Trace.traceBegin(Trace.TRACE_TAG_POWER, traceMethodName);
+        try {
+            // Phase 2: Handle wakefulness change and bookkeeping.
             // Under lock, invalidate before set ensures caches won't return stale values.
             mInjector.invalidateIsInteractiveCaches();
             mWakefulnessRaw = wakefulness;
@@ -1850,6 +1933,37 @@
                 mNotifier.onWakefulnessChangeStarted(wakefulness, reason, eventTime);
             }
             mAttentionDetector.onWakefulnessChangeStarted(wakefulness);
+
+            // Phase 3: Handle post-wakefulness change bookkeeping.
+            switch (wakefulness) {
+                case WAKEFULNESS_AWAKE:
+                    mNotifier.onWakeUp(reason, details, uid, opPackageName, opUid);
+                    userActivityNoUpdateLocked(
+                            eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
+                    if (sQuiescent) {
+                        mDirty |= DIRTY_QUIESCENT;
+                    }
+                    break;
+
+                case WAKEFULNESS_DOZING:
+                    // Report the number of wake locks that will be cleared by going to sleep.
+                    int numWakeLocksCleared = 0;
+                    final int numWakeLocks = mWakeLocks.size();
+                    for (int i = 0; i < numWakeLocks; i++) {
+                        final WakeLock wakeLock = mWakeLocks.get(i);
+                        switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
+                            case PowerManager.FULL_WAKE_LOCK:
+                            case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
+                            case PowerManager.SCREEN_DIM_WAKE_LOCK:
+                                numWakeLocksCleared += 1;
+                                break;
+                        }
+                    }
+                    EventLogTags.writePowerSleepRequested(numWakeLocksCleared);
+                    break;
+            }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_POWER);
         }
     }
 
@@ -1872,7 +1986,7 @@
     }
 
     private void finishWakefulnessChangeIfNeededLocked() {
-        if (mWakefulnessChanging && mDisplayReady) {
+        if (mWakefulnessChanging && mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) {
             if (getWakefulnessLocked() == WAKEFULNESS_DOZING
                     && (mWakeLockSummary & WAKE_LOCK_DOZE) == 0) {
                 return; // wait until dream has enabled dozing
@@ -1884,13 +1998,6 @@
                     || getWakefulnessLocked() == WAKEFULNESS_ASLEEP) {
                 logSleepTimeoutRecapturedLocked();
             }
-            if (getWakefulnessLocked() == WAKEFULNESS_AWAKE) {
-                Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0);
-                final int latencyMs = (int) (mClock.uptimeMillis() - mLastWakeTime);
-                if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) {
-                    Slog.w(TAG, "Screen on took " + latencyMs + " ms");
-                }
-            }
             mWakefulnessChanging = false;
             mNotifier.onWakefulnessChangeFinished();
         }
@@ -1989,7 +2096,6 @@
         if ((dirty & DIRTY_BATTERY_STATE) != 0) {
             final boolean wasPowered = mIsPowered;
             final int oldPlugType = mPlugType;
-            final boolean oldLevelLow = mBatteryLevelLow;
             mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
             mPlugType = mBatteryManagerInternal.getPlugType();
             mBatteryLevel = mBatteryManagerInternal.getBatteryLevel();
@@ -2018,7 +2124,8 @@
                 final long now = mClock.uptimeMillis();
                 if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType,
                         dockedOnWirelessCharger)) {
-                    wakeUpNoUpdateLocked(now, PowerManager.WAKE_REASON_PLUGGED_IN,
+                    wakeDisplayGroupNoUpdateLocked(DisplayGroup.DEFAULT, now,
+                            PowerManager.WAKE_REASON_PLUGGED_IN,
                             "android.server.power:PLUGGED:" + mIsPowered, Process.SYSTEM_UID,
                             mContext.getOpPackageName(), Process.SYSTEM_UID);
                 }
@@ -2300,7 +2407,8 @@
                     nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;
                     if (now < nextTimeout) {
                         final DisplayPowerRequest displayPowerRequest =
-                                mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY);
+                                mDisplayGroupPowerStateMapper.getPowerRequestLocked(
+                                        DisplayGroup.DEFAULT);
                         if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT
                                 || displayPowerRequest.policy == DisplayPowerRequest.POLICY_VR) {
                             mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
@@ -2555,13 +2663,23 @@
                 }
                 final long time = mClock.uptimeMillis();
                 if (isAttentiveTimeoutExpired(time)) {
-                    changed = goToSleepNoUpdateLocked(time, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
-                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
+                    // TODO (b/175764389): Support per-display timeouts.
+                    for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+                        changed = sleepDisplayGroupNoUpdateLocked(id, time,
+                                PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
+                                PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
+                    }
                 } else if (shouldNapAtBedTimeLocked()) {
-                    changed = napNoUpdateLocked(time, Process.SYSTEM_UID);
+                    // TODO (b/175764389): Support per-display timeouts.
+                    for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+                        changed = dreamDisplayGroupNoUpdateLocked(id, time, Process.SYSTEM_UID);
+                    }
                 } else {
-                    changed = goToSleepNoUpdateLocked(time,
-                            PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
+                    // TODO (b/175764389): Support per-display timeouts.
+                    for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+                        changed = sleepDisplayGroupNoUpdateLocked(id, time,
+                                PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
+                    }
                 }
             }
         }
@@ -2644,7 +2762,7 @@
                 | DIRTY_STAY_ON
                 | DIRTY_PROXIMITY_POSITIVE
                 | DIRTY_BATTERY_STATE)) != 0 || displayBecameReady) {
-            if (mDisplayReady) {
+            if (mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) {
                 scheduleSandmanLocked();
             }
         }
@@ -2659,6 +2777,14 @@
         }
     }
 
+    private void handleSandman() {
+        for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+            if (mDisplayGroupPowerStateMapper.isSandmanSupported(id)) {
+                handleSandman(id);
+            }
+        }
+    }
+
     /**
      * Called when the device enters or exits a dreaming or dozing state.
      *
@@ -2666,16 +2792,18 @@
      * the dream and we don't want to hold our lock while doing so.  There is a risk that
      * the device will wake or go to sleep in the meantime so we have to handle that case.
      */
-    private void handleSandman() { // runs on handler thread
+    private void handleSandman(int groupId) { // runs on handler thread
         // Handle preconditions.
         final boolean startDreaming;
         final int wakefulness;
         synchronized (mLock) {
             mSandmanScheduled = false;
+            // TODO (b/175764708): Support per-display doze.
             wakefulness = getWakefulnessLocked();
-            if (mSandmanSummoned && mDisplayReady) {
-                startDreaming = canDreamLocked() || canDozeLocked();
-                mSandmanSummoned = false;
+            if (mDisplayGroupPowerStateMapper.isSandmanSummoned(groupId)
+                    && mDisplayGroupPowerStateMapper.isReady(groupId)) {
+                startDreaming = canDreamLocked(groupId) || canDozeLocked();
+                mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, false);
             } else {
                 startDreaming = false;
             }
@@ -2714,14 +2842,15 @@
 
             // If preconditions changed, wait for the next iteration to determine
             // whether the dream should continue (or be restarted).
-            if (mSandmanSummoned || getWakefulnessLocked() != wakefulness) {
+            if (mDisplayGroupPowerStateMapper.isSandmanSummoned(groupId)
+                    || mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId) != wakefulness) {
                 return; // wait for next cycle
             }
 
             // Determine whether the dream should continue.
             long now = mClock.uptimeMillis();
             if (wakefulness == WAKEFULNESS_DREAMING) {
-                if (isDreaming && canDreamLocked()) {
+                if (isDreaming && canDreamLocked(groupId)) {
                     if (mDreamsBatteryLevelDrainCutoffConfig >= 0
                             && mBatteryLevel < mBatteryLevelWhenDreamStarted
                                     - mDreamsBatteryLevelDrainCutoffConfig
@@ -2741,16 +2870,13 @@
 
                 // Dream has ended or will be stopped.  Update the power state.
                 if (isItBedTimeYetLocked()) {
-                    int flags = 0;
-                    if (isAttentiveTimeoutExpired(now)) {
-                        flags |= PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE;
-                    }
-                    goToSleepNoUpdateLocked(now, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, flags,
-                            Process.SYSTEM_UID);
+                    final int flags = isAttentiveTimeoutExpired(now)
+                            ? PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE : 0;
+                    sleepDisplayGroupNoUpdateLocked(groupId, now,
+                            PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, flags, Process.SYSTEM_UID);
                     updatePowerStateLocked();
                 } else {
-                    wakeUpNoUpdateLocked(now,
-                            PowerManager.WAKE_REASON_UNKNOWN,
+                    wakeDisplayGroupNoUpdateLocked(groupId, now, PowerManager.WAKE_REASON_UNKNOWN,
                             "android.server.power:DREAM_FINISHED", Process.SYSTEM_UID,
                             mContext.getOpPackageName(), Process.SYSTEM_UID);
                     updatePowerStateLocked();
@@ -2761,7 +2887,7 @@
                 }
 
                 // Doze has ended or will be stopped.  Update the power state.
-                reallyGoToSleepNoUpdateLocked(now, Process.SYSTEM_UID);
+                reallySleepDisplayGroupNoUpdateLocked(groupId, now, Process.SYSTEM_UID);
                 updatePowerStateLocked();
             }
         }
@@ -2773,11 +2899,11 @@
     }
 
     /**
-     * Returns true if the device is allowed to dream in its current state.
+     * Returns true if the {@code groupId} is allowed to dream in its current state.
      */
-    private boolean canDreamLocked() {
+    private boolean canDreamLocked(int groupId) {
         final DisplayPowerRequest displayPowerRequest =
-                mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY);
+                mDisplayGroupPowerStateMapper.getPowerRequestLocked(groupId);
         if (getWakefulnessLocked() != WAKEFULNESS_DREAMING
                 || !mDreamsSupportedConfig
                 || !mDreamsEnabledSetting
@@ -2815,91 +2941,113 @@
 
     /**
      * Updates the display power state asynchronously.
-     * When the update is finished, mDisplayReady will be set to true.  The display
-     * controller posts a message to tell us when the actual display power state
+     * When the update is finished, the ready state of the displays will be updated.  The display
+     * controllers post a message to tell us when the actual display power state
      * has been updated so we come back here to double-check and finish up.
      *
      * This function recalculates the display power state each time.
      *
-     * @return True if the display became ready.
+     * @return {@code true} if all displays became ready; {@code false} otherwise
      */
     private boolean updateDisplayPowerStateLocked(int dirty) {
-        final boolean oldDisplayReady = mDisplayReady;
+        final boolean oldDisplayReady = mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked();
         if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
                 | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
                 | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_VR_MODE_CHANGED |
-                DIRTY_QUIESCENT)) != 0) {
+                DIRTY_QUIESCENT | DIRTY_DISPLAY_GROUP_POWER_UPDATED)) != 0) {
             if ((dirty & DIRTY_QUIESCENT) != 0) {
                 sQuiescent = false;
             }
 
-            final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get(
-                    Display.DEFAULT_DISPLAY);
-            displayPowerRequest.policy = getDesiredScreenPolicyLocked();
+            for (final int groupId : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+                final DisplayPowerRequest displayPowerRequest =
+                        mDisplayGroupPowerStateMapper.getPowerRequestLocked(groupId);
+                displayPowerRequest.policy = getDesiredScreenPolicyLocked(groupId);
 
-            // Determine appropriate screen brightness and auto-brightness adjustments.
-            final boolean autoBrightness;
-            final float screenBrightnessOverride;
-            if (!mBootCompleted) {
-                // Keep the brightness steady during boot. This requires the
-                // bootloader brightness and the default brightness to be identical.
-                autoBrightness = false;
-                screenBrightnessOverride = mScreenBrightnessDefault;
-            } else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {
-                autoBrightness = false;
-                screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManager;
-            } else {
-                autoBrightness = (mScreenBrightnessModeSetting ==
-                        Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-                screenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
-            }
+                // Determine appropriate screen brightness and auto-brightness adjustments.
+                final boolean autoBrightness;
+                final float screenBrightnessOverride;
+                if (!mBootCompleted) {
+                    // Keep the brightness steady during boot. This requires the
+                    // bootloader brightness and the default brightness to be identical.
+                    autoBrightness = false;
+                    screenBrightnessOverride = mScreenBrightnessDefault;
+                } else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {
+                    autoBrightness = false;
+                    screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManager;
+                } else {
+                    autoBrightness = (mScreenBrightnessModeSetting ==
+                            Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+                    screenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+                }
 
-            // Update display power request.
-            displayPowerRequest.screenBrightnessOverride = screenBrightnessOverride;
-            displayPowerRequest.useAutoBrightness = autoBrightness;
-            displayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
-            displayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness();
+                // Update display power request.
+                displayPowerRequest.screenBrightnessOverride = screenBrightnessOverride;
+                displayPowerRequest.useAutoBrightness = autoBrightness;
+                displayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
+                displayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness();
 
-            updatePowerRequestFromBatterySaverPolicy(displayPowerRequest);
+                updatePowerRequestFromBatterySaverPolicy(displayPowerRequest);
 
-            if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
-                displayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
-                if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0
-                        && !mDrawWakeLockOverrideFromSidekick) {
-                    if (displayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) {
-                        displayPowerRequest.dozeScreenState = Display.STATE_DOZE;
+                if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
+                    displayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
+                    if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0
+                            && !mDrawWakeLockOverrideFromSidekick) {
+                        if (displayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) {
+                            displayPowerRequest.dozeScreenState = Display.STATE_DOZE;
+                        }
+                        if (displayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) {
+                            displayPowerRequest.dozeScreenState = Display.STATE_ON;
+                        }
                     }
-                    if (displayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) {
-                        displayPowerRequest.dozeScreenState = Display.STATE_ON;
+                    displayPowerRequest.dozeScreenBrightness =
+                            mDozeScreenBrightnessOverrideFromDreamManagerFloat;
+                } else {
+                    displayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN;
+                    displayPowerRequest.dozeScreenBrightness =
+                            PowerManager.BRIGHTNESS_INVALID_FLOAT;
+                }
+
+                final boolean ready = mDisplayManagerInternal.requestPowerState(groupId,
+                        displayPowerRequest, mRequestWaitForNegativeProximity);
+
+                if (DEBUG_SPEW) {
+                    Slog.d(TAG, "updateDisplayPowerStateLocked: displayReady=" + ready
+                            + ", groupId=" + groupId
+                            + ", policy=" + displayPowerRequest.policy
+                            + ", mWakefulness="
+                            + mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId)
+                            + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)
+                            + ", mUserActivitySummary=0x" + Integer.toHexString(
+                            mUserActivitySummary)
+                            + ", mBootCompleted=" + mBootCompleted
+                            + ", screenBrightnessOverride="
+                            + displayPowerRequest.screenBrightnessOverride
+                            + ", useAutoBrightness=" + displayPowerRequest.useAutoBrightness
+                            + ", mScreenBrightnessBoostInProgress="
+                            + mScreenBrightnessBoostInProgress
+                            + ", mIsVrModeEnabled= " + mIsVrModeEnabled
+                            + ", sQuiescent=" + sQuiescent);
+                }
+
+                final boolean displayReadyStateChanged =
+                        mDisplayGroupPowerStateMapper.setDisplayGroupReadyLocked(groupId, ready);
+                if (ready && displayReadyStateChanged
+                        && mDisplayGroupPowerStateMapper.getWakefulnessLocked(
+                        groupId) == WAKEFULNESS_AWAKE) {
+                    Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId);
+                    final int latencyMs = (int) (mClock.uptimeMillis()
+                            - mDisplayGroupPowerStateMapper.getLastPowerOnTimeLocked(groupId));
+                    if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) {
+                        Slog.w(TAG, "Screen on took " + latencyMs + " ms");
                     }
                 }
-                displayPowerRequest.dozeScreenBrightness =
-                        mDozeScreenBrightnessOverrideFromDreamManagerFloat;
-            } else {
-                displayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN;
-                displayPowerRequest.dozeScreenBrightness =
-                        PowerManager.BRIGHTNESS_INVALID_FLOAT;
             }
 
-            mDisplayReady = mDisplayManagerInternal.requestPowerState(displayPowerRequest,
-                    mRequestWaitForNegativeProximity);
             mRequestWaitForNegativeProximity = false;
 
-            if (DEBUG_SPEW) {
-                Slog.d(TAG, "updateDisplayPowerStateLocked: mDisplayReady=" + mDisplayReady
-                        + ", policy=" + displayPowerRequest.policy
-                        + ", mWakefulness=" + getWakefulnessLocked()
-                        + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)
-                        + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
-                        + ", mBootCompleted=" + mBootCompleted
-                        + ", screenBrightnessOverride=" + screenBrightnessOverride
-                        + ", useAutoBrightness=" + autoBrightness
-                        + ", mScreenBrightnessBoostInProgress=" + mScreenBrightnessBoostInProgress
-                        + ", mIsVrModeEnabled= " + mIsVrModeEnabled
-                        + ", sQuiescent=" + sQuiescent);
-            }
         }
-        return mDisplayReady && !oldDisplayReady;
+        return mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked() && !oldDisplayReady;
     }
 
     private void updateScreenBrightnessBoostLocked(int dirty) {
@@ -2933,12 +3081,11 @@
     }
 
     @VisibleForTesting
-    int getDesiredScreenPolicyLocked() {
-        if (getWakefulnessLocked() == WAKEFULNESS_ASLEEP || sQuiescent) {
+    int getDesiredScreenPolicyLocked(int groupId) {
+        final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId);
+        if (wakefulness == WAKEFULNESS_ASLEEP || sQuiescent) {
             return DisplayPowerRequest.POLICY_OFF;
-        }
-
-        if (getWakefulnessLocked() == WAKEFULNESS_DOZING) {
+        } else if (wakefulness == WAKEFULNESS_DOZING) {
             if ((mWakeLockSummary & WAKE_LOCK_DOZE) != 0) {
                 return DisplayPowerRequest.POLICY_DOZE;
             }
@@ -2968,7 +3115,6 @@
 
     private final DisplayManagerInternal.DisplayPowerCallbacks mDisplayPowerCallbacks =
             new DisplayManagerInternal.DisplayPowerCallbacks() {
-        private int mDisplayState = Display.STATE_UNKNOWN;
 
         @Override
         public void onStateChanged() {
@@ -2999,29 +3145,25 @@
         }
 
         @Override
-        public void onDisplayStateChange(int state) {
+        public void onDisplayStateChange(boolean allInactive, boolean allOff) {
             // This method is only needed to support legacy display blanking behavior
             // where the display's power state is coupled to suspend or to the power HAL.
             // The order of operations matters here.
             synchronized (mLock) {
-                if (mDisplayState != state) {
-                    mDisplayState = state;
-                    setPowerModeInternal(MODE_DISPLAY_INACTIVE,
-                            !Display.isActiveState(state));
-                    if (state == Display.STATE_OFF) {
-                        if (!mDecoupleHalInteractiveModeFromDisplayConfig) {
-                            setHalInteractiveModeLocked(false);
-                        }
-                        if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) {
-                            setHalAutoSuspendModeLocked(true);
-                        }
-                    } else {
-                        if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) {
-                            setHalAutoSuspendModeLocked(false);
-                        }
-                        if (!mDecoupleHalInteractiveModeFromDisplayConfig) {
-                            setHalInteractiveModeLocked(true);
-                        }
+                setPowerModeInternal(MODE_DISPLAY_INACTIVE, allInactive);
+                if (allOff) {
+                    if (!mDecoupleHalInteractiveModeFromDisplayConfig) {
+                        setHalInteractiveModeLocked(false);
+                    }
+                    if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) {
+                        setHalAutoSuspendModeLocked(true);
+                    }
+                } else {
+                    if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) {
+                        setHalAutoSuspendModeLocked(false);
+                    }
+                    if (!mDecoupleHalInteractiveModeFromDisplayConfig) {
+                        setHalInteractiveModeLocked(true);
                     }
                 }
             }
@@ -3036,13 +3178,6 @@
         public void releaseSuspendBlocker() {
             mDisplaySuspendBlocker.release();
         }
-
-        @Override
-        public String toString() {
-            synchronized (this) {
-                return "state=" + Display.stateToString(mDisplayState);
-            }
-        }
     };
 
     private boolean shouldUseProximitySensorLocked() {
@@ -3058,9 +3193,13 @@
         final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0);
         final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked();
         final boolean autoSuspend = !needDisplaySuspendBlocker;
-        final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get(
-                Display.DEFAULT_DISPLAY);
-        final boolean interactive = displayPowerRequest.isBrightOrDim();
+        final DisplayPowerRequest displayPowerRequest =
+                mDisplayGroupPowerStateMapper.getPowerRequestLocked(DisplayGroup.DEFAULT);
+        final int[] groupIds = mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked();
+        boolean interactive = false;
+        for (int id : groupIds) {
+            interactive |= mDisplayGroupPowerStateMapper.getPowerRequestLocked(id).isBrightOrDim();
+        }
 
         // Disable auto-suspend if needed.
         // FIXME We should consider just leaving auto-suspend enabled forever since
@@ -3090,7 +3229,7 @@
             // until the display is actually ready so that all transitions have
             // completed.  This is probably a good sign that things have gotten
             // too tangled over here...
-            if (interactive || mDisplayReady) {
+            if (interactive || mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) {
                 setHalInteractiveModeLocked(interactive);
             }
         }
@@ -3116,29 +3255,10 @@
      * We do so if the screen is on or is in transition between states.
      */
     private boolean needDisplaySuspendBlockerLocked() {
-        if (!mDisplayReady) {
+        if (!mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) {
             return true;
         }
-        final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get(
-                Display.DEFAULT_DISPLAY);
-        if (displayPowerRequest.isBrightOrDim()) {
-            // If we asked for the screen to be on but it is off due to the proximity
-            // sensor then we may suspend but only if the configuration allows it.
-            // On some hardware it may not be safe to suspend because the proximity
-            // sensor may not be correctly configured as a wake-up source.
-            if (!displayPowerRequest.useProximitySensor || !mProximityPositive
-                    || !mSuspendWhenScreenOffDueToProximityConfig) {
-                return true;
-            }
-        }
 
-        if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE
-                && displayPowerRequest.dozeScreenState == Display.STATE_ON) {
-            // Although we are in DOZE and would normally allow the device to suspend,
-            // the doze service has explicitly requested the display to remain in the ON
-            // state which means we should hold the display suspend blocker.
-            return true;
-        }
         if (mScreenBrightnessBoostInProgress) {
             return true;
         }
@@ -3152,6 +3272,30 @@
             return true;
         }
 
+        final int[] groupIds = mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked();
+        for (int id : groupIds) {
+            final DisplayPowerRequest displayPowerRequest =
+                    mDisplayGroupPowerStateMapper.getPowerRequestLocked(id);
+            if (displayPowerRequest.isBrightOrDim()) {
+                // If we asked for the screen to be on but it is off due to the proximity
+                // sensor then we may suspend but only if the configuration allows it.
+                // On some hardware it may not be safe to suspend because the proximity
+                // sensor may not be correctly configured as a wake-up source.
+                if (!displayPowerRequest.useProximitySensor || !mProximityPositive
+                        || !mSuspendWhenScreenOffDueToProximityConfig) {
+                    return true;
+                }
+            }
+
+            if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE
+                    && displayPowerRequest.dozeScreenState == Display.STATE_ON) {
+                // Although we are in DOZE and would normally allow the device to suspend,
+                // the doze service has explicitly requested the display to remain in the ON
+                // state which means we should hold the display suspend blocker.
+                return true;
+            }
+        }
+
         // Let the system suspend if the screen is off or dozing.
         return false;
     }
@@ -3685,9 +3829,15 @@
             synchronized (mLock) {
                 mForceSuspendActive = true;
                 // Place the system in an non-interactive state
-                goToSleepInternal(mClock.uptimeMillis(),
-                        PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND,
-                        PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, uid);
+                boolean updatePowerState = false;
+                for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+                    updatePowerState |= sleepDisplayGroupNoUpdateLocked(id, mClock.uptimeMillis(),
+                            PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND,
+                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, uid);
+                }
+                if (updatePowerState) {
+                    updatePowerStateLocked();
+                }
 
                 // Disable all the partial wake locks as well
                 updateWakeLockDisabledStatesLocked();
@@ -3827,7 +3977,6 @@
             pw.println("  mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary));
             pw.println("  mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity);
             pw.println("  mSandmanScheduled=" + mSandmanScheduled);
-            pw.println("  mSandmanSummoned=" + mSandmanSummoned);
             pw.println("  mBatteryLevelLow=" + mBatteryLevelLow);
             pw.println("  mLightDeviceIdleMode=" + mLightDeviceIdleMode);
             pw.println("  mDeviceIdleMode=" + mDeviceIdleMode);
@@ -3845,7 +3994,6 @@
                     + TimeUtils.formatUptime(mLastScreenBrightnessBoostTime));
             pw.println("  mScreenBrightnessBoostInProgress="
                     + mScreenBrightnessBoostInProgress);
-            pw.println("  mDisplayReady=" + mDisplayReady);
             pw.println("  mHoldingWakeLockSuspendBlocker=" + mHoldingWakeLockSuspendBlocker);
             pw.println("  mHoldingDisplaySuspendBlocker=" + mHoldingDisplaySuspendBlocker);
 
@@ -4080,7 +4228,6 @@
                     PowerManagerServiceDumpProto.IS_REQUEST_WAIT_FOR_NEGATIVE_PROXIMITY,
                     mRequestWaitForNegativeProximity);
             proto.write(PowerManagerServiceDumpProto.IS_SANDMAN_SCHEDULED, mSandmanScheduled);
-            proto.write(PowerManagerServiceDumpProto.IS_SANDMAN_SUMMONED, mSandmanSummoned);
             proto.write(PowerManagerServiceDumpProto.IS_BATTERY_LEVEL_LOW, mBatteryLevelLow);
             proto.write(PowerManagerServiceDumpProto.IS_LIGHT_DEVICE_IDLE_MODE, mLightDeviceIdleMode);
             proto.write(PowerManagerServiceDumpProto.IS_DEVICE_IDLE_MODE, mDeviceIdleMode);
@@ -4107,7 +4254,6 @@
             proto.write(
                     PowerManagerServiceDumpProto.IS_SCREEN_BRIGHTNESS_BOOST_IN_PROGRESS,
                     mScreenBrightnessBoostInProgress);
-            proto.write(PowerManagerServiceDumpProto.IS_DISPLAY_READY, mDisplayReady);
             proto.write(
                     PowerManagerServiceDumpProto.IS_HOLDING_WAKE_LOCK_SUSPEND_BLOCKER,
                     mHoldingWakeLockSuspendBlocker);
@@ -4921,7 +5067,8 @@
             final int uid = Binder.getCallingUid();
             final long ident = Binder.clearCallingIdentity();
             try {
-                wakeUpInternal(eventTime, reason, details, uid, opPackageName, uid);
+                wakeDisplayGroup(DisplayGroup.DEFAULT, eventTime, reason, details, uid,
+                        opPackageName, uid);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -4939,7 +5086,7 @@
             final int uid = Binder.getCallingUid();
             final long ident = Binder.clearCallingIdentity();
             try {
-                goToSleepInternal(eventTime, reason, flags, uid);
+                sleepDisplayGroup(DisplayGroup.DEFAULT, eventTime, reason, flags, uid);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -4957,7 +5104,7 @@
             final int uid = Binder.getCallingUid();
             final long ident = Binder.clearCallingIdentity();
             try {
-                napInternal(eventTime, uid);
+                dreamDisplayGroup(DisplayGroup.DEFAULT, eventTime, uid);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -5590,7 +5737,7 @@
             // also tells us that we're not already ignoring the proximity sensor.
 
             final DisplayPowerRequest displayPowerRequest =
-                    mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY);
+                    mDisplayGroupPowerStateMapper.getPowerRequestLocked(DisplayGroup.DEFAULT);
             if (displayPowerRequest.useProximitySensor && mProximityPositive) {
                 mDisplayManagerInternal.ignoreProximitySensorUntilChanged();
                 return true;
diff --git a/services/core/java/com/android/server/power/PreRebootLogger.java b/services/core/java/com/android/server/power/PreRebootLogger.java
index c9e81ed..2e4b054 100644
--- a/services/core/java/com/android/server/power/PreRebootLogger.java
+++ b/services/core/java/com/android/server/power/PreRebootLogger.java
@@ -19,6 +19,7 @@
 import android.annotation.DurationMillisLong;
 import android.annotation.NonNull;
 import android.content.Context;
+import android.os.Binder;
 import android.os.Environment;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
@@ -146,6 +147,7 @@
             return;
         }
 
+        final long token = Binder.clearCallingIdentity();
         try {
             final File dumpFile = new File(dumpDir, serviceName);
             final ParcelFileDescriptor fd = ParcelFileDescriptor.open(dumpFile,
@@ -154,6 +156,8 @@
             binder.dump(fd.getFileDescriptor(), ArrayUtils.emptyArray(String.class));
         } catch (FileNotFoundException | RemoteException e) {
             Slog.e(TAG, String.format("Failed to dump %s service before reboot", serviceName), e);
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
     }
 }
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
index 5c01e43..fd2d8e1 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.IntentSender;
 import android.content.pm.PackageManager;
+import android.hardware.boot.V1_0.IBootControl;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
 import android.os.Binder;
@@ -73,6 +74,8 @@
     static final String INIT_SERVICE_SETUP_BCB = "init.svc.setup-bcb";
     @VisibleForTesting
     static final String INIT_SERVICE_CLEAR_BCB = "init.svc.clear-bcb";
+    @VisibleForTesting
+    static final String AB_UPDATE = "ro.build.ab_update";
 
     private static final Object sRequestLock = new Object();
 
@@ -177,6 +180,25 @@
             return socket;
         }
 
+        /**
+         * Throws remote exception if there's an error getting the boot control HAL.
+         * Returns null if the boot control HAL's version is older than V1_2.
+         */
+        public android.hardware.boot.V1_2.IBootControl getBootControl() throws RemoteException {
+            IBootControl bootControlV10 = IBootControl.getService(true);
+            if (bootControlV10 == null) {
+                throw new RemoteException("Failed to get boot control HAL V1_0.");
+            }
+
+            android.hardware.boot.V1_2.IBootControl bootControlV12 =
+                    android.hardware.boot.V1_2.IBootControl.castFrom(bootControlV10);
+            if (bootControlV12 == null) {
+                Slog.w(TAG, "Device doesn't implement boot control HAL V1_2.");
+                return null;
+            }
+            return bootControlV12;
+        }
+
         public void threadSleep(long millis) throws InterruptedException {
             Thread.sleep(millis);
         }
@@ -476,6 +498,56 @@
         return needClear ? ROR_REQUESTED_NEED_CLEAR : ROR_REQUESTED_SKIP_CLEAR;
     }
 
+    private boolean isAbDevice() {
+        return "true".equalsIgnoreCase(mInjector.systemPropertiesGet(AB_UPDATE));
+    }
+
+    private boolean verifySlotForNextBoot(boolean slotSwitch) {
+        if (!isAbDevice()) {
+            Slog.w(TAG, "Device isn't a/b, skipping slot verification.");
+            return true;
+        }
+
+        android.hardware.boot.V1_2.IBootControl bootControl;
+        try {
+            bootControl = mInjector.getBootControl();
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to get the boot control HAL " + e);
+            return false;
+        }
+
+        // TODO(xunchang) enforce boot control V1_2 HAL on devices using multi client RoR
+        if (bootControl == null) {
+            Slog.w(TAG, "Cannot get the boot control HAL, skipping slot verification.");
+            return true;
+        }
+
+        int current_slot;
+        int next_active_slot;
+        try {
+            current_slot = bootControl.getCurrentSlot();
+            if (current_slot != 0 && current_slot != 1) {
+                throw new IllegalStateException("Current boot slot should be 0 or 1, got "
+                        + current_slot);
+            }
+            next_active_slot = bootControl.getActiveBootSlot();
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to query the active slots", e);
+            return false;
+        }
+
+        int expected_active_slot = current_slot;
+        if (slotSwitch) {
+            expected_active_slot = current_slot == 0 ? 1 : 0;
+        }
+        if (next_active_slot != expected_active_slot) {
+            Slog.w(TAG, "The next active boot slot doesn't match the expected value, "
+                    + "expected " + expected_active_slot + ", got " + next_active_slot);
+            return false;
+        }
+        return true;
+    }
+
     private boolean rebootWithLskfImpl(String packageName, String reason, boolean slotSwitch) {
         if (packageName == null) {
             Slog.w(TAG, "Missing packageName when rebooting with lskf.");
@@ -485,7 +557,10 @@
             return false;
         }
 
-        // TODO(xunchang) check the slot to boot into, and fail the reboot upon slot mismatch.
+        if (!verifySlotForNextBoot(slotSwitch)) {
+            return false;
+        }
+
         // TODO(xunchang) write the vbmeta digest along with the escrowKey before reboot.
         if (!mInjector.getLockSettingsService().armRebootEscrow()) {
             Slog.w(TAG, "Failure to escrow key for reboot");
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
index f20d80d..ae71c1a 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
@@ -76,7 +76,7 @@
     private int rebootAndApply() throws RemoteException {
         String packageName = getNextArgRequired();
         String rebootReason = getNextArgRequired();
-        boolean success = mService.rebootWithLskf(packageName, rebootReason, true);
+        boolean success = mService.rebootWithLskf(packageName, rebootReason, false);
         PrintWriter pw = getOutPrintWriter();
         // Keep the old message for cts test.
         pw.printf("%s Reboot and apply status: %s\n", packageName,
diff --git a/services/core/java/com/android/server/security/FileIntegrityService.java b/services/core/java/com/android/server/security/FileIntegrityService.java
index 225bd82..6ec71b7 100644
--- a/services/core/java/com/android/server/security/FileIntegrityService.java
+++ b/services/core/java/com/android/server/security/FileIntegrityService.java
@@ -60,8 +60,7 @@
     private final IBinder mService = new IFileIntegrityService.Stub() {
         @Override
         public boolean isApkVeritySupported() {
-            return Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.R
-                    || SystemProperties.getInt("ro.apk_verity.mode", 0) == 2;
+            return FileIntegrityService.isApkVeritySupported();
         }
 
         @Override
@@ -111,6 +110,11 @@
         }
     };
 
+    public static boolean isApkVeritySupported() {
+        return Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.R
+                || SystemProperties.getInt("ro.apk_verity.mode", 0) == 2;
+    }
+
     public FileIntegrityService(final Context context) {
         super(context);
         try {
diff --git a/services/core/java/com/android/server/security/OWNERS b/services/core/java/com/android/server/security/OWNERS
new file mode 100644
index 0000000..91b240b
--- /dev/null
+++ b/services/core/java/com/android/server/security/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 36824
+
+per-file FileIntegrityService.java = victorhsieh@google.com
+per-file VerityUtils.java = victorhsieh@google.com
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index f204aa2..09ee001 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -73,7 +73,12 @@
         if (Files.size(Paths.get(signaturePath)) > MAX_SIGNATURE_FILE_SIZE_BYTES) {
             throw new SecurityException("Signature file is unexpectedly large: " + signaturePath);
         }
-        byte[] pkcs7Signature = Files.readAllBytes(Paths.get(signaturePath));
+        setUpFsverity(filePath, Files.readAllBytes(Paths.get(signaturePath)));
+    }
+
+    /** Enables fs-verity for the file with a PKCS#7 detached signature bytes. */
+    public static void setUpFsverity(@NonNull String filePath, @NonNull byte[] pkcs7Signature)
+            throws IOException {
         // This will fail if the public key is not already in .fs-verity kernel keyring.
         int errno = enableFsverityNative(filePath, pkcs7Signature);
         if (errno != 0) {
diff --git a/services/core/java/com/android/server/textservices/TextServicesManagerService.java b/services/core/java/com/android/server/textservices/TextServicesManagerService.java
index f39067b..cd2b8943 100644
--- a/services/core/java/com/android/server/textservices/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/textservices/TextServicesManagerService.java
@@ -45,6 +45,7 @@
 import android.util.SparseArray;
 import android.view.textservice.SpellCheckerInfo;
 import android.view.textservice.SpellCheckerSubtype;
+import android.view.textservice.SuggestionsInfo;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
@@ -58,7 +59,6 @@
 import com.android.internal.util.DumpUtils;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -567,7 +567,7 @@
     @Override
     public void getSpellCheckerService(@UserIdInt int userId, String sciId, String locale,
             ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener,
-            Bundle bundle) {
+            Bundle bundle, @SuggestionsInfo.ResultAttrs int supportedAttributes) {
         verifyUser(userId);
         if (TextUtils.isEmpty(sciId) || tsListener == null || scListener == null) {
             Slog.e(TAG, "getSpellCheckerService: Invalid input.");
@@ -603,7 +603,8 @@
             // Start getISpellCheckerSession async IPC, or just queue the request until the spell
             // checker service is bound.
             bindGroup.getISpellCheckerSessionOrQueueLocked(
-                    new SessionRequest(uid, locale, tsListener, scListener, bundle));
+                    new SessionRequest(uid, locale, tsListener, scListener, bundle,
+                            supportedAttributes));
         }
     }
 
@@ -774,15 +775,18 @@
         public final ISpellCheckerSessionListener mScListener;
         @Nullable
         public final Bundle mBundle;
+        public final int mSupportedAttributes;
 
         SessionRequest(int uid, @Nullable String locale,
                 @NonNull ITextServicesSessionListener tsListener,
-                @NonNull ISpellCheckerSessionListener scListener, @Nullable Bundle bundle) {
+                @NonNull ISpellCheckerSessionListener scListener, @Nullable Bundle bundle,
+                @SuggestionsInfo.ResultAttrs int supportedAttributes) {
             mUid = uid;
             mLocale = locale;
             mTsListener = tsListener;
             mScListener = scListener;
             mBundle = bundle;
+            mSupportedAttributes = supportedAttributes;
         }
     }
 
@@ -825,6 +829,7 @@
                     final SessionRequest request = mPendingSessionRequests.get(i);
                     mSpellChecker.getISpellCheckerSession(
                             request.mLocale, request.mScListener, request.mBundle,
+                            request.mSupportedAttributes,
                             new ISpellCheckerServiceCallbackBinder(this, request));
                     mOnGoingSessionRequests.add(request);
                 }
@@ -913,6 +918,7 @@
             try {
                 mSpellChecker.getISpellCheckerSession(
                         request.mLocale, request.mScListener, request.mBundle,
+                        request.mSupportedAttributes,
                         new ISpellCheckerServiceCallbackBinder(this, request));
                 mOnGoingSessionRequests.add(request);
             } catch(RemoteException e) {
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 7024e67..4e0c0c5 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -123,7 +123,7 @@
     private static final String DISCONNECT_REASON_UNDERLYING_NETWORK_LOST =
             "Underlying Network lost";
     private static final String DISCONNECT_REASON_TEARDOWN = "teardown() called on VcnTunnel";
-    private static final int TOKEN_ANY = Integer.MIN_VALUE;
+    private static final int TOKEN_ALL = Integer.MIN_VALUE;
 
     private static final int NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS = 30;
     private static final int TEARDOWN_TIMEOUT_SECONDS = 5;
@@ -139,7 +139,7 @@
      *
      * <p>In the Connected state, this MAY indicate a mobility even occurred.
      *
-     * @param arg1 The "any" token; this event is always applicable.
+     * @param arg1 The "all" token; this event is always applicable.
      * @param obj @NonNull An EventUnderlyingNetworkChangedInfo instance with relevant data.
      */
     private static final int EVENT_UNDERLYING_NETWORK_CHANGED = 1;
@@ -175,7 +175,7 @@
      * <p>Upon receipt of this signal, the state machine will transition from the Retry-timeout
      * state to the Connecting state.
      *
-     * @param arg1 The "any" token; no sessions are active in the RetryTimeoutState.
+     * @param arg1 The "all" token; no sessions are active in the RetryTimeoutState.
      */
     private static final int EVENT_RETRY_TIMEOUT_EXPIRED = 2;
 
@@ -318,7 +318,7 @@
      * <p>Upon receipt of this signal, the state machine MUST tear down all active sessions, cancel
      * any pending work items, and move to the Disconnected state.
      *
-     * @param arg1 The "any" token; this signal is always honored.
+     * @param arg1 The "all" token; this signal is always honored.
      * @param obj @NonNull An EventDisconnectRequestedInfo instance with relevant data.
      */
     private static final int EVENT_DISCONNECT_REQUESTED = 7;
@@ -504,16 +504,9 @@
      * <p>Once torn down, this VcnTunnel CANNOT be started again.
      */
     public void teardownAsynchronously() {
-        mUnderlyingNetworkTracker.teardown();
-
-        // No need to call setInterfaceDown(); the IpSecInterface is being fully torn down.
-        if (mTunnelIface != null) {
-            mTunnelIface.close();
-        }
-
         sendMessage(
                 EVENT_DISCONNECT_REQUESTED,
-                TOKEN_ANY,
+                TOKEN_ALL,
                 new EventDisconnectRequestedInfo(DISCONNECT_REASON_TEARDOWN));
         quit();
 
@@ -521,6 +514,16 @@
         // is also called asynchronously when a NetworkAgent becomes unwanted
     }
 
+    @Override
+    protected void onQuitting() {
+        // No need to call setInterfaceDown(); the IpSecInterface is being fully torn down.
+        if (mTunnelIface != null) {
+            mTunnelIface.close();
+        }
+
+        mUnderlyingNetworkTracker.teardown();
+    }
+
     private class VcnUnderlyingNetworkTrackerCallback implements UnderlyingNetworkTrackerCallback {
         @Override
         public void onSelectedUnderlyingNetworkChanged(
@@ -530,26 +533,24 @@
             if (underlying == null) {
                 sendMessageDelayed(
                         EVENT_DISCONNECT_REQUESTED,
-                        TOKEN_ANY,
+                        TOKEN_ALL,
                         new EventDisconnectRequestedInfo(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST),
                         TimeUnit.SECONDS.toMillis(NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS));
-                return;
-            }
+            } else if (getHandler() != null) {
+                // Cancel any existing disconnect due to loss of underlying network
+                // getHandler() can return null if the state machine has already quit. Since this is
+                // called from other classes, this condition must be verified.
 
-            // Cancel any existing disconnect due to loss of underlying network
-            // getHandler() can return null if the state machine has already quit. Since this is
-            // called
-            // from other classes, this condition must be verified.
-            if (getHandler() != null) {
                 getHandler()
                         .removeEqualMessages(
                                 EVENT_DISCONNECT_REQUESTED,
                                 new EventDisconnectRequestedInfo(
                                         DISCONNECT_REASON_UNDERLYING_NETWORK_LOST));
             }
+
             sendMessage(
                     EVENT_UNDERLYING_NETWORK_CHANGED,
-                    TOKEN_ANY,
+                    TOKEN_ALL,
                     new EventUnderlyingNetworkChangedInfo(underlying));
         }
     }
@@ -594,10 +595,101 @@
     }
 
     private abstract class BaseState extends State {
+        @Override
+        public void enter() {
+            try {
+                enterState();
+            } catch (Exception e) {
+                Slog.wtf(TAG, "Uncaught exception", e);
+                sendMessage(
+                        EVENT_DISCONNECT_REQUESTED,
+                        TOKEN_ALL,
+                        new EventDisconnectRequestedInfo(
+                                DISCONNECT_REASON_INTERNAL_ERROR + e.toString()));
+            }
+        }
+
         protected void enterState() throws Exception {}
 
+        /**
+         * Top-level processMessage with safeguards to prevent crashing the System Server on non-eng
+         * builds.
+         */
+        @Override
+        public boolean processMessage(Message msg) {
+            try {
+                processStateMsg(msg);
+            } catch (Exception e) {
+                Slog.wtf(TAG, "Uncaught exception", e);
+                sendMessage(
+                        EVENT_DISCONNECT_REQUESTED,
+                        TOKEN_ALL,
+                        new EventDisconnectRequestedInfo(
+                                DISCONNECT_REASON_INTERNAL_ERROR + e.toString()));
+            }
+
+            return HANDLED;
+        }
+
         protected abstract void processStateMsg(Message msg) throws Exception;
+
+        protected void logUnhandledMessage(Message msg) {
+            // Log as unexpected all known messages, and log all else as unknown.
+            switch (msg.what) {
+                case EVENT_UNDERLYING_NETWORK_CHANGED: // Fallthrough
+                case EVENT_RETRY_TIMEOUT_EXPIRED: // Fallthrough
+                case EVENT_SESSION_LOST: // Fallthrough
+                case EVENT_SESSION_CLOSED: // Fallthrough
+                case EVENT_TRANSFORM_CREATED: // Fallthrough
+                case EVENT_SETUP_COMPLETED: // Fallthrough
+                case EVENT_DISCONNECT_REQUESTED: // Fallthrough
+                case EVENT_TEARDOWN_TIMEOUT_EXPIRED:
+                    logUnexpectedEvent(msg.what);
+                    break;
+                default:
+                    logWtfUnknownEvent(msg.what);
+                    break;
+            }
+        }
+
+        protected void teardownNetwork() {
+            if (mNetworkAgent != null) {
+                mNetworkAgent.sendNetworkInfo(buildNetworkInfo(false /* isConnected */));
+                mNetworkAgent = null;
+            }
+        }
+
+        protected void teardownIke() {
+            if (mIkeSession != null) {
+                mIkeSession.close();
+            }
+        }
+
+        protected void handleDisconnectRequested(String msg) {
+            Slog.v(TAG, "Tearing down. Cause: " + msg);
+            teardownNetwork();
+            teardownIke();
+
+            if (mIkeSession == null) {
+                // Already disconnected, go straight to DisconnectedState
+                transitionTo(mDisconnectedState);
+            } else {
+                // Still need to wait for full closure
+                transitionTo(mDisconnectingState);
+            }
+        }
+
+        protected void logUnexpectedEvent(int what) {
+            Slog.d(TAG, String.format(
+                    "Unexpected event code %d in state %s", what, this.getClass().getSimpleName()));
+        }
+
+        protected void logWtfUnknownEvent(int what) {
+            Slog.wtf(TAG, String.format(
+                    "Unknown event code %d in state %s", what, this.getClass().getSimpleName()));
+        }
     }
+
     /**
      * State representing the a disconnected VCN tunnel.
      *
@@ -608,7 +700,29 @@
         protected void processStateMsg(Message msg) {}
     }
 
-    private abstract class ActiveBaseState extends BaseState {}
+    private abstract class ActiveBaseState extends BaseState {
+        /**
+         * Handles all incoming messages, discarding messages for previous networks.
+         *
+         * <p>States that handle mobility events may need to override this method to receive
+         * messages for all underlying networks.
+         */
+        @Override
+        public boolean processMessage(Message msg) {
+            final int token = msg.arg1;
+            // Only process if a valid token is presented.
+            if (isValidToken(token)) {
+                return super.processMessage(msg);
+            }
+
+            Slog.v(TAG, "Message called with obsolete token: " + token + "; what: " + msg.what);
+            return HANDLED;
+        }
+
+        protected boolean isValidToken(int token) {
+            return (token == TOKEN_ALL || token == mCurrentToken);
+        }
+    }
 
     /**
      * Transitive state representing a VCN that is tearing down an IKE session.
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index a4d888b..c36375e 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -39,8 +39,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.PriorityQueue;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 
 /** Plays a {@link Vibration} in dedicated thread. */
 // TODO(b/159207608): Make this package-private once vibrator services are moved to this package
@@ -76,7 +74,6 @@
         void onVibrationEnded(long vibrationId, Vibration.Status status);
     }
 
-    private final Object mLock = new Object();
     private final WorkSource mWorkSource = new WorkSource();
     private final PowerManager.WakeLock mWakeLock;
     private final IBatteryStats mBatteryStatsService;
@@ -84,7 +81,7 @@
     private final VibrationCallbacks mCallbacks;
     private final SparseArray<VibratorController> mVibrators;
 
-    @GuardedBy("mLock")
+    @GuardedBy("this")
     @Nullable
     private VibrateStep mCurrentVibrateStep;
     @GuardedBy("this")
@@ -147,7 +144,7 @@
 
     /** Notify current vibration that a step has completed on given vibrator. */
     public void vibratorComplete(int vibratorId) {
-        synchronized (mLock) {
+        synchronized (this) {
             if (mCurrentVibrateStep != null) {
                 mCurrentVibrateStep.vibratorComplete(vibratorId);
             }
@@ -171,7 +168,7 @@
             final int stepCount = steps.size();
             for (int i = 0; i < stepCount; i++) {
                 Step step = steps.get(i);
-                synchronized (mLock) {
+                synchronized (this) {
                     if (step instanceof VibrateStep) {
                         mCurrentVibrateStep = (VibrateStep) step;
                     } else {
@@ -315,27 +312,6 @@
         }
     }
 
-    /**
-     * Sleeps until given {@link CountDownLatch} has finished or {@code wakeUpTime} was reached.
-     *
-     * <p>This stops immediately when {@link #cancel()} is called.
-     */
-    private void awaitUntil(CountDownLatch counter, long wakeUpTime) {
-        synchronized (this) {
-            long durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
-            while (counter.getCount() > 0 && durationRemaining > 0) {
-                try {
-                    counter.await(durationRemaining, TimeUnit.MILLISECONDS);
-                } catch (InterruptedException e) {
-                }
-                if (mForceStop) {
-                    break;
-                }
-                durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
-            }
-        }
-    }
-
     private void noteVibratorOn(long duration) {
         try {
             mBatteryStatsService.noteVibratorOn(mVibration.uid, duration);
@@ -371,12 +347,10 @@
     private final class SingleVibrateStep implements VibrateStep {
         private final VibratorController mVibrator;
         private final VibrationEffect mEffect;
-        private final CountDownLatch mCounter;
 
         SingleVibrateStep(VibratorController vibrator, VibrationEffect effect) {
             mVibrator = vibrator;
             mEffect = effect;
-            mCounter = new CountDownLatch(1);
         }
 
         @Override
@@ -390,7 +364,9 @@
                 return;
             }
             mVibrator.off();
-            mCounter.countDown();
+            synchronized (VibrationThread.this) {
+                VibrationThread.this.notify();
+            }
         }
 
         @Override
@@ -408,7 +384,11 @@
                     noteVibratorOn(duration);
                     // Vibration is playing with no need to control amplitudes, just wait for native
                     // callback or timeout.
-                    awaitUntil(mCounter, startTime + duration + CALLBACKS_EXTRA_TIMEOUT);
+                    waitUntil(startTime + duration + CALLBACKS_EXTRA_TIMEOUT);
+                    if (mForceStop) {
+                        mVibrator.off();
+                        return Vibration.Status.CANCELLED;
+                    }
                     return Vibration.Status.FINISHED;
                 }
 
@@ -499,14 +479,15 @@
     /** Represent a synchronized vibration step on multiple vibrators. */
     private final class SyncedVibrateStep implements VibrateStep {
         private final SparseArray<VibrationEffect> mEffects;
-        private final CountDownLatch mActiveVibratorCounter;
-
         private final int mRequiredCapabilities;
         private final int[] mVibratorIds;
 
+        @GuardedBy("VibrationThread.this")
+        private int mActiveVibratorCounter;
+
         SyncedVibrateStep(SparseArray<VibrationEffect> effects) {
             mEffects = effects;
-            mActiveVibratorCounter = new CountDownLatch(mEffects.size());
+            mActiveVibratorCounter = mEffects.size();
             // TODO(b/159207608): Calculate required capabilities for syncing this step.
             mRequiredCapabilities = 0;
             mVibratorIds = new int[effects.size()];
@@ -527,7 +508,11 @@
                 return;
             }
             mVibrators.get(vibratorId).off();
-            mActiveVibratorCounter.countDown();
+            synchronized (VibrationThread.this) {
+                if (--mActiveVibratorCounter <= 0) {
+                    VibrationThread.this.notify();
+                }
+            }
         }
 
         @Override
@@ -556,7 +541,9 @@
                     AmplitudeStep nextStep = step.nextStep();
                     if (nextStep == null) {
                         // This vibrator has finished playing the effect for this step.
-                        mActiveVibratorCounter.countDown();
+                        synchronized (VibrationThread.this) {
+                            mActiveVibratorCounter--;
+                        }
                     } else {
                         nextSteps.add(nextStep);
                     }
@@ -564,7 +551,16 @@
 
                 // All OneShot and Waveform effects have finished. Just wait for the other effects
                 // to end via native callbacks before finishing this synced step.
-                awaitUntil(mActiveVibratorCounter, startTime + timeout + CALLBACKS_EXTRA_TIMEOUT);
+                synchronized (VibrationThread.this) {
+                    if (mActiveVibratorCounter > 0) {
+                        waitUntil(startTime + timeout + CALLBACKS_EXTRA_TIMEOUT);
+                    }
+                }
+                if (mForceStop) {
+                    stopAllVibrators();
+                    return Vibration.Status.CANCELLED;
+                }
+
                 return Vibration.Status.FINISHED;
             } finally {
                 if (timeout > 0) {
@@ -779,7 +775,7 @@
                     Slog.d(TAG, "DelayStep of " + mDelay + "ms starting...");
                 }
                 waitUntil(SystemClock.uptimeMillis() + mDelay);
-                return Vibration.Status.FINISHED;
+                return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
             } finally {
                 if (DEBUG) {
                     Slog.d(TAG, "DelayStep done.");
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 53f52e2..9dcf12c 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -272,12 +272,18 @@
             return 0;
         }
         synchronized (mLock) {
-            mNativeWrapper.compose(effect.getPrimitiveEffects().toArray(
-                    new VibrationEffect.Composition.PrimitiveEffect[0]), vibrationId);
+            VibrationEffect.Composition.PrimitiveEffect[] primitives =
+                    effect.getPrimitiveEffects().toArray(
+                            new VibrationEffect.Composition.PrimitiveEffect[0]);
+            mNativeWrapper.compose(primitives, vibrationId);
             notifyVibratorOnLocked();
             // Compose don't actually give us an estimated duration, so we just guess here.
-            // TODO(b/177807015): use exposed durations from IVibrator here instead
-            return 20 * effect.getPrimitiveEffects().size();
+            long duration = 0;
+            for (VibrationEffect.Composition.PrimitiveEffect primitive : primitives) {
+                // TODO(b/177807015): use exposed durations from IVibrator here instead
+                duration += 20 + primitive.delay;
+            }
+            return duration;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0f4bb4c..ba0292df 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2749,8 +2749,7 @@
 
             if (ensureVisibility) {
                 mDisplayContent.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
-                        false /* preserveWindows */, true /* notifyClients */,
-                        mTaskSupervisor.mUserLeaving);
+                        false /* preserveWindows */, true /* notifyClients */);
             }
         }
 
@@ -4823,7 +4822,7 @@
         handleAlreadyVisible();
     }
 
-    void makeInvisible(boolean userLeaving) {
+    void makeInvisible() {
         if (!mVisibleRequested) {
             if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + this);
             return;
@@ -4864,7 +4863,7 @@
                     // If the app is capable of entering PIP, we should try pausing it now
                     // so it can PIP correctly.
                     if (deferHidingClient) {
-                        task.startPausingLocked(userLeaving, false /* uiSleeping */,
+                        task.startPausingLocked(false /* uiSleeping */,
                                 null /* resuming */, "makeInvisible");
                         break;
                     }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index e67210e..683bb08 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1588,6 +1588,7 @@
             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
             startedActivityStack = handleStartResult(r, result);
             mService.continueWindowLayout();
+            mSupervisor.mUserLeaving = false;
 
             // Transition housekeeping
             if (!ActivityManager.isStartResultSuccessful(result)) {
@@ -2340,6 +2341,7 @@
                 mDoResume = false;
                 mAvoidMoveToFront = true;
             }
+            mTargetRootTask = Task.fromWindowContainerToken(mOptions.getLaunchRootTask());
         }
 
         mNotTop = (mLaunchFlags & FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? sourceRecord : null;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index b332739..efae16b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1940,7 +1940,7 @@
                 } else {
                     stack.setWindowingMode(windowingMode);
                     stack.mDisplayContent.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
-                            true /* notifyClients */, mTaskSupervisor.mUserLeaving);
+                            true /* notifyClients */);
                 }
                 return true;
             } finally {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index a68f5575..d2ce369 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1361,53 +1361,58 @@
             return;
         }
 
-        if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
-            mUserLeaving = true;
-        }
-
-        mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_TO_FRONT,
-                0 /* flags */, task, options != null ? options.getRemoteTransition() : null);
-        reason = reason + " findTaskToMoveToFront";
-        boolean reparented = false;
-        if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
-            final Rect bounds = options.getLaunchBounds();
-            task.setBounds(bounds);
-
-            Task stack =
-                    mRootWindowContainer.getLaunchRootTask(null, options, task, ON_TOP);
-
-            if (stack != currentStack) {
-                moveHomeRootTaskToFrontIfNeeded(flags, stack.getDisplayArea(), reason);
-                task.reparent(stack, ON_TOP, REPARENT_KEEP_ROOT_TASK_AT_FRONT, !ANIMATE,
-                        DEFER_RESUME, reason);
-                currentStack = stack;
-                reparented = true;
-                // task.reparent() should already placed the task on top,
-                // still need moveTaskToFrontLocked() below for any transition settings.
+        try {
+            if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
+                mUserLeaving = true;
             }
-            if (stack.shouldResizeRootTaskWithLaunchBounds()) {
-                stack.resize(bounds, !PRESERVE_WINDOWS, !DEFER_RESUME);
-            } else {
-                // WM resizeTask must be done after the task is moved to the correct stack,
-                // because Task's setBounds() also updates dim layer's bounds, but that has
-                // dependency on the stack.
-                task.resize(false /* relayout */, false /* forced */);
+
+            mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_TO_FRONT,
+                    0 /* flags */, task, options != null ? options.getRemoteTransition() : null);
+            reason = reason + " findTaskToMoveToFront";
+            boolean reparented = false;
+            if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
+                final Rect bounds = options.getLaunchBounds();
+                task.setBounds(bounds);
+
+                Task stack =
+                        mRootWindowContainer.getLaunchRootTask(null, options, task, ON_TOP);
+
+                if (stack != currentStack) {
+                    moveHomeRootTaskToFrontIfNeeded(flags, stack.getDisplayArea(), reason);
+                    task.reparent(stack, ON_TOP, REPARENT_KEEP_ROOT_TASK_AT_FRONT, !ANIMATE,
+                            DEFER_RESUME, reason);
+                    currentStack = stack;
+                    reparented = true;
+                    // task.reparent() should already placed the task on top,
+                    // still need moveTaskToFrontLocked() below for any transition settings.
+                }
+                if (stack.shouldResizeRootTaskWithLaunchBounds()) {
+                    stack.resize(bounds, !PRESERVE_WINDOWS, !DEFER_RESUME);
+                } else {
+                    // WM resizeTask must be done after the task is moved to the correct stack,
+                    // because Task's setBounds() also updates dim layer's bounds, but that has
+                    // dependency on the stack.
+                    task.resize(false /* relayout */, false /* forced */);
+                }
             }
+
+            if (!reparented) {
+                moveHomeRootTaskToFrontIfNeeded(flags, currentStack.getDisplayArea(), reason);
+            }
+
+            final ActivityRecord r = task.getTopNonFinishingActivity();
+            currentStack.moveTaskToFront(task, false /* noAnimation */, options,
+                    r == null ? null : r.appTimeTracker, reason);
+
+            if (DEBUG_ROOT_TASK) Slog.d(TAG_ROOT_TASK,
+                    "findTaskToMoveToFront: moved to front of stack=" + currentStack);
+
+            handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED,
+                    mRootWindowContainer.getDefaultTaskDisplayArea(), currentStack,
+                    forceNonResizeable);
+        } finally {
+            mUserLeaving = false;
         }
-
-        if (!reparented) {
-            moveHomeRootTaskToFrontIfNeeded(flags, currentStack.getDisplayArea(), reason);
-        }
-
-        final ActivityRecord r = task.getTopNonFinishingActivity();
-        currentStack.moveTaskToFront(task, false /* noAnimation */, options,
-                r == null ? null : r.appTimeTracker, reason);
-
-        if (DEBUG_ROOT_TASK) Slog.d(TAG_ROOT_TASK,
-                "findTaskToMoveToFront: moved to front of stack=" + currentStack);
-
-        handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED,
-                mRootWindowContainer.getDefaultTaskDisplayArea(), currentStack, forceNonResizeable);
     }
 
     private void moveHomeRootTaskToFrontIfNeeded(int flags, TaskDisplayArea taskDisplayArea,
@@ -2209,7 +2214,7 @@
                         .notifyActivityDismissingDockedStack();
                 taskDisplayArea.onSplitScreenModeDismissed(task);
                 taskDisplayArea.mDisplayContent.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
-                        true /* notifyClients */, mUserLeaving);
+                        true /* notifyClients */);
             }
             return;
         }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9769244..b7970cd 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1559,6 +1559,13 @@
         }
         final int rotation = rotationForActivityInDifferentOrientation(r);
         if (rotation == ROTATION_UNDEFINED) {
+            // The display rotation won't be changed by current top activity. If there was fixed
+            // rotation activity, its rotated state should be cleared to cancel the adjustments.
+            if (hasTopFixedRotationLaunchingApp()
+                    // Avoid breaking recents animation.
+                    && !mFixedRotationLaunchingApp.getTask().isAnimatingByRecents()) {
+                clearFixedRotationLaunchingApp();
+            }
             return false;
         }
         if (!r.getParent().matchParentBounds()) {
@@ -5536,7 +5543,7 @@
 
 
     void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
-            boolean preserveWindows, boolean notifyClients, boolean userLeaving) {
+            boolean preserveWindows, boolean notifyClients) {
         if (mInEnsureActivitiesVisible) {
             // Don't do recursive work.
             return;
@@ -5546,7 +5553,7 @@
         try {
             forAllRootTasks(rootTask -> {
                 rootTask.ensureActivitiesVisible(starting, configChanges, preserveWindows,
-                        notifyClients, userLeaving);
+                        notifyClients);
             });
         } finally {
             mAtmService.mTaskSupervisor.endActivityVisibilityUpdate();
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index 4581024..74264d3 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -33,7 +33,6 @@
     private int mConfigChanges;
     private boolean mPreserveWindows;
     private boolean mNotifyClients;
-    private boolean mUserLeaving;
 
     EnsureActivitiesVisibleHelper(Task container) {
         mTask = container;
@@ -50,7 +49,7 @@
      *                      be sent to the clients.
      */
     void reset(ActivityRecord starting, int configChanges, boolean preserveWindows,
-            boolean notifyClients, boolean userLeaving) {
+            boolean notifyClients) {
         mStarting = starting;
         mTop = mTask.topRunningActivity();
         // If the top activity is not fullscreen, then we need to make sure any activities under it
@@ -61,7 +60,6 @@
         mConfigChanges = configChanges;
         mPreserveWindows = preserveWindows;
         mNotifyClients = notifyClients;
-        mUserLeaving = userLeaving;
     }
 
     /**
@@ -78,12 +76,10 @@
      * @param preserveWindows Flag indicating whether windows should be preserved when updating.
      * @param notifyClients Flag indicating whether the configuration and visibility changes shoulc
      *                      be sent to the clients.
-     * @param userLeaving Flag indicating whether a userLeaving callback should be issued in the
-     *                      case the activity is being set to invisible.
      */
     void process(@Nullable ActivityRecord starting, int configChanges, boolean preserveWindows,
-            boolean notifyClients, boolean userLeaving) {
-        reset(starting, configChanges, preserveWindows, notifyClients, userLeaving);
+            boolean notifyClients) {
+        reset(starting, configChanges, preserveWindows, notifyClients);
 
         if (DEBUG_VISIBILITY) {
             Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + mTop
@@ -177,7 +173,7 @@
                         + " behindFullscreenActivity=" + mBehindFullscreenActivity
                         + " mLaunchTaskBehind=" + r.mLaunchTaskBehind);
             }
-            r.makeInvisible(mUserLeaving);
+            r.makeInvisible();
         }
 
         if (!mBehindFullscreenActivity && mTask.isActivityTypeHome() && r.isRootOfTask()) {
diff --git a/services/core/java/com/android/server/wm/ImpressionAttestationController.java b/services/core/java/com/android/server/wm/ImpressionAttestationController.java
index b0afc57..bad6c80 100644
--- a/services/core/java/com/android/server/wm/ImpressionAttestationController.java
+++ b/services/core/java/com/android/server/wm/ImpressionAttestationController.java
@@ -86,7 +86,7 @@
 
     private final Handler mHandler;
 
-    private final String mSalt;
+    private final byte[] mSalt;
 
     private final float[] mTmpFloat9 = new float[9];
     private final Matrix mTmpMatrix = new Matrix();
@@ -99,7 +99,7 @@
     ImpressionAttestationController(Context context) {
         mContext = context;
         mHandler = new Handler(Looper.getMainLooper());
-        mSalt = UUID.randomUUID().toString();
+        mSalt = UUID.randomUUID().toString().getBytes();
     }
 
     String[] getSupportedImpressionAlgorithms() {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 7d1da5a..c575ca3 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -392,6 +392,7 @@
                     Slog.e(TAG, "Failed to clean up recents activity", e);
                     throw e;
                 } finally {
+                    mTaskSupervisor.mUserLeaving = false;
                     mService.continueWindowLayout();
                     // Make sure the surfaces are updated with the latest state. Sometimes the
                     // surface placement may be skipped if display configuration is changed (i.e.
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d8b1e18..4300aed 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1796,8 +1796,7 @@
         // Passing null here for 'starting' param value, so that visibility of actual starting
         // activity will be properly updated.
         ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
-                false /* preserveWindows */, false /* notifyClients */,
-                mTaskSupervisor.mUserLeaving);
+                false /* preserveWindows */, false /* notifyClients */);
 
         if (displayId == INVALID_DISPLAY) {
             // The caller didn't provide a valid display id, skip updating config.
@@ -1973,15 +1972,14 @@
      */
     void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
             boolean preserveWindows) {
-        ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */,
-                mTaskSupervisor.mUserLeaving);
+        ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */);
     }
 
     /**
      * @see #ensureActivitiesVisible(ActivityRecord, int, boolean)
      */
     void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
-            boolean preserveWindows, boolean notifyClients, boolean userLeaving) {
+            boolean preserveWindows, boolean notifyClients) {
         if (mTaskSupervisor.inActivityVisibilityUpdate()) {
             // Don't do recursive work.
             return;
@@ -1993,7 +1991,7 @@
             for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
                 final DisplayContent display = getChildAt(displayNdx);
                 display.ensureActivitiesVisible(starting, configChanges, preserveWindows,
-                        notifyClients, userLeaving);
+                        notifyClients);
             }
         } finally {
             mTaskSupervisor.endActivityVisibilityUpdate();
@@ -2858,6 +2856,11 @@
             final WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
             taskDisplayArea = daToken != null
                     ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
+
+            final Task rootTask = Task.fromWindowContainerToken(options.getLaunchRootTask());
+            if (rootTask != null) {
+                return rootTask;
+            }
         }
 
         // First preference for stack goes to the task Id set in the activity options. Use the stack
@@ -3032,7 +3035,8 @@
             final int activityType =
                     options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED
                             ? options.getLaunchActivityType() : r.getActivityType();
-            return taskDisplayArea.createRootTask(windowingMode, activityType, true /*onTop*/);
+            return taskDisplayArea.createRootTask(
+                    windowingMode, activityType, true /*onTop*/, options);
         }
 
         return null;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2295ee3..fb50fa1e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -209,6 +209,7 @@
 import android.window.ITaskOrganizer;
 import android.window.StartingWindowInfo;
 import android.window.TaskSnapshot;
+import android.window.WindowContainerToken;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -880,6 +881,11 @@
         EventLogTags.writeWmTaskCreated(mTaskId, isRootTask() ? INVALID_TASK_ID : getRootTaskId());
     }
 
+    static Task fromWindowContainerToken(WindowContainerToken token) {
+        if (token == null) return null;
+        return fromBinder(token.asBinder()).asTask();
+    }
+
     Task reuseAsLeafTask(IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
             Intent intent, ActivityInfo info, ActivityRecord activity) {
         voiceSession = _voiceSession;
@@ -5600,6 +5606,10 @@
         return false;
     }
 
+    final boolean startPausingLocked(boolean uiSleeping, ActivityRecord resuming, String reason) {
+        return startPausingLocked(mTaskSupervisor.mUserLeaving, uiSleeping, resuming, reason);
+    }
+
     /**
      * Start pausing the currently resumed activity.  It is an error to call this if there
      * is already an activity being paused or there is no resumed activity.
@@ -5864,8 +5874,7 @@
      */
     void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
             boolean preserveWindows) {
-        ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */,
-                mTaskSupervisor.mUserLeaving);
+        ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */);
     }
 
     /**
@@ -5882,16 +5891,14 @@
      * @param configChanges Parts of the configuration that changed for this activity for evaluating
      *                      if the screen should be frozen as part of
      *                      {@link mEnsureActivitiesVisibleHelper}.
-     * @param userLeaving Flag indicating whether a userLeaving callback should be issued in the
-     *                      case an activity is being set to invisible.
      */
     // TODO: Should be re-worked based on the fact that each task as a stack in most cases.
     void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
-            boolean preserveWindows, boolean notifyClients, boolean userLeaving) {
+            boolean preserveWindows, boolean notifyClients) {
         mTaskSupervisor.beginActivityVisibilityUpdate();
         try {
             forAllLeafTasks(task -> task.mEnsureActivitiesVisibleHelper.process(
-                    starting, configChanges, preserveWindows, notifyClients, userLeaving),
+                    starting, configChanges, preserveWindows, notifyClients),
                     true /* traverseTopToBottom */);
 
             // Notify WM shell that task visibilities may have changed
@@ -6087,11 +6094,6 @@
 
         mRootWindowContainer.cancelInitializingActivities();
 
-        // Remember how we'll process this pause/resume situation, and ensure
-        // that the state is reset however we wind up proceeding.
-        boolean userLeaving = mTaskSupervisor.mUserLeaving;
-        mTaskSupervisor.mUserLeaving = false;
-
         if (!hasRunningActivity) {
             // There are no activities left in the stack, let's look somewhere else.
             return resumeNextFocusableActivityWhenRootTaskIsEmpty(prev, options);
@@ -6111,7 +6113,7 @@
             // a fullscreen window forward to cover another freeform activity.)
             if (taskDisplayArea.inMultiWindowMode()) {
                 taskDisplayArea.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
-                        false /* preserveWindows */, true /* notifyClients */, userLeaving);
+                        false /* preserveWindows */, true /* notifyClients */);
             }
             ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Top activity "
                     + "resumed %s", next);
@@ -6176,19 +6178,12 @@
             // doesn't represent the last resumed activity. However, the last focus stack does if
             // it isn't null.
             lastResumed = lastFocusedRootTask.getResumedActivity();
-            if (userLeaving && inMultiWindowMode() && lastFocusedRootTask.shouldBeVisible(next)) {
-                // The user isn't leaving if this stack is the multi-window mode and the last
-                // focused stack should still be visible.
-                if(DEBUG_USER_LEAVING) Slog.i(TAG_USER_LEAVING, "Overriding userLeaving to false"
-                        + " next=" + next + " lastResumed=" + lastResumed);
-                userLeaving = false;
-            }
         }
 
-        boolean pausing = taskDisplayArea.pauseBackTasks(userLeaving, next);
+        boolean pausing = taskDisplayArea.pauseBackTasks(next);
         if (mResumedActivity != null) {
             ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Pausing %s", mResumedActivity);
-            pausing |= startPausingLocked(userLeaving, false /* uiSleeping */, next,
+            pausing |= startPausingLocked(false /* uiSleeping */, next,
                     "resumeTopActivityInnerLocked");
         }
         if (pausing) {
@@ -7380,6 +7375,7 @@
             task = new Task.Builder(mAtmService)
                     .setTaskId(taskId)
                     .setActivityInfo(info)
+                    .setActivityOptions(options)
                     .setIntent(intent)
                     .setVoiceSession(voiceSession)
                     .setVoiceInteractor(voiceInteractor)
@@ -7819,6 +7815,7 @@
         private int mMinWidth = INVALID_MIN_SIZE;
         private int mMinHeight = INVALID_MIN_SIZE;
         private ActivityInfo mActivityInfo;
+        private ActivityOptions mActivityOptions;
         private IVoiceInteractionSession mVoiceSession;
         private IVoiceInteractor mVoiceInteractor;
         private int mActivityType;
@@ -7872,6 +7869,11 @@
             return this;
         }
 
+        Builder setActivityOptions(ActivityOptions opts) {
+            mActivityOptions = opts;
+            return this;
+        }
+
         Builder setVoiceSession(IVoiceInteractionSession voiceSession) {
             mVoiceSession = voiceSession;
             return this;
@@ -8078,7 +8080,7 @@
 
             // Task created by organizer are added as root.
             final Task launchRootTask = mCreatedByOrganizer
-                    ? null : tda.getLaunchRootTask(mWindowingMode, mActivityType);
+                    ? null : tda.getLaunchRootTask(mWindowingMode, mActivityType, mActivityOptions);
             if (launchRootTask != null) {
                 // Since this task will be put into a root task, its windowingMode will be
                 // inherited.
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 0136c01..a83f81d 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -48,6 +48,7 @@
 import android.util.IntArray;
 import android.util.Slog;
 import android.view.SurfaceControl;
+import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -997,11 +998,11 @@
      * Returns an existing stack compatible with the windowing mode and activity type or creates one
      * if a compatible stack doesn't exist.
      *
-     * @see #getOrCreateRootTask(int, int, boolean, Intent, Task)
+     * @see #getOrCreateRootTask(int, int, boolean, Intent, Task, ActivityOptions)
      */
     Task getOrCreateRootTask(int windowingMode, int activityType, boolean onTop) {
         return getOrCreateRootTask(windowingMode, activityType, onTop, null /* intent */,
-                null /* candidateTask */);
+                null /* candidateTask */, null /* options */);
     }
 
     /**
@@ -1014,7 +1015,7 @@
      * @see #createRootTask(int, int, boolean)
      */
     Task getOrCreateRootTask(int windowingMode, int activityType, boolean onTop,
-            Intent intent, Task candidateTask) {
+            Intent intent, Task candidateTask, ActivityOptions options) {
         // Need to pass in a determined windowing mode to see if a new stack should be created,
         // so use its parent's windowing mode if it is undefined.
         if (!alwaysCreateRootTask(
@@ -1027,7 +1028,7 @@
         } else if (candidateTask != null) {
             final Task stack = candidateTask;
             final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
-            final Task launchRootTask = getLaunchRootTask(windowingMode, activityType);
+            final Task launchRootTask = getLaunchRootTask(windowingMode, activityType, options);
 
             if (launchRootTask != null) {
                 if (stack.getParent() == null) {
@@ -1054,6 +1055,7 @@
                 .setOnTop(onTop)
                 .setParent(this)
                 .setIntent(intent)
+                .setActivityOptions(options)
                 .build();
     }
 
@@ -1074,7 +1076,7 @@
         // it's display's windowing mode.
         windowingMode = validateWindowingMode(windowingMode, r, candidateTask, activityType);
         return getOrCreateRootTask(windowingMode, activityType, onTop, null /* intent */,
-                candidateTask);
+                candidateTask, options);
     }
 
     @VisibleForTesting
@@ -1082,6 +1084,10 @@
         return mAtmService.mTaskSupervisor.getNextTaskIdForUser();
     }
 
+    Task createRootTask(int windowingMode, int activityType, boolean onTop) {
+        return createRootTask(windowingMode, activityType, onTop, null /* activityOptions */);
+    }
+
     /**
      * A convinenit method of creating a root task by providing windowing mode and activity type
      * on this display.
@@ -1095,14 +1101,16 @@
      *                           {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
      * @param onTop              If true the root task will be created at the top of the display,
      *                           else at the bottom.
+     * @param opts               The activity options.
      * @return The newly created root task.
      */
-    Task createRootTask(int windowingMode, int activityType, boolean onTop) {
+    Task createRootTask(int windowingMode, int activityType, boolean onTop, ActivityOptions opts) {
         return new Task.Builder(mAtmService)
                 .setWindowingMode(windowingMode)
                 .setActivityType(activityType)
                 .setParent(this)
                 .setOnTop(onTop)
+                .setActivityOptions(opts)
                 .build();
     }
 
@@ -1134,7 +1142,16 @@
         }
     }
 
-    Task getLaunchRootTask(int windowingMode, int activityType) {
+    Task getLaunchRootTask(int windowingMode, int activityType, ActivityOptions options) {
+        // Try to use the launch root task in options if available.
+        if (options != null) {
+            final Task launchRootTask = Task.fromWindowContainerToken(options.getLaunchRootTask());
+            // We only allow this for created by organizer tasks.
+            if (launchRootTask != null && launchRootTask.mCreatedByOrganizer) {
+                return launchRootTask;
+            }
+        }
+
         for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) {
             if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) {
                 return mLaunchRootTasks.get(i).task;
@@ -1297,11 +1314,10 @@
      * pause activities in visible root tasks, so if an activity is launched within the same root
      * task, hen we should explicitly pause that root task's top activity.
      *
-     * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
      * @param resuming    The resuming activity.
      * @return {@code true} if any activity was paused as a result of this call.
      */
-    boolean pauseBackTasks(boolean userLeaving, ActivityRecord resuming) {
+    boolean pauseBackTasks(ActivityRecord resuming) {
         final int[] someActivityPaused = {0};
         forAllLeafTasks((task) -> {
             final ActivityRecord resumedActivity = task.getResumedActivity();
@@ -1310,7 +1326,7 @@
                     || !task.isTopActivityFocusable())) {
                 ProtoLog.d(WM_DEBUG_STATES, "pauseBackTasks: task=%s "
                         + "mResumedActivity=%s", task, resumedActivity);
-                if (task.startPausingLocked(userLeaving, false /* uiSleeping*/,
+                if (task.startPausingLocked(false /* uiSleeping*/,
                         resuming, "pauseBackTasks")) {
                     someActivityPaused[0]++;
                 }
@@ -1816,12 +1832,12 @@
     }
 
     void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
-            boolean preserveWindows, boolean notifyClients, boolean userLeaving) {
+            boolean preserveWindows, boolean notifyClients) {
         mAtmService.mTaskSupervisor.beginActivityVisibilityUpdate();
         try {
             forAllRootTasks(rootTask -> {
                 rootTask.ensureActivitiesVisible(starting, configChanges, preserveWindows,
-                        notifyClients, userLeaving);
+                        notifyClients);
             });
         } finally {
             mAtmService.mTaskSupervisor.endActivityVisibilityUpdate();
@@ -1866,7 +1882,7 @@
                 // Reparent task to corresponding launch root or display area.
                 final WindowContainer launchRoot = task.supportsSplitScreenWindowingMode()
                         ? toDisplayArea.getLaunchRootTask(
-                                task.getWindowingMode(), task.getActivityType())
+                                task.getWindowingMode(), task.getActivityType(), null /* options */)
                         : null;
                 task.reparent(launchRoot == null ? toDisplayArea : launchRoot, POSITION_TOP);
 
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 389f428..5676909 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -519,13 +519,21 @@
         }
     }
 
-    public boolean areBackgroundActivityStartsAllowed() {
+    /**
+     * Is this WindowProcessController in the state of allowing background FGS start?
+     */
+    public boolean areBackgroundFgsStartsAllowed() {
         synchronized (mAtm.mGlobalLock) {
-            return areBackgroundActivityStartsAllowed(mAtm.getBalAppSwitchesAllowed());
+            return areBackgroundActivityStartsAllowed(mAtm.getBalAppSwitchesAllowed(), true);
         }
     }
 
     boolean areBackgroundActivityStartsAllowed(boolean appSwitchAllowed) {
+        return areBackgroundActivityStartsAllowed(appSwitchAllowed, false);
+    }
+
+    boolean areBackgroundActivityStartsAllowed(boolean appSwitchAllowed,
+            boolean isCheckingForFgsStart) {
         // If app switching is not allowed, we ignore all the start activity grace period
         // exception so apps cannot start itself in onPause() after pressing home button.
         if (appSwitchAllowed) {
@@ -579,7 +587,7 @@
             return true;
         }
         // allow if the flag was explicitly set
-        if (isBackgroundStartAllowedByToken()) {
+        if (isBackgroundStartAllowedByToken(isCheckingForFgsStart)) {
             if (DEBUG_ACTIVITY_STARTS) {
                 Slog.d(TAG, "[WindowProcessController(" + mPid
                         + ")] Activity start allowed: process allowed by token");
@@ -590,13 +598,20 @@
     }
 
     /**
-     * If there are no tokens, we don't allow *by token*. If there are tokens, we ask the callback
-     * if the start is allowed for these tokens, otherwise if there is no callback we allow.
+     * If there are no tokens, we don't allow *by token*. If there are tokens and
+     * isCheckingForFgsStart is false, we ask the callback if the start is allowed for these tokens,
+     * otherwise if there is no callback we allow.
      */
-    private boolean isBackgroundStartAllowedByToken() {
+    private boolean isBackgroundStartAllowedByToken(boolean isCheckingForFgsStart) {
         if (mBackgroundActivityStartTokens.isEmpty()) {
             return false;
         }
+
+        if (isCheckingForFgsStart) {
+            /// The checking is for BG-FGS-start.
+            return true;
+        }
+
         if (mBackgroundActivityStartCallback == null) {
             // We have tokens but no callback to decide => allow
             return true;
@@ -843,7 +858,8 @@
             if (mPreQTopResumedActivity != null && mPreQTopResumedActivity.isState(RESUMED)) {
                 final Task task = mPreQTopResumedActivity.getTask();
                 if (task != null) {
-                    task.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
+                    boolean userLeaving = task.shouldBeVisible(null);
+                    task.startPausingLocked(userLeaving, false /* uiSleeping */,
                             activity, "top-resumed-changed");
                 }
             }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5319592..621b971 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -67,7 +67,6 @@
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
-import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
@@ -3048,8 +3047,8 @@
             return;
         }
 
-        if (mAttrs.type == TYPE_APPLICATION_OVERLAY && mSession.mCanCreateSystemApplicationOverlay
-                && (mAttrs.privateFlags & SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY) != 0) {
+        if (mAttrs.type == TYPE_APPLICATION_OVERLAY && mAttrs.isSystemApplicationOverlay()
+                && mSession.mCanCreateSystemApplicationOverlay) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 9e0fee39..64e8184 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -647,11 +647,6 @@
         state.mIsTransforming = false;
         if (applyDisplayRotation != null) {
             applyDisplayRotation.run();
-        } else {
-            // The display will not rotate to the rotation of this container, let's cancel them.
-            for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) {
-                state.mAssociatedTokens.get(i).cancelFixedRotationTransform();
-            }
         }
         // The state is cleared at the end, because it is used to indicate that other windows can
         // use seamless rotation when applying rotation to display.
@@ -659,6 +654,10 @@
             final WindowToken token = state.mAssociatedTokens.get(i);
             token.mFixedRotationTransformState = null;
             token.notifyFixedRotationTransform(false /* enabled */);
+            if (applyDisplayRotation == null) {
+                // Notify cancellation because the display does not change rotation.
+                token.cancelFixedRotationTransform();
+            }
         }
     }
 
@@ -707,7 +706,6 @@
             // The window may be detached or detaching.
             return;
         }
-        notifyFixedRotationTransform(false /* enabled */);
         final int originalRotation = getWindowConfiguration().getRotation();
         onConfigurationChanged(parent.getConfiguration());
         onCancelFixedRotationTransform(originalRotation);
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 1cb9e57..7d705c1 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -35,6 +35,18 @@
                 </xs:element>
                 <xs:element type="highBrightnessMode" name="highBrightnessMode" minOccurs="0" maxOccurs="1"/>
                 <xs:element type="displayQuirks" name="quirks" minOccurs="0" maxOccurs="1" />
+                <xs:element type="nonNegativeDecimal" name="screenBrightnessRampFastDecrease">
+                    <xs:annotation name="final"/>
+                </xs:element>
+                <xs:element type="nonNegativeDecimal" name="screenBrightnessRampFastIncrease">
+                    <xs:annotation name="final"/>
+                </xs:element>
+                <xs:element type="nonNegativeDecimal" name="screenBrightnessRampSlowDecrease">
+                    <xs:annotation name="final"/>
+                </xs:element>
+                <xs:element type="nonNegativeDecimal" name="screenBrightnessRampSlowIncrease">
+                    <xs:annotation name="final"/>
+                </xs:element>
             </xs:sequence>
         </xs:complexType>
     </xs:element>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index e073ab3..eb3f1b7 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -7,10 +7,18 @@
     method public com.android.server.display.config.DisplayQuirks getQuirks();
     method @NonNull public final java.math.BigDecimal getScreenBrightnessDefault();
     method @NonNull public final com.android.server.display.config.NitsMap getScreenBrightnessMap();
+    method public final java.math.BigDecimal getScreenBrightnessRampFastDecrease();
+    method public final java.math.BigDecimal getScreenBrightnessRampFastIncrease();
+    method public final java.math.BigDecimal getScreenBrightnessRampSlowDecrease();
+    method public final java.math.BigDecimal getScreenBrightnessRampSlowIncrease();
     method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode);
     method public void setQuirks(com.android.server.display.config.DisplayQuirks);
     method public final void setScreenBrightnessDefault(@NonNull java.math.BigDecimal);
     method public final void setScreenBrightnessMap(@NonNull com.android.server.display.config.NitsMap);
+    method public final void setScreenBrightnessRampFastDecrease(java.math.BigDecimal);
+    method public final void setScreenBrightnessRampFastIncrease(java.math.BigDecimal);
+    method public final void setScreenBrightnessRampSlowDecrease(java.math.BigDecimal);
+    method public final void setScreenBrightnessRampSlowIncrease(java.math.BigDecimal);
   }
 
   public class DisplayQuirks {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 55ba6c9..1194099 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -17,8 +17,11 @@
 
 import android.annotation.NonNull;
 import android.app.admin.DevicePolicySafetyChecker;
+import android.app.admin.FullyManagedDeviceProvisioningParams;
 import android.app.admin.IDevicePolicyManager;
+import android.app.admin.ManagedProfileProvisioningParams;
 import android.content.ComponentName;
+import android.os.UserHandle;
 import android.util.Slog;
 
 import com.android.server.SystemService;
@@ -115,4 +118,13 @@
 
     public void setOrganizationIdForUser(
             @NonNull String callerPackage, @NonNull String enterpriseId, int userId) {}
+
+    public UserHandle createAndProvisionManagedProfile(
+            @NonNull ManagedProfileProvisioningParams provisioningParams) {
+        return null;
+    }
+
+    public void provisionFullyManagedDevice(
+            FullyManagedDeviceProvisioningParams provisioningParams) {
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 8a9ec08..12b3f40 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -21,8 +21,11 @@
 import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE;
 import static android.app.admin.DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE;
+import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
+import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
 import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY;
 import static android.app.admin.DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE;
@@ -34,6 +37,7 @@
 import static android.app.admin.DevicePolicyManager.CODE_NOT_SYSTEM_USER;
 import static android.app.admin.DevicePolicyManager.CODE_NOT_SYSTEM_USER_SPLIT;
 import static android.app.admin.DevicePolicyManager.CODE_OK;
+import static android.app.admin.DevicePolicyManager.CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS;
 import static android.app.admin.DevicePolicyManager.CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER;
 import static android.app.admin.DevicePolicyManager.CODE_SYSTEM_USER;
 import static android.app.admin.DevicePolicyManager.CODE_USER_HAS_PROFILE_OWNER;
@@ -83,16 +87,25 @@
 import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
 import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_NO_ERROR;
 import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
+import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED;
+import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_PRE_CONDITION_FAILED;
+import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_PROFILE_CREATION_FAILED;
+import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED;
+import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED;
+import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED;
+import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_STARTING_PROFILE_FAILED;
 import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
 import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
 import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
 import static android.app.admin.DevicePolicyManager.WIPE_SILENTLY;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
 import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
+import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static android.provider.Telephony.Carriers.DPC_URI;
 import static android.provider.Telephony.Carriers.ENFORCE_KEY;
 import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
@@ -109,10 +122,14 @@
 import static com.android.server.pm.UserManagerInternal.OWNER_TYPE_PROFILE_OWNER;
 import static com.android.server.pm.UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE;
 
+import android.Manifest;
 import android.Manifest.permission;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accounts.Account;
 import android.accounts.AccountManager;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -146,6 +163,8 @@
 import android.app.admin.DevicePolicySafetyChecker;
 import android.app.admin.DeviceStateCache;
 import android.app.admin.FactoryResetProtectionPolicy;
+import android.app.admin.FullyManagedDeviceProvisioningParams;
+import android.app.admin.ManagedProfileProvisioningParams;
 import android.app.admin.NetworkEvent;
 import android.app.admin.PasswordMetrics;
 import android.app.admin.PasswordPolicy;
@@ -267,6 +286,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.LocalePicker;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.net.NetworkUtilsInternal;
@@ -325,12 +345,14 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 /**
  * Implementation of the device policy APIs.
@@ -380,6 +402,8 @@
 
     private static final String NULL_STRING_ARRAY = "nullStringArray";
 
+    private static final String ALLOW_USER_PROVISIONING_KEY = "ro.config.allowuserprovisioning";
+
     // Comprehensive list of delegations.
     private static final String DELEGATIONS[] = {
         DELEGATION_CERT_INSTALL,
@@ -1231,6 +1255,10 @@
             return LocalServices.getService(LockSettingsInternal.class);
         }
 
+        CrossProfileApps getCrossProfileApps() {
+            return mContext.getSystemService(CrossProfileApps.class);
+        }
+
         boolean hasUserSetupCompleted(DevicePolicyData userData) {
             return userData.mUserSetupComplete;
         }
@@ -11327,7 +11355,7 @@
 
     private void showLocationSettingsEnabledNotification(UserHandle user) {
         Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
-                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                .addFlags(FLAG_ACTIVITY_NEW_TASK);
         // Fill the component explicitly to prevent the PendingIntent from being intercepted
         // and fired with crafted target. b/155183624
         ActivityInfo targetInfo = intent.resolveActivityInfo(
@@ -12188,7 +12216,7 @@
         final Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
         intent.putExtra(Intent.EXTRA_USER_ID, userId);
         intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
         return intent;
     }
 
@@ -12666,6 +12694,9 @@
             logMissingFeatureAction("Cannot check provisioning for action " + action);
             return CODE_DEVICE_ADMIN_NOT_SUPPORTED;
         }
+        if (!isProvisioningAllowed()) {
+            return CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS;
+        }
         final int code = checkProvisioningPreConditionSkipPermissionNoLog(action, packageName);
         if (code != CODE_OK) {
             Slog.d(LOG_TAG, "checkProvisioningPreCondition(" + action + ", " + packageName
@@ -12675,6 +12706,23 @@
         return code;
     }
 
+    /**
+     *  Checks if provisioning is allowed during regular usage (non-developer/CTS). This could
+     *  return {@code false} if the device has an overlaid config value set to false. If not set,
+     *  the default is true.
+     */
+    private boolean isProvisioningAllowed() {
+        boolean isDeveloperMode = isDeveloperMode(mContext);
+        boolean isProvisioningAllowedForNormalUsers = SystemProperties.getBoolean(
+                ALLOW_USER_PROVISIONING_KEY, /* defValue= */ true);
+
+        return isDeveloperMode || isProvisioningAllowedForNormalUsers;
+    }
+
+    private static boolean isDeveloperMode(Context context) {
+        return Global.getInt(context.getContentResolver(), Global.ADB_ENABLED, 0) > 0;
+    }
+
     private int checkProvisioningPreConditionSkipPermissionNoLog(String action,
             String packageName) {
         final int callingUserId = mInjector.userHandleGetCallingUserId();
@@ -12831,14 +12879,11 @@
                         callingUserHandle, hasDeviceOwner));
                 return CODE_CANNOT_ADD_MANAGED_PROFILE;
             }
-            // If there's a restriction on removing the managed profile then we have to take it
-            // into account when checking whether more profiles can be added.
-            boolean canRemoveProfile =
-                    !mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
-                    callingUserHandle);
-            if (!mUserManager.canAddMoreManagedProfiles(callingUserId, canRemoveProfile)) {
-                Slog.i(LOG_TAG, String.format(
-                        "Cannot add more profiles: Can remove current? %b", canRemoveProfile));
+
+            // Bail out if we are trying to provision a work profile but one already exists.
+            if (!mUserManager.canAddMoreManagedProfiles(
+                    callingUserId, /* allowedToRemoveOne= */ false)) {
+                Slog.i(LOG_TAG, String.format("A work profile already exists."));
                 return CODE_CANNOT_ADD_MANAGED_PROFILE;
             }
         } finally {
@@ -13765,7 +13810,7 @@
         }
         final Uri packageURI = Uri.parse("package:" + packageName);
         final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
-        uninstallIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        uninstallIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
         mContext.startActivityAsUser(uninstallIntent, UserHandle.of(userId));
     }
 
@@ -15957,4 +16002,415 @@
                 .setBoolean(isManagedProfile(userId))
                 .write();
     }
+
+    @Override
+    public UserHandle createAndProvisionManagedProfile(
+            @NonNull ManagedProfileProvisioningParams provisioningParams) {
+        final ComponentName admin = provisioningParams.getProfileAdminComponentName();
+        Objects.requireNonNull(admin, "admin is null");
+
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(
+                hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+
+        UserInfo userInfo = null;
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            final int result = checkProvisioningPreConditionSkipPermission(
+                    ACTION_PROVISION_MANAGED_PROFILE, admin.getPackageName());
+            if (result != CODE_OK) {
+                throw new ServiceSpecificException(
+                        PROVISIONING_RESULT_PRE_CONDITION_FAILED,
+                        "Provisioning preconditions failed with result: " + result);
+            }
+
+            final Set<String> nonRequiredApps = provisioningParams.isLeaveAllSystemAppsEnabled()
+                    ? Collections.emptySet()
+                    : mOverlayPackagesProvider.getNonRequiredApps(
+                            admin, caller.getUserId(), ACTION_PROVISION_MANAGED_PROFILE);
+            userInfo = mUserManager.createProfileForUserEvenWhenDisallowed(
+                    provisioningParams.getProfileName(),
+                    UserManager.USER_TYPE_PROFILE_MANAGED,
+                    UserInfo.FLAG_DISABLED,
+                    caller.getUserId(),
+                    nonRequiredApps.toArray(new String[nonRequiredApps.size()]));
+            if (userInfo == null) {
+                throw new ServiceSpecificException(
+                        PROVISIONING_RESULT_PROFILE_CREATION_FAILED,
+                        "Error creating profile, createProfileForUserEvenWhenDisallowed "
+                                + "returned null.");
+            }
+
+            resetInteractAcrossProfilesAppOps();
+            installExistingAdminPackage(userInfo.id, admin.getPackageName());
+            if (!enableAdminAndSetProfileOwner(
+                    userInfo.id, caller.getUserId(), admin, provisioningParams.getOwnerName())) {
+                throw new ServiceSpecificException(
+                        PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED,
+                        "Error setting profile owner.");
+            }
+            setUserSetupComplete(userInfo.id);
+
+            startUser(userInfo.id);
+            maybeMigrateAccount(
+                    userInfo.id, caller.getUserId(), provisioningParams.getAccountToMigrate(),
+                    provisioningParams.isKeepAccountMigrated());
+
+            if (provisioningParams.isOrganizationOwnedProvisioning()) {
+                markIsProfileOwnerOnOrganizationOwnedDevice(admin, userInfo.id);
+                restrictRemovalOfManagedProfile(admin, userInfo.id);
+            }
+
+            final Intent intent = new Intent(DevicePolicyManager.ACTION_MANAGED_PROFILE_CREATED)
+                    .putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id)
+                    .putExtra(
+                            DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
+                            provisioningParams.isLeaveAllSystemAppsEnabled())
+                    .setPackage(getManagedProvisioningPackage(mContext))
+                    .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+            mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+
+            return userInfo.getUserHandle();
+        } catch (Exception e) {
+            // in case of any errors during provisioning, remove the newly created profile.
+            if (userInfo != null) {
+                mUserManager.removeUserEvenWhenDisallowed(userInfo.id);
+            }
+            throw e;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private void resetInteractAcrossProfilesAppOps() {
+        mInjector.getCrossProfileApps().clearInteractAcrossProfilesAppOps();
+        pregrantDefaultInteractAcrossProfilesAppOps();
+    }
+
+    private void pregrantDefaultInteractAcrossProfilesAppOps() {
+        final String op =
+                AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES);
+        for (String packageName : getConfigurableDefaultCrossProfilePackages()) {
+            if (appOpIsChangedFromDefault(op, packageName)) {
+                continue;
+            }
+            mInjector.getCrossProfileApps().setInteractAcrossProfilesAppOp(
+                    packageName, MODE_ALLOWED);
+        }
+    }
+
+    private Set<String> getConfigurableDefaultCrossProfilePackages() {
+        List<String> defaultPackages = getDefaultCrossProfilePackages();
+        return defaultPackages.stream().filter(
+                mInjector.getCrossProfileApps()::canConfigureInteractAcrossProfiles).collect(
+                Collectors.toSet());
+    }
+
+    private boolean appOpIsChangedFromDefault(String op, String packageName) {
+        try {
+            final int uid = mContext.getPackageManager().getPackageUid(
+                    packageName, /* flags= */ 0);
+            return mInjector.getAppOpsManager().unsafeCheckOpNoThrow(
+                    op, uid, packageName)
+                    != AppOpsManager.MODE_DEFAULT;
+        } catch (NameNotFoundException e) {
+            return false;
+        }
+    }
+
+    private void installExistingAdminPackage(int userId, String packageName) {
+        try {
+            final int status = mContext.getPackageManager().installExistingPackageAsUser(
+                    packageName,
+                    userId);
+            if (status != PackageManager.INSTALL_SUCCEEDED) {
+                throw new ServiceSpecificException(
+                        PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED,
+                        String.format("Failed to install existing package %s for user %d with "
+                                        + "result code %d",
+                                packageName, userId, status));
+            }
+        } catch (NameNotFoundException e) {
+            throw new ServiceSpecificException(
+                    PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED,
+                    String.format("Failed to install existing package %s for user %d: %s",
+                            packageName, userId, e.getMessage()));
+        }
+    }
+
+    private boolean enableAdminAndSetProfileOwner(
+            @UserIdInt int userId, @UserIdInt int callingUserId, ComponentName adminComponent,
+            String ownerName) {
+        enableAndSetActiveAdmin(userId, callingUserId, adminComponent);
+        return setProfileOwner(adminComponent, ownerName, userId);
+    }
+
+    private void enableAndSetActiveAdmin(
+            @UserIdInt int userId, @UserIdInt int callingUserId, ComponentName adminComponent) {
+        final String adminPackage = adminComponent.getPackageName();
+        enablePackage(adminPackage, callingUserId);
+        setActiveAdmin(adminComponent, /* refreshing= */ true, userId);
+    }
+
+    private void enablePackage(String packageName, @UserIdInt int userId) {
+        try {
+            final int enabledSetting = mIPackageManager.getApplicationEnabledSetting(
+                    packageName, userId);
+            if (enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+                    && enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+                mIPackageManager.setApplicationEnabledSetting(
+                        packageName,
+                        PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+                        // Device policy app may have launched ManagedProvisioning, play nice and
+                        // don't kill it as a side-effect of this call.
+                        PackageManager.DONT_KILL_APP,
+                        userId,
+                        mContext.getOpPackageName());
+            }
+        } catch (RemoteException e) {
+            // Shouldn't happen.
+        }
+    }
+
+    private void setUserSetupComplete(@UserIdInt int userId) {
+        Settings.Secure.putIntForUser(
+                mContext.getContentResolver(), USER_SETUP_COMPLETE, 1, userId);
+    }
+
+    private void startUser(@UserIdInt int userId) throws IllegalStateException {
+        final UserUnlockedBlockingReceiver unlockedReceiver = new UserUnlockedBlockingReceiver(
+                userId);
+        mContext.registerReceiverAsUser(
+                unlockedReceiver,
+                new UserHandle(userId),
+                new IntentFilter(Intent.ACTION_USER_UNLOCKED),
+                /* broadcastPermission = */ null,
+                /* scheduler= */ null);
+        try {
+            if (!mInjector.getIActivityManager().startUserInBackground(userId)) {
+                throw new ServiceSpecificException(PROVISIONING_RESULT_STARTING_PROFILE_FAILED,
+                        String.format("Unable to start user %d in background", userId));
+            }
+
+            if (!unlockedReceiver.waitForUserUnlocked()) {
+                throw new ServiceSpecificException(PROVISIONING_RESULT_STARTING_PROFILE_FAILED,
+                        String.format("Timeout whilst waiting for unlock of user %d.", userId));
+            }
+        } catch (RemoteException e) {
+            // Shouldn't happen.
+        } finally {
+            mContext.unregisterReceiver(unlockedReceiver);
+        }
+    }
+
+    void maybeMigrateAccount(
+            @UserIdInt int targetUserId, @UserIdInt int sourceUserId, Account accountToMigrate,
+            boolean keepAccountMigrated) {
+        final UserHandle sourceUser = UserHandle.of(sourceUserId);
+        final UserHandle targetUser = UserHandle.of(targetUserId);
+        if (accountToMigrate == null) {
+            Slog.d(LOG_TAG, "No account to migrate.");
+            return;
+        }
+        if (sourceUser.equals(targetUser)) {
+            Slog.w(LOG_TAG, "sourceUser and targetUser are the same, won't migrate account.");
+            return;
+        }
+        copyAccount(targetUser, sourceUser, accountToMigrate);
+        if (!keepAccountMigrated) {
+            removeAccount(accountToMigrate);
+        }
+    }
+
+    void copyAccount(UserHandle targetUser, UserHandle sourceUser, Account accountToMigrate) {
+        try {
+            final AccountManager accountManager = mContext.getSystemService(AccountManager.class);
+            final boolean copySucceeded = accountManager.copyAccountToUser(
+                    accountToMigrate,
+                    sourceUser,
+                    targetUser,
+                    /* callback= */ null, /* handler= */ null)
+                    .getResult(60 * 3, TimeUnit.SECONDS);
+            if (!copySucceeded) {
+                Slog.e(LOG_TAG, "Failed to copy account to " + targetUser);
+            }
+        } catch (OperationCanceledException | AuthenticatorException | IOException e) {
+            // Account migration is not considered a critical operation.
+            Slog.e(LOG_TAG, "Exception copying account to " + targetUser, e);
+        }
+    }
+
+    void removeAccount(Account account) {
+        final AccountManager accountManager =
+                mContext.getSystemService(AccountManager.class);
+        final AccountManagerFuture<Bundle> bundle = accountManager.removeAccount(account,
+                null, null /* callback */, null /* handler */);
+        try {
+            final Bundle result = bundle.getResult();
+            if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, /* default */ false)) {
+                Slog.i(LOG_TAG, "Account removed from the primary user.");
+            } else {
+                // TODO(174768447): Revisit start activity logic.
+                final Intent removeIntent = result.getParcelable(AccountManager.KEY_INTENT);
+                removeIntent.addFlags(FLAG_ACTIVITY_NEW_TASK);
+                if (removeIntent != null) {
+                    Slog.i(LOG_TAG, "Starting activity to remove account");
+                    new Handler(Looper.getMainLooper()).post(() -> {
+                        mContext.startActivity(removeIntent);
+                    });
+                } else {
+                    Slog.e(LOG_TAG, "Could not remove account from the primary user.");
+                }
+            }
+        } catch (OperationCanceledException | AuthenticatorException | IOException e) {
+            Slog.e(LOG_TAG, "Exception removing account from the primary user.", e);
+        }
+    }
+
+    private void markIsProfileOwnerOnOrganizationOwnedDevice(
+            ComponentName admin, @UserIdInt int profileId) {
+        getDpmForProfile(profileId).markProfileOwnerOnOrganizationOwnedDevice(admin);
+    }
+
+    private void restrictRemovalOfManagedProfile(
+            ComponentName admin, @UserIdInt int profileId) {
+        getDpmForProfile(profileId).addUserRestriction(
+                admin, UserManager.DISALLOW_REMOVE_MANAGED_PROFILE);
+    }
+
+    private DevicePolicyManager getDpmForProfile(@UserIdInt int profileId) {
+        final Context profileContext = mContext.createContextAsUser(
+                UserHandle.of(profileId), /* flags= */ 0);
+        return profileContext.getSystemService(DevicePolicyManager.class);
+    }
+
+    @Override
+    public void provisionFullyManagedDevice(
+            FullyManagedDeviceProvisioningParams provisioningParams) {
+        ComponentName deviceAdmin = provisioningParams.getDeviceAdminComponentName();
+
+        Objects.requireNonNull(deviceAdmin, "admin is null.");
+        Objects.requireNonNull(provisioningParams.getOwnerName(), "owner name is null.");
+
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(
+                hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            int result = checkProvisioningPreConditionSkipPermission(
+                    ACTION_PROVISION_MANAGED_DEVICE, deviceAdmin.getPackageName());
+            if (result != CODE_OK) {
+                throw new ServiceSpecificException(
+                        PROVISIONING_RESULT_PRE_CONDITION_FAILED,
+                        "Provisioning preconditions failed with result: " + result);
+            }
+
+            setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime());
+            setLocale(provisioningParams.getLocale());
+
+            if (!removeNonRequiredAppsForManagedDevice(
+                    caller.getUserId(),
+                    provisioningParams.isLeaveAllSystemAppsEnabled(),
+                    deviceAdmin)) {
+                throw new ServiceSpecificException(
+                        PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED,
+                        "PackageManager failed to remove non required apps.");
+            }
+
+            if (!setActiveAdminAndDeviceOwner(
+                    caller.getUserId(), deviceAdmin, provisioningParams.getOwnerName())) {
+                throw new ServiceSpecificException(
+                        PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED,
+                        "Failed to set device owner.");
+            }
+
+            disallowAddUser();
+
+            final Intent intent = new Intent(DevicePolicyManager.ACTION_PROVISIONED_MANAGED_DEVICE)
+                    .putExtra(Intent.EXTRA_USER_HANDLE, caller.getUserId())
+                    .putExtra(
+                            DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
+                            provisioningParams.isLeaveAllSystemAppsEnabled())
+                    .setPackage(getManagedProvisioningPackage(mContext))
+                    .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+            mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private void setTimeAndTimezone(String timeZone, long localTime) {
+        try {
+            final AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class);
+            if (timeZone != null) {
+                alarmManager.setTimeZone(timeZone);
+            }
+            if (localTime > 0) {
+                alarmManager.setTime(localTime);
+            }
+        } catch (Exception e) {
+            // Do not stop provisioning and ignore this error.
+            Slog.e(LOG_TAG, "Alarm manager failed to set the system time/timezone.", e);
+        }
+    }
+
+    private void setLocale(Locale locale) {
+        if (locale == null || locale.equals(Locale.getDefault())) {
+            return;
+        }
+        try {
+            // If locale is different from current locale this results in a configuration change,
+            // which will trigger the restarting of the activity.
+            LocalePicker.updateLocale(locale);
+        } catch (Exception e) {
+            // Do not stop provisioning and ignore this error.
+            Slog.e(LOG_TAG, "Failed to set the system locale.", e);
+        }
+    }
+
+    private boolean removeNonRequiredAppsForManagedDevice(
+            int userId, boolean leaveAllSystemAppsEnabled, ComponentName admin) {
+        Set<String> packagesToDelete = leaveAllSystemAppsEnabled
+                ? Collections.emptySet()
+                : mOverlayPackagesProvider.getNonRequiredApps(
+                        admin, userId, ACTION_PROVISION_MANAGED_DEVICE);
+        if (packagesToDelete.isEmpty()) {
+            return true;
+        }
+        NonRequiredPackageDeleteObserver packageDeleteObserver =
+                new NonRequiredPackageDeleteObserver(packagesToDelete.size());
+        for (String packageName : packagesToDelete) {
+            if (isPackageInstalledForUser(packageName, userId)) {
+                Slog.i(LOG_TAG, "Deleting package [" + packageName + "] as user " + userId);
+                mContext.getPackageManager().deletePackageAsUser(
+                        packageName,
+                        packageDeleteObserver,
+                        PackageManager.DELETE_SYSTEM_APP,
+                        userId);
+            }
+        }
+        Slog.i(LOG_TAG, "Waiting for non required apps to be deleted");
+        return packageDeleteObserver.awaitPackagesDeletion();
+    }
+
+    private void disallowAddUser() {
+        if (mInjector.userManagerIsHeadlessSystemUserMode()) {
+            Slog.i(LOG_TAG, "Not setting DISALLOW_ADD_USER on headless system user mode.");
+            return;
+        }
+        for (UserInfo userInfo : mUserManager.getUsers()) {
+            UserHandle userHandle = userInfo.getUserHandle();
+            if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER, userHandle)) {
+                mUserManager.setUserRestriction(
+                        UserManager.DISALLOW_ADD_USER, /* value= */ true, userHandle);
+            }
+        }
+    }
+
+    private boolean setActiveAdminAndDeviceOwner(
+            @UserIdInt int userId, ComponentName adminComponent, String name) {
+        enableAndSetActiveAdmin(userId, userId, adminComponent);
+        return setDeviceOwner(adminComponent, name, userId);
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NonRequiredPackageDeleteObserver.java b/services/devicepolicy/java/com/android/server/devicepolicy/NonRequiredPackageDeleteObserver.java
new file mode 100644
index 0000000..0e448cd
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NonRequiredPackageDeleteObserver.java
@@ -0,0 +1,70 @@
+/*
+ * 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.devicepolicy;
+
+import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
+
+import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.PackageManager;
+import android.util.Log;
+import android.util.Slog;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Awaits the deletion of all the non-required apps.
+ */
+final class NonRequiredPackageDeleteObserver extends IPackageDeleteObserver.Stub {
+    private static final int PACKAGE_DELETE_TIMEOUT_SEC = 30;
+
+    private final AtomicInteger mPackageCount = new AtomicInteger(/* initialValue= */ 0);
+    private final CountDownLatch mLatch;
+    private boolean mSuccess;
+
+    NonRequiredPackageDeleteObserver(int packageCount) {
+        this.mLatch = new CountDownLatch(packageCount);
+        this.mPackageCount.set(packageCount);
+    }
+
+    @Override
+    public void packageDeleted(String packageName, int returnCode) {
+        if (returnCode != PackageManager.DELETE_SUCCEEDED) {
+            Slog.e(LOG_TAG, "Failed to delete package: " + packageName);
+            mLatch.notifyAll();
+            return;
+        }
+        int currentPackageCount = mPackageCount.decrementAndGet();
+        if (currentPackageCount == 0) {
+            mSuccess = true;
+            Slog.i(LOG_TAG, "All non-required system apps with launcher icon, "
+                    + "and all disallowed apps have been uninstalled.");
+        }
+        mLatch.countDown();
+    }
+
+    public boolean awaitPackagesDeletion() {
+        try {
+            mLatch.await(PACKAGE_DELETE_TIMEOUT_SEC, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            Log.w(LOG_TAG, "Interrupted while waiting for package deletion", e);
+            Thread.currentThread().interrupt();
+        }
+        return mSuccess;
+    }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/UserUnlockedBlockingReceiver.java b/services/devicepolicy/java/com/android/server/devicepolicy/UserUnlockedBlockingReceiver.java
new file mode 100644
index 0000000..4ce96b7
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/UserUnlockedBlockingReceiver.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.devicepolicy;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * BroadcastReceiver that listens to {@link Intent#ACTION_USER_UNLOCKED} in order to provide
+ * a blocking wait until the managed profile has been started and unlocked.
+ */
+class UserUnlockedBlockingReceiver extends BroadcastReceiver {
+    private static final int WAIT_FOR_USER_UNLOCKED_TIMEOUT_SECONDS = 120;
+
+    private final Semaphore mSemaphore = new Semaphore(0);
+    private final int mUserId;
+
+    UserUnlockedBlockingReceiver(int userId) {
+        mUserId = userId;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (!Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
+            return;
+        }
+        if (intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL) == mUserId) {
+            mSemaphore.release();
+        }
+    }
+
+    public boolean waitForUserUnlocked() {
+        try {
+            return mSemaphore.tryAcquire(
+                    WAIT_FOR_USER_UNLOCKED_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        } catch (InterruptedException ie) {
+            return false;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index c038a0f..28a6ff7 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -124,7 +124,7 @@
     private Context mContext;
     FullScreenMagnificationController mFullScreenMagnificationController;
     @Mock
-    MagnificationGestureHandler.ScaleChangedListener mMockScaleChangedListener;
+    MagnificationGestureHandler.Callback mMockCallback;
     @Mock
     MagnificationRequestObserver mMagnificationRequestObserver;
     @Mock
@@ -179,7 +179,7 @@
     private FullScreenMagnificationGestureHandler newInstance(boolean detectTripleTap,
             boolean detectShortcutTrigger) {
         FullScreenMagnificationGestureHandler h = new FullScreenMagnificationGestureHandler(
-                mContext, mFullScreenMagnificationController, mMockScaleChangedListener,
+                mContext, mFullScreenMagnificationController, mMockCallback,
                 detectTripleTap, detectShortcutTrigger,
                 mWindowMagnificationPromptController, DISPLAY_0);
         mHandler = new TestHandler(h.mDetectingState, mClock) {
@@ -451,6 +451,14 @@
         verify(mWindowMagnificationPromptController).showNotificationIfNeeded();
     }
 
+    @Test
+    public void testZoomedWithTripleTap_callsOnTripleTapped() {
+        goFromStateIdleTo(STATE_ZOOMED_2TAPS);
+
+        verify(mMockCallback).onTripleTapped(DISPLAY_0,
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+    }
+
     private void assertActionsInOrder(List<MotionEvent> actualEvents,
             List<Integer> expectedActions) {
         assertTrue(actualEvents.size() == expectedActions.size());
@@ -554,8 +562,6 @@
                 check(mMgh.mCurrentState == mMgh.mPanningScalingState,
                         state);
                 check(mMgh.mPanningScalingState.mScaling, state);
-                verify(mMockScaleChangedListener).onMagnificationScaleChanged(DISPLAY_0,
-                        Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
             } break;
             default: throw new IllegalArgumentException("Illegal state: " + state);
         }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index cba618b..0c3640c 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -109,6 +109,8 @@
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
         mMagnificationController = spy(new MagnificationController(mService, new Object(), mContext,
                 mScreenMagnificationController, mWindowMagnificationManager));
+        mMagnificationController.setMagnificationCapabilities(
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
     }
 
     @After
@@ -278,34 +280,128 @@
 
         verify(mWindowMagnificationManager).setScale(eq(TEST_DISPLAY), eq(newScale));
         verify(mWindowMagnificationManager).persistScale(eq(TEST_DISPLAY));
-        verify(mMagnificationController).onMagnificationScaleChanged(eq(TEST_DISPLAY),
-                eq(MODE_WINDOW));
     }
 
     @Test
-    public void onMagnificationScaleChanged_capabilitiesAllMode_showMagnificationButton()
+    public void onTouchInteractionStart_fullScreenAndCapabilitiesAll_showMagnificationButton()
             throws RemoteException {
-        mMagnificationController.setMagnificationCapabilities(
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+        setMagnificationEnabled(MODE_FULLSCREEN);
 
-        mMagnificationController.onMagnificationScaleChanged(TEST_DISPLAY, MODE_WINDOW);
+        mMagnificationController.onTouchInteractionStart(TEST_DISPLAY, MODE_FULLSCREEN);
+
+        verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
+                eq(MODE_FULLSCREEN));
+    }
+
+    @Test
+    public void onTouchInteractionEnd_fullScreenAndCapabilitiesAll_showMagnificationButton()
+            throws RemoteException {
+        setMagnificationEnabled(MODE_FULLSCREEN);
+
+        mMagnificationController.onTouchInteractionEnd(TEST_DISPLAY, MODE_FULLSCREEN);
+
+        verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
+                eq(MODE_FULLSCREEN));
+    }
+
+    @Test
+    public void onTouchInteractionStart_windowModeAndCapabilitiesAll_showMagnificationButton()
+            throws RemoteException {
+        setMagnificationEnabled(MODE_WINDOW);
+
+        mMagnificationController.onTouchInteractionStart(TEST_DISPLAY, MODE_WINDOW);
 
         verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
                 eq(MODE_WINDOW));
     }
 
     @Test
-    public void onMagnificationScaleChanged_capabilitiesNotAllMode_notShowMagnificationButton()
+    public void onTouchInteractionEnd_windowModeAndCapabilitiesAll_showMagnificationButton()
+            throws RemoteException {
+        setMagnificationEnabled(MODE_WINDOW);
+
+        mMagnificationController.onTouchInteractionEnd(TEST_DISPLAY, MODE_WINDOW);
+
+        verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
+                eq(MODE_WINDOW));
+    }
+
+    @Test
+    public void onTouchInteractionChanged_notCapabilitiesAll_notShowMagnificationButton()
             throws RemoteException {
         mMagnificationController.setMagnificationCapabilities(
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+        setMagnificationEnabled(MODE_FULLSCREEN);
 
-        mMagnificationController.onMagnificationScaleChanged(TEST_DISPLAY, MODE_WINDOW);
+        mMagnificationController.onTouchInteractionStart(TEST_DISPLAY, MODE_FULLSCREEN);
+        mMagnificationController.onTouchInteractionEnd(TEST_DISPLAY, MODE_FULLSCREEN);
 
         verify(mWindowMagnificationManager, never()).showMagnificationButton(eq(TEST_DISPLAY),
+                eq(MODE_FULLSCREEN));
+    }
+
+    @Test
+    public void onShortcutTriggered_windowModeEnabledAndCapabilitiesAll_showMagnificationButton()
+            throws RemoteException {
+        setMagnificationEnabled(MODE_WINDOW);
+
+        mMagnificationController.onShortcutTriggered(TEST_DISPLAY, MODE_WINDOW);
+
+        verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
                 eq(MODE_WINDOW));
     }
 
+    @Test
+    public void onShortcutTriggered_fullscreenEnabledAndCapabilitiesAll_showMagnificationButton()
+            throws RemoteException {
+        setMagnificationEnabled(MODE_FULLSCREEN);
+
+        mMagnificationController.onShortcutTriggered(TEST_DISPLAY, MODE_FULLSCREEN);
+
+        verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
+                eq(MODE_FULLSCREEN));
+    }
+
+    @Test
+    public void onShortcutTriggered_windowModeDisabled_removeMagnificationButton()
+            throws RemoteException {
+
+        mMagnificationController.onShortcutTriggered(TEST_DISPLAY, MODE_WINDOW);
+
+        verify(mWindowMagnificationManager).removeMagnificationButton(eq(TEST_DISPLAY));
+    }
+
+    @Test
+    public void onTripleTap_windowModeEnabledAndCapabilitiesAll_showMagnificationButton()
+            throws RemoteException {
+        setMagnificationEnabled(MODE_WINDOW);
+
+        mMagnificationController.onTripleTapped(TEST_DISPLAY, MODE_WINDOW);
+
+        verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
+                eq(MODE_WINDOW));
+    }
+
+    @Test
+    public void onTripleTap_fullscreenEnabledAndCapabilitiesAll_showMagnificationButton()
+            throws RemoteException {
+        setMagnificationEnabled(MODE_FULLSCREEN);
+
+        mMagnificationController.onTripleTapped(TEST_DISPLAY, MODE_FULLSCREEN);
+
+        verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
+                eq(MODE_FULLSCREEN));
+    }
+
+    @Test
+    public void onTripleTap_windowModeDisabled_removeMagnificationButton()
+            throws RemoteException {
+
+        mMagnificationController.onTripleTapped(TEST_DISPLAY, MODE_WINDOW);
+
+        verify(mWindowMagnificationManager).removeMagnificationButton(eq(TEST_DISPLAY));
+    }
+
     private void setMagnificationEnabled(int mode) throws RemoteException {
 
         setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
new file mode 100644
index 0000000..514d16a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.server.accessibility.magnification;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_UP;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.testng.AssertJUnit.assertTrue;
+
+import android.annotation.NonNull;
+import android.provider.Settings;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link MagnificationGestureHandler}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class MagnificationGestureHandlerTest {
+
+    private TestMagnificationGestureHandler mMgh;
+    private static final int DISPLAY_0 = 0;
+    private static final int FULLSCREEN_MODE =
+            Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+
+    @Mock
+    MagnificationGestureHandler.Callback mCallback;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mMgh = new TestMagnificationGestureHandler(DISPLAY_0,
+                /* detectTripleTap= */true,
+                /* detectShortcutTrigger= */true,
+                mCallback);
+    }
+
+    @Test
+    public void onMotionEvent_isFromScreen_onMotionEventInternal() {
+        final MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+        downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+
+        mMgh.onMotionEvent(downEvent, downEvent, /* policyFlags= */ 0);
+
+        try {
+            assertTrue(mMgh.mIsInternalMethodCalled);
+        } finally {
+            downEvent.recycle();
+        }
+    }
+
+    @Test
+    public void onMotionEvent_downEvent_handleInteractionStart() {
+        final MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+        downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+
+        mMgh.onMotionEvent(downEvent, downEvent, /* policyFlags= */ 0);
+
+        try {
+            verify(mCallback).onTouchInteractionStart(eq(DISPLAY_0), eq(mMgh.getMode()));
+        } finally {
+            downEvent.recycle();
+        }
+    }
+
+    @Test
+    public void onMotionEvent_upEvent_handleInteractionEnd() {
+        final MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0);
+        upEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+
+        mMgh.onMotionEvent(upEvent, upEvent, /* policyFlags= */ 0);
+
+        try {
+            verify(mCallback).onTouchInteractionEnd(eq(DISPLAY_0), eq(mMgh.getMode()));
+        } finally {
+            upEvent.recycle();
+        }
+    }
+
+    @Test
+    public void onMotionEvent_cancelEvent_handleInteractionEnd() {
+        final MotionEvent cancelEvent = MotionEvent.obtain(0, 0, ACTION_CANCEL, 0, 0, 0);
+        cancelEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+
+        mMgh.onMotionEvent(cancelEvent, cancelEvent, /* policyFlags= */ 0);
+
+        try {
+            verify(mCallback).onTouchInteractionEnd(eq(DISPLAY_0), eq(mMgh.getMode()));
+        } finally {
+            cancelEvent.recycle();
+        }
+    }
+
+
+    @Test
+    public void notifyShortcutTriggered_callsOnShortcutTriggered() {
+        mMgh.notifyShortcutTriggered();
+
+        verify(mCallback).onShortcutTriggered(eq(DISPLAY_0), eq(mMgh.getMode()));
+    }
+
+    private static class TestMagnificationGestureHandler extends MagnificationGestureHandler {
+
+        boolean mIsInternalMethodCalled = false;
+
+        TestMagnificationGestureHandler(int displayId, boolean detectTripleTap,
+                boolean detectShortcutTrigger, @NonNull Callback callback) {
+            super(displayId, detectTripleTap, detectShortcutTrigger, callback);
+        }
+
+        @Override
+        void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+            mIsInternalMethodCalled = true;
+        }
+
+        @Override
+        public void notifyShortcutTriggered() {
+            super.notifyShortcutTriggered();
+        }
+
+        @Override
+        public void handleShortcutTriggered() {
+        }
+
+        @Override
+        public int getMode() {
+            return FULLSCREEN_MODE;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index 9f930da..4b7ebbc 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -19,7 +19,9 @@
 import static com.android.server.testutils.TestUtils.strictMock;
 
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
 import android.content.Context;
 import android.graphics.Rect;
@@ -38,6 +40,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.util.function.IntConsumer;
 
@@ -48,7 +52,7 @@
 public class WindowMagnificationGestureHandlerTest {
 
     public static final int STATE_IDLE = 1;
-    public static final int STATE_SHOW_MAGNIFIER = 2;
+    public static final int STATE_SHOW_MAGNIFIER_SHORTCUT = 2;
     public static final int STATE_TWO_FINGERS_DOWN = 3;
     public static final int STATE_SHOW_MAGNIFIER_TRIPLE_TAP = 4;
     //TODO: Test it after can injecting Handler to GestureMatcher is available.
@@ -65,16 +69,18 @@
     private WindowMagnificationManager mWindowMagnificationManager;
     private MockWindowMagnificationConnection mMockConnection;
     private WindowMagnificationGestureHandler mWindowMagnificationGestureHandler;
+    @Mock
+    MagnificationGestureHandler.Callback mMockCallback;
 
     @Before
     public void setUp() throws RemoteException {
+        MockitoAnnotations.initMocks(this);
         mContext = InstrumentationRegistry.getContext();
         mWindowMagnificationManager = new WindowMagnificationManager(mContext, 0,
                 mock(WindowMagnificationManager.Callback.class));
         mMockConnection = new MockWindowMagnificationConnection();
         mWindowMagnificationGestureHandler = new WindowMagnificationGestureHandler(
-                mContext, mWindowMagnificationManager, mock(
-                MagnificationGestureHandler.ScaleChangedListener.class),
+                mContext, mWindowMagnificationManager, mMockCallback,
                 /** detectTripleTap= */true,   /** detectShortcutTrigger= */true, DISPLAY_0);
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
         mWindowMagnificationGestureHandler.setNext(strictMock(EventStreamTransformation.class));
@@ -140,6 +146,14 @@
         });
     }
 
+    @Test
+    public void onTripleTap_callsOnTripleTapped() {
+        goFromStateIdleTo(STATE_SHOW_MAGNIFIER_TRIPLE_TAP);
+
+        verify(mMockCallback).onTripleTapped(eq(DISPLAY_0),
+                eq(mWindowMagnificationGestureHandler.getMode()));
+    }
+
     private void forEachState(IntConsumer action) {
         for (int state = FIRST_STATE; state <= LAST_STATE; state++) {
             action.accept(state);
@@ -159,7 +173,7 @@
                         == mWindowMagnificationGestureHandler.mDetectingState, state);
             }
             break;
-            case STATE_SHOW_MAGNIFIER:
+            case STATE_SHOW_MAGNIFIER_SHORTCUT:
             case STATE_SHOW_MAGNIFIER_TRIPLE_TAP: {
                 check(isWindowMagnifierEnabled(DISPLAY_0), state);
                 check(mWindowMagnificationGestureHandler.mCurrentState
@@ -188,12 +202,12 @@
                     // no op
                 }
                 break;
-                case STATE_SHOW_MAGNIFIER: {
+                case STATE_SHOW_MAGNIFIER_SHORTCUT: {
                     triggerShortcut();
                 }
                 break;
                 case STATE_TWO_FINGERS_DOWN: {
-                    goFromStateIdleTo(STATE_SHOW_MAGNIFIER);
+                    goFromStateIdleTo(STATE_SHOW_MAGNIFIER_SHORTCUT);
                     final Rect frame = mMockConnection.getMirrorWindowFrame();
                     send(downEvent(frame.centerX(), frame.centerY()));
                     //Second finger is outside the window.
@@ -225,14 +239,14 @@
                 // no op
             }
             break;
-            case STATE_SHOW_MAGNIFIER: {
+            case STATE_SHOW_MAGNIFIER_SHORTCUT: {
                 mWindowMagnificationManager.disableWindowMagnification(DISPLAY_0, false);
             }
             break;
             case STATE_TWO_FINGERS_DOWN: {
                 final Rect frame = mMockConnection.getMirrorWindowFrame();
                 send(upEvent(frame.centerX(), frame.centerY()));
-                returnToNormalFrom(STATE_SHOW_MAGNIFIER);
+                returnToNormalFrom(STATE_SHOW_MAGNIFIER_SHORTCUT);
             }
             break;
             case STATE_SHOW_MAGNIFIER_TRIPLE_TAP: {
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
new file mode 100644
index 0000000..b5f4912
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
@@ -0,0 +1,426 @@
+/*
+ * 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.
+ */
+
+// TODO(b/169883602): This is purposely a different package from the path so that it can access
+// AppSearchImpl's methods without having to make them public. This should be replaced by proper
+// global query integration tests that can test AppSearchImpl-VisibilityStore integration logic.
+package com.android.server.appsearch.external.localstorage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.appsearch.AppSearchSchema;
+import android.app.appsearch.GenericDocument;
+import android.app.appsearch.PackageIdentifier;
+import android.app.appsearch.SearchResultPage;
+import android.app.appsearch.SearchSpec;
+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;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.util.Collections;
+import java.util.List;
+
+/** This tests AppSearchImpl when it's running with a platform-backed VisibilityStore. */
+public class AppSearchImplPlatformTest {
+    @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 {
+        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,
+                        mContext.getUserId(),
+                        mContext.getPackageName());
+        mGlobalQuerierUid =
+                mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
+    }
+    /**
+     * TODO(b/169883602): This should be an integration test at the cts-level. This is a short-term
+     * test until we have official support for multiple-apps indexing at once.
+     */
+    @Test
+    public void testGlobalQueryWithMultiplePackages_noPackageFilters() throws Exception {
+        // Insert package1 schema
+        List<AppSearchSchema> schema1 =
+                ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
+        mAppSearchImpl.setSchema(
+                "package1",
+                "database1",
+                schema1,
+                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false);
+
+        // Insert package2 schema
+        List<AppSearchSchema> schema2 =
+                ImmutableList.of(new AppSearchSchema.Builder("schema2").build());
+        mAppSearchImpl.setSchema(
+                "package2",
+                "database2",
+                schema2,
+                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false);
+
+        // Insert package1 document
+        GenericDocument document1 =
+                new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
+        mAppSearchImpl.putDocument("package1", "database1", document1);
+
+        // Insert package2 document
+        GenericDocument document2 =
+                new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
+        mAppSearchImpl.putDocument("package2", "database2", document2);
+
+        // No query filters specified, global query can retrieve all documents.
+        SearchSpec searchSpec =
+                new SearchSpec.Builder().setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY).build();
+        SearchResultPage searchResultPage =
+                mAppSearchImpl.globalQuery(
+                        "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
+        assertThat(searchResultPage.getResults()).hasSize(2);
+
+        // Document2 will be first since it got indexed later and has a "better", aka more recent
+        // score.
+        assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document2);
+        assertThat(searchResultPage.getResults().get(1).getDocument()).isEqualTo(document1);
+    }
+
+    /**
+     * TODO(b/169883602): This should be an integration test at the cts-level. This is a short-term
+     * test until we have official support for multiple-apps indexing at once.
+     */
+    @Test
+    public void testGlobalQueryWithMultiplePackages_withPackageFilters() throws Exception {
+        // Insert package1 schema
+        List<AppSearchSchema> schema1 =
+                ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
+        mAppSearchImpl.setSchema(
+                "package1",
+                "database1",
+                schema1,
+                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false);
+
+        // Insert package2 schema
+        List<AppSearchSchema> schema2 =
+                ImmutableList.of(new AppSearchSchema.Builder("schema2").build());
+        mAppSearchImpl.setSchema(
+                "package2",
+                "database2",
+                schema2,
+                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false);
+
+        // Insert package1 document
+        GenericDocument document1 =
+                new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
+        mAppSearchImpl.putDocument("package1", "database1", document1);
+
+        // Insert package2 document
+        GenericDocument document2 =
+                new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
+        mAppSearchImpl.putDocument("package2", "database2", document2);
+
+        // "package1" filter specified
+        SearchSpec searchSpec =
+                new SearchSpec.Builder()
+                        .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+                        .addFilterPackageNames("package1")
+                        .build();
+        SearchResultPage searchResultPage =
+                mAppSearchImpl.globalQuery(
+                        "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
+        assertThat(searchResultPage.getResults()).hasSize(1);
+        assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document1);
+
+        // "package2" filter specified
+        searchSpec =
+                new SearchSpec.Builder()
+                        .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+                        .addFilterPackageNames("package2")
+                        .build();
+        searchResultPage =
+                mAppSearchImpl.globalQuery(
+                        "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
+        assertThat(searchResultPage.getResults()).hasSize(1);
+        assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document2);
+    }
+
+    @Test
+    public void testSetSchema_existingSchemaRetainsVisibilitySetting() throws Exception {
+        // 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.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
+        mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
+
+        // Set schema1
+        String prefix = AppSearchImpl.createPrefix("package", "database");
+        mAppSearchImpl.setSchema(
+                "package",
+                "database",
+                Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
+                /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
+                /*schemasPackageAccessible=*/ ImmutableMap.of(
+                        "schema1",
+                        ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
+                /*forceOverride=*/ false);
+
+        // "schema1" is platform hidden now and package visible to package1
+        assertThat(
+                        mAppSearchImpl
+                                .getVisibilityStoreLocked()
+                                .isSchemaSearchableByCaller(
+                                        prefix, prefix + "schema1", mGlobalQuerierUid))
+                .isFalse();
+        assertThat(
+                        mAppSearchImpl
+                                .getVisibilityStoreLocked()
+                                .isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
+                .isTrue();
+
+        // Add a new schema, and include the already-existing "schema1"
+        mAppSearchImpl.setSchema(
+                "package",
+                "database",
+                ImmutableList.of(
+                        new AppSearchSchema.Builder("schema1").build(),
+                        new AppSearchSchema.Builder("schema2").build()),
+                /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
+                /*schemasPackageAccessible=*/ ImmutableMap.of(
+                        "schema1",
+                        ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
+                /*forceOverride=*/ false);
+
+        // Check that "schema1" still has the same visibility settings
+        assertThat(
+                        mAppSearchImpl
+                                .getVisibilityStoreLocked()
+                                .isSchemaSearchableByCaller(
+                                        prefix, prefix + "schema1", mGlobalQuerierUid))
+                .isFalse();
+        assertThat(
+                        mAppSearchImpl
+                                .getVisibilityStoreLocked()
+                                .isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
+                .isTrue();
+
+        // "schema2" has default visibility settings
+        assertThat(
+                        mAppSearchImpl
+                                .getVisibilityStoreLocked()
+                                .isSchemaSearchableByCaller(
+                                        prefix, prefix + "schema2", mGlobalQuerierUid))
+                .isTrue();
+        assertThat(
+                        mAppSearchImpl
+                                .getVisibilityStoreLocked()
+                                .isSchemaSearchableByCaller(prefix, prefix + "schema2", uidFoo))
+                .isFalse();
+    }
+
+    @Test
+    public void testRemoveSchema_removedFromVisibilityStore() throws Exception {
+        // 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.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
+        mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
+
+        String prefix = AppSearchImpl.createPrefix("package", "database");
+        mAppSearchImpl.setSchema(
+                "package",
+                "database",
+                Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
+                /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
+                /*schemasPackageAccessible=*/ ImmutableMap.of(
+                        "schema1",
+                        ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
+                /*forceOverride=*/ false);
+
+        // "schema1" is platform hidden now and package accessible
+        assertThat(
+                        mAppSearchImpl
+                                .getVisibilityStoreLocked()
+                                .isSchemaSearchableByCaller(
+                                        prefix, prefix + "schema1", mGlobalQuerierUid))
+                .isFalse();
+        assertThat(
+                        mAppSearchImpl
+                                .getVisibilityStoreLocked()
+                                .isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
+                .isTrue();
+
+        // Remove "schema1" by force overriding
+        mAppSearchImpl.setSchema(
+                "package",
+                "database",
+                /*schemas=*/ Collections.emptyList(),
+                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*forceOverride=*/ true);
+
+        // Check that "schema1" is no longer considered platform hidden or package accessible
+        assertThat(
+                        mAppSearchImpl
+                                .getVisibilityStoreLocked()
+                                .isSchemaSearchableByCaller(
+                                        prefix, prefix + "schema1", mGlobalQuerierUid))
+                .isTrue();
+        assertThat(
+                        mAppSearchImpl
+                                .getVisibilityStoreLocked()
+                                .isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
+                .isFalse();
+
+        // Add "schema1" back, it gets default visibility settings which means it's not platform
+        // hidden and not package accessible
+        mAppSearchImpl.setSchema(
+                "package",
+                "database",
+                Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
+                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false);
+        assertThat(
+                        mAppSearchImpl
+                                .getVisibilityStoreLocked()
+                                .isSchemaSearchableByCaller(
+                                        prefix, prefix + "schema1", mGlobalQuerierUid))
+                .isTrue();
+        assertThat(
+                        mAppSearchImpl
+                                .getVisibilityStoreLocked()
+                                .isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
+                .isFalse();
+    }
+
+    @Test
+    public void testSetSchema_defaultPlatformVisible() throws Exception {
+        String prefix = AppSearchImpl.createPrefix("package", "database");
+        mAppSearchImpl.setSchema(
+                "package",
+                "database",
+                Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
+                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false);
+        assertThat(
+                        mAppSearchImpl
+                                .getVisibilityStoreLocked()
+                                .isSchemaSearchableByCaller(
+                                        prefix, prefix + "Schema", mGlobalQuerierUid))
+                .isTrue();
+    }
+
+    @Test
+    public void testSetSchema_platformHidden() throws Exception {
+        String prefix = AppSearchImpl.createPrefix("package", "database");
+        mAppSearchImpl.setSchema(
+                "package",
+                "database",
+                Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
+                /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("Schema"),
+                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false);
+        assertThat(
+                        mAppSearchImpl
+                                .getVisibilityStoreLocked()
+                                .isSchemaSearchableByCaller(
+                                        prefix, prefix + "Schema", mGlobalQuerierUid))
+                .isFalse();
+    }
+
+    @Test
+    public void testSetSchema_defaultNotPackageAccessible() throws Exception {
+        String prefix = AppSearchImpl.createPrefix("package", "database");
+        mAppSearchImpl.setSchema(
+                "package",
+                "database",
+                Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
+                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false);
+        assertThat(
+                        mAppSearchImpl
+                                .getVisibilityStoreLocked()
+                                .isSchemaSearchableByCaller(
+                                        prefix, prefix + "Schema", /*callerUid=*/ 42))
+                .isFalse();
+    }
+
+    @Test
+    public void testSetSchema_packageAccessible() throws Exception {
+        // 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.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
+        mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
+
+        String prefix = AppSearchImpl.createPrefix("package", "database");
+        mAppSearchImpl.setSchema(
+                "package",
+                "database",
+                Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
+                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+                /*schemasPackageAccessible=*/ ImmutableMap.of(
+                        "Schema",
+                        ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
+                /*forceOverride=*/ false);
+        assertThat(
+                        mAppSearchImpl
+                                .getVisibilityStoreLocked()
+                                .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/MockPackageManager.java
similarity index 70%
rename from services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/MockPackageManager.java
rename to services/tests/servicestests/src/com/android/server/appsearch/MockPackageManager.java
index c2a8243..459fc53 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/MockPackageManager.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/MockPackageManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+// TODO(b/169883602): This is purposely a different package from the path so that AppSearchImplTest
+// can use it without an extra import. This should be moved into a proper package once
+// AppSearchImpl-VisibilityStore's dependencies are refactored.
 package com.android.server.appsearch.external.localstorage;
 
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -21,16 +24,13 @@
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.content.pm.PackageManager;
 
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-/**
- * Mock to help test package name, UID, and certificate verification
- *
- * @hide
- */
+/** Mock to help test package name, UID, and certificate verification. */
 public class MockPackageManager {
 
     @Mock private PackageManager mMockPackageManager;
@@ -47,7 +47,10 @@
     /** Mock a NameNotFoundException if the package name isn't installed. */
     public void mockThrowsNameNotFoundException(String packageName) {
         try {
-            when(mMockPackageManager.getPackageUid(eq(packageName), /*flags=*/ anyInt()))
+            when(mMockPackageManager.getPackageUidAsUser(eq(packageName), /*userId=*/ anyInt()))
+                    .thenThrow(new PackageManager.NameNotFoundException());
+            when(mMockPackageManager.getPackageUidAsUser(
+                            eq(packageName), /*flags=*/ anyInt(), /*userId=*/ anyInt()))
                     .thenThrow(new PackageManager.NameNotFoundException());
         } catch (PackageManager.NameNotFoundException e) {
             // Shouldn't ever happen since we're mocking the exception
@@ -56,9 +59,12 @@
     }
 
     /** Mocks that {@code uid} contains the {@code packageName} */
-    public void mockGetPackageUid(String packageName, int uid) {
+    public void mockGetPackageUidAsUser(String packageName, @UserIdInt int callerUserId, int uid) {
         try {
-            when(mMockPackageManager.getPackageUid(eq(packageName), /*flags=*/ anyInt()))
+            when(mMockPackageManager.getPackageUidAsUser(eq(packageName), eq(callerUserId)))
+                    .thenReturn(uid);
+            when(mMockPackageManager.getPackageUidAsUser(
+                            eq(packageName), /*flags=*/ anyInt(), eq(callerUserId)))
                     .thenReturn(uid);
         } catch (PackageManager.NameNotFoundException e) {
             // Shouldn't ever happen since we're mocking the method.
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java b/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java
similarity index 91%
rename from services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java
rename to services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java
index da3e999..8d35ebe 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+// TODO(b/169883602): This is purposely a different package from the path so that it can access
+// AppSearchImpl and VisibilityStore methods without having to make methods public. This should be
+// moved into a proper package once AppSearchImpl-VisibilityStore's dependencies are refactored.
 package com.android.server.appsearch.external.localstorage;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -61,6 +64,7 @@
                 AppSearchImpl.create(
                         mTemporaryFolder.newFolder(),
                         mContext,
+                        mContext.getUserId(),
                         /*globalQuerierPackage=*/ mContext.getPackageName());
         mGlobalQuerierUid =
                 mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
@@ -164,6 +168,7 @@
                 AppSearchImpl.create(
                         mTemporaryFolder.newFolder(),
                         mContext,
+                        mContext.getUserId(),
                         /*globalQuerierPackage=*/ mContext.getPackageName());
         VisibilityStore visibilityStore = appSearchImpl.getVisibilityStoreLocked();
 
@@ -211,7 +216,7 @@
                         ImmutableList.of(new PackageIdentifier(packageNameBar, sha256CertBar))));
 
         // Should fail if PackageManager doesn't see that it has the proper certificate
-        mMockPackageManager.mockGetPackageUid(packageNameFoo, uidFoo);
+        mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
         mMockPackageManager.mockRemoveSigningCertificate(packageNameFoo, sha256CertFoo);
         assertThat(
                         mVisibilityStore.isSchemaSearchableByCaller(
@@ -219,7 +224,8 @@
                 .isFalse();
 
         // Should fail if PackageManager doesn't think the package belongs to the uid
-        mMockPackageManager.mockGetPackageUid(packageNameFoo, uidNotFooOrBar);
+        mMockPackageManager.mockGetPackageUidAsUser(
+                packageNameFoo, mContext.getUserId(), uidNotFooOrBar);
         mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
         assertThat(
                         mVisibilityStore.isSchemaSearchableByCaller(
@@ -227,14 +233,14 @@
                 .isFalse();
 
         // But if uid and certificate match, then we should have access
-        mMockPackageManager.mockGetPackageUid(packageNameFoo, uidFoo);
+        mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
         mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
         assertThat(
                         mVisibilityStore.isSchemaSearchableByCaller(
                                 "prefix", "prefix/schemaFoo", uidFoo))
                 .isTrue();
 
-        mMockPackageManager.mockGetPackageUid(packageNameBar, uidBar);
+        mMockPackageManager.mockGetPackageUidAsUser(packageNameBar, mContext.getUserId(), uidBar);
         mMockPackageManager.mockAddSigningCertificate(packageNameBar, sha256CertBar);
         assertThat(
                         mVisibilityStore.isSchemaSearchableByCaller(
@@ -250,14 +256,14 @@
                         "prefix/schemaFoo",
                         ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))));
 
-        mMockPackageManager.mockGetPackageUid(packageNameFoo, uidFoo);
+        mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
         mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
         assertThat(
                         mVisibilityStore.isSchemaSearchableByCaller(
                                 "prefix", "prefix/schemaFoo", uidFoo))
                 .isTrue();
 
-        mMockPackageManager.mockGetPackageUid(packageNameBar, uidBar);
+        mMockPackageManager.mockGetPackageUidAsUser(packageNameBar, mContext.getUserId(), uidBar);
         mMockPackageManager.mockAddSigningCertificate(packageNameBar, sha256CertBar);
         assertThat(
                         mVisibilityStore.isSchemaSearchableByCaller(
@@ -310,7 +316,7 @@
                                 /*prefix=*/ "", "schema", mGlobalQuerierUid))
                 .isTrue();
 
-        mMockPackageManager.mockGetPackageUid(packageNameFoo, uidFoo);
+        mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), 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/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
index b2a860d..8d744c4 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
@@ -16,20 +16,21 @@
 
 package com.android.server.appsearch.external.localstorage;
 
+import static android.app.appsearch.AppSearchResult.RESULT_OK;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.testng.Assert.expectThrows;
 
 import android.app.appsearch.AppSearchSchema;
 import android.app.appsearch.GenericDocument;
-import android.app.appsearch.PackageIdentifier;
 import android.app.appsearch.SearchResult;
 import android.app.appsearch.SearchResultPage;
 import android.app.appsearch.SearchSpec;
+import android.app.appsearch.SetSchemaResult;
 import android.app.appsearch.exceptions.AppSearchException;
 import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.pm.PackageManager;
+import android.util.ArraySet;
 
 import androidx.test.core.app.ApplicationProvider;
 
@@ -46,7 +47,6 @@
 import com.android.server.appsearch.proto.TermMatchType;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 
 import org.junit.Before;
@@ -57,33 +57,23 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 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 {
         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);
+                        context,
+                        VisibilityStore.NO_OP_USER_ID,
+                        /*globalQuerierPackage
+                        =*/ context.getPackageName());
     }
 
     // TODO(b/175430168) add test to verify reset is working properly.
@@ -702,125 +692,10 @@
                 new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
         SearchResultPage searchResultPage =
                 mAppSearchImpl.globalQuery(
-                        "", searchSpec, mContext.getPackageName(), /*callerUid=*/ 0);
+                        "", searchSpec, /*callerPackageName=*/ "", /*callerUid=*/ 0);
         assertThat(searchResultPage.getResults()).isEmpty();
     }
 
-    /**
-     * TODO(b/169883602): This should be an integration test at the cts-level. This is a short-term
-     * test until we have official support for multiple-apps indexing at once.
-     */
-    @Test
-    public void testGlobalQueryWithMultiplePackages_noPackageFilters() throws Exception {
-        // Insert package1 schema
-        List<AppSearchSchema> schema1 =
-                ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
-        mAppSearchImpl.setSchema(
-                "package1",
-                "database1",
-                schema1,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
-                /*forceOverride=*/ false);
-
-        // Insert package2 schema
-        List<AppSearchSchema> schema2 =
-                ImmutableList.of(new AppSearchSchema.Builder("schema2").build());
-        mAppSearchImpl.setSchema(
-                "package2",
-                "database2",
-                schema2,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
-                /*forceOverride=*/ false);
-
-        // Insert package1 document
-        GenericDocument document1 =
-                new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
-        mAppSearchImpl.putDocument("package1", "database1", document1);
-
-        // Insert package2 document
-        GenericDocument document2 =
-                new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
-        mAppSearchImpl.putDocument("package2", "database2", document2);
-
-        // 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, mContext.getPackageName(), mGlobalQuerierUid);
-        assertThat(searchResultPage.getResults()).hasSize(2);
-
-        // Document2 will be first since it got indexed later and has a "better", aka more recent
-        // score.
-        assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document2);
-        assertThat(searchResultPage.getResults().get(1).getDocument()).isEqualTo(document1);
-    }
-
-    /**
-     * TODO(b/169883602): This should be an integration test at the cts-level. This is a short-term
-     * test until we have official support for multiple-apps indexing at once.
-     */
-    @Test
-    public void testGlobalQueryWithMultiplePackages_withPackageFilters() throws Exception {
-        // Insert package1 schema
-        List<AppSearchSchema> schema1 =
-                ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
-        mAppSearchImpl.setSchema(
-                "package1",
-                "database1",
-                schema1,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
-                /*forceOverride=*/ false);
-
-        // Insert package2 schema
-        List<AppSearchSchema> schema2 =
-                ImmutableList.of(new AppSearchSchema.Builder("schema2").build());
-        mAppSearchImpl.setSchema(
-                "package2",
-                "database2",
-                schema2,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
-                /*forceOverride=*/ false);
-
-        // Insert package1 document
-        GenericDocument document1 =
-                new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
-        mAppSearchImpl.putDocument("package1", "database1", document1);
-
-        // Insert package2 document
-        GenericDocument document2 =
-                new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
-        mAppSearchImpl.putDocument("package2", "database2", document2);
-
-        // "package1" filter specified
-        SearchSpec searchSpec =
-                new SearchSpec.Builder()
-                        .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
-                        .addFilterPackageNames("package1")
-                        .build();
-        SearchResultPage searchResultPage =
-                mAppSearchImpl.globalQuery(
-                        "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
-        assertThat(searchResultPage.getResults()).hasSize(1);
-        assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document1);
-
-        // "package2" filter specified
-        searchSpec =
-                new SearchSpec.Builder()
-                        .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
-                        .addFilterPackageNames("package2")
-                        .build();
-        searchResultPage =
-                mAppSearchImpl.globalQuery(
-                        "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
-        assertThat(searchResultPage.getResults()).hasSize(1);
-        assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document2);
-    }
-
     @Test
     public void testRemoveEmptyDatabase_noExceptionThrown() throws Exception {
         SearchSpec searchSpec =
@@ -874,79 +749,51 @@
     }
 
     @Test
-    public void testSetSchema_existingSchemaRetainsVisibilitySetting() throws Exception {
-        // Values for a "foo" client
-        String packageNameFoo = "packageFoo";
-        byte[] sha256CertFoo = new byte[] {10};
-        int uidFoo = 1;
+    public void testSetSchema_incompatible() throws Exception {
+        List<SchemaTypeConfigProto> existingSchemas =
+                mAppSearchImpl.getSchemaProtoLocked().getTypesList();
 
-        // 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");
+        List<AppSearchSchema> oldSchemas = new ArrayList<>();
+        oldSchemas.add(
+                new AppSearchSchema.Builder("Email")
+                        .addProperty(
+                                new AppSearchSchema.StringPropertyConfig.Builder("foo")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setTokenizerType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .build());
+        oldSchemas.add(new AppSearchSchema.Builder("Text").build());
+        // Set schema Email to AppSearch database1
         mAppSearchImpl.setSchema(
                 "package",
-                "database",
-                Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
-                /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
-                /*schemasPackageAccessible=*/ ImmutableMap.of(
-                        "schema1",
-                        ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
+                "database1",
+                oldSchemas,
+                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+                /*schemasPackageAccessible=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false);
 
-        // "schema1" is platform hidden now and package visible to package1
-        assertThat(
-                        mAppSearchImpl
-                                .getVisibilityStoreLocked()
-                                .isSchemaSearchableByCaller(
-                                        prefix, prefix + "schema1", mGlobalQuerierUid))
-                .isFalse();
-        assertThat(
-                        mAppSearchImpl
-                                .getVisibilityStoreLocked()
-                                .isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
-                .isTrue();
+        // Create incompatible schema
+        List<AppSearchSchema> newSchemas =
+                Collections.singletonList(new AppSearchSchema.Builder("Email").build());
 
-        // Add a new schema, and include the already-existing "schema1"
-        mAppSearchImpl.setSchema(
-                "package",
-                "database",
-                ImmutableList.of(
-                        new AppSearchSchema.Builder("schema1").build(),
-                        new AppSearchSchema.Builder("schema2").build()),
-                /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
-                /*schemasPackageAccessible=*/ ImmutableMap.of(
-                        "schema1",
-                        ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
-                /*forceOverride=*/ false);
-
-        // Check that "schema1" still has the same visibility settings
-        assertThat(
-                        mAppSearchImpl
-                                .getVisibilityStoreLocked()
-                                .isSchemaSearchableByCaller(
-                                        prefix, prefix + "schema1", mGlobalQuerierUid))
-                .isFalse();
-        assertThat(
-                        mAppSearchImpl
-                                .getVisibilityStoreLocked()
-                                .isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
-                .isTrue();
-
-        // "schema2" has default visibility settings
-        assertThat(
-                        mAppSearchImpl
-                                .getVisibilityStoreLocked()
-                                .isSchemaSearchableByCaller(
-                                        prefix, prefix + "schema2", mGlobalQuerierUid))
-                .isTrue();
-        assertThat(
-                        mAppSearchImpl
-                                .getVisibilityStoreLocked()
-                                .isSchemaSearchableByCaller(prefix, prefix + "schema2", uidFoo))
-                .isFalse();
+        // set email incompatible and delete text
+        SetSchemaResult setSchemaResult =
+                mAppSearchImpl.setSchema(
+                        "package",
+                        "database1",
+                        newSchemas,
+                        /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+                        /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                        /*forceOverride=*/ true);
+        assertThat(setSchemaResult.getDeletedSchemaTypes()).containsExactly("Text");
+        assertThat(setSchemaResult.getIncompatibleSchemaTypes()).containsExactly("Email");
+        assertThat(setSchemaResult.getResultCode()).isEqualTo(RESULT_OK);
     }
 
     @Test
@@ -989,20 +836,17 @@
 
         final List<AppSearchSchema> finalSchemas =
                 Collections.singletonList(new AppSearchSchema.Builder("Email").build());
-        // Check the incompatible error has been thrown.
-        AppSearchException e =
-                expectThrows(
-                        AppSearchException.class,
-                        () ->
-                                mAppSearchImpl.setSchema(
-                                        "package",
-                                        "database1",
-                                        finalSchemas,
-                                        /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                                        /*schemasPackageAccessible=*/ Collections.emptyMap(),
-                                        /*forceOverride=*/ false));
-        assertThat(e).hasMessageThat().contains("Schema is incompatible");
-        assertThat(e).hasMessageThat().contains("Deleted types: [package$database1/Document]");
+        SetSchemaResult setSchemaResult =
+                mAppSearchImpl.setSchema(
+                        "package",
+                        "database1",
+                        finalSchemas,
+                        /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+                        /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                        /*forceOverride=*/ false);
+
+        // Check the Document type has been deleted.
+        assertThat(setSchemaResult.getDeletedSchemaTypes()).containsExactly("Document");
 
         // ForceOverride to delete.
         mAppSearchImpl.setSchema(
@@ -1121,167 +965,6 @@
     }
 
     @Test
-    public void testRemoveSchema_removedFromVisibilityStore() throws Exception {
-        // 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(
-                "package",
-                "database",
-                Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
-                /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
-                /*schemasPackageAccessible=*/ ImmutableMap.of(
-                        "schema1",
-                        ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
-                /*forceOverride=*/ false);
-
-        // "schema1" is platform hidden now and package accessible
-        assertThat(
-                        mAppSearchImpl
-                                .getVisibilityStoreLocked()
-                                .isSchemaSearchableByCaller(
-                                        prefix, prefix + "schema1", mGlobalQuerierUid))
-                .isFalse();
-        assertThat(
-                        mAppSearchImpl
-                                .getVisibilityStoreLocked()
-                                .isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
-                .isTrue();
-
-        // Remove "schema1" by force overriding
-        mAppSearchImpl.setSchema(
-                "package",
-                "database",
-                /*schemas=*/ Collections.emptyList(),
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
-                /*forceOverride=*/ true);
-
-        // Check that "schema1" is no longer considered platform hidden or package accessible
-        assertThat(
-                        mAppSearchImpl
-                                .getVisibilityStoreLocked()
-                                .isSchemaSearchableByCaller(
-                                        prefix, prefix + "schema1", mGlobalQuerierUid))
-                .isTrue();
-        assertThat(
-                        mAppSearchImpl
-                                .getVisibilityStoreLocked()
-                                .isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
-                .isFalse();
-
-        // Add "schema1" back, it gets default visibility settings which means it's not platform
-        // hidden and not package accessible
-        mAppSearchImpl.setSchema(
-                "package",
-                "database",
-                Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
-                /*forceOverride=*/ false);
-        assertThat(
-                        mAppSearchImpl
-                                .getVisibilityStoreLocked()
-                                .isSchemaSearchableByCaller(
-                                        prefix, prefix + "schema1", mGlobalQuerierUid))
-                .isTrue();
-        assertThat(
-                        mAppSearchImpl
-                                .getVisibilityStoreLocked()
-                                .isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
-                .isFalse();
-    }
-
-    @Test
-    public void testSetSchema_defaultPlatformVisible() throws Exception {
-        String prefix = AppSearchImpl.createPrefix("package", "database");
-        mAppSearchImpl.setSchema(
-                "package",
-                "database",
-                Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
-                /*forceOverride=*/ false);
-        assertThat(
-                        mAppSearchImpl
-                                .getVisibilityStoreLocked()
-                                .isSchemaSearchableByCaller(
-                                        prefix, prefix + "Schema", mGlobalQuerierUid))
-                .isTrue();
-    }
-
-    @Test
-    public void testSetSchema_platformHidden() throws Exception {
-        String prefix = AppSearchImpl.createPrefix("package", "database");
-        mAppSearchImpl.setSchema(
-                "package",
-                "database",
-                Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
-                /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("Schema"),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
-                /*forceOverride=*/ false);
-        assertThat(
-                        mAppSearchImpl
-                                .getVisibilityStoreLocked()
-                                .isSchemaSearchableByCaller(
-                                        prefix, prefix + "Schema", mGlobalQuerierUid))
-                .isFalse();
-    }
-
-    @Test
-    public void testSetSchema_defaultNotPackageAccessible() throws Exception {
-        String prefix = AppSearchImpl.createPrefix("package", "database");
-        mAppSearchImpl.setSchema(
-                "package",
-                "database",
-                Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
-                /*forceOverride=*/ false);
-        assertThat(
-                        mAppSearchImpl
-                                .getVisibilityStoreLocked()
-                                .isSchemaSearchableByCaller(
-                                        prefix, prefix + "Schema", /*callerUid=*/ 42))
-                .isFalse();
-    }
-
-    @Test
-    public void testSetSchema_packageAccessible() throws Exception {
-        // 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(
-                "package",
-                "database",
-                Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ ImmutableMap.of(
-                        "Schema",
-                        ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
-                /*forceOverride=*/ false);
-        assertThat(
-                        mAppSearchImpl
-                                .getVisibilityStoreLocked()
-                                .isSchemaSearchableByCaller(prefix, prefix + "Schema", uidFoo))
-                .isTrue();
-    }
-
-    @Test
     public void testHasSchemaType() throws Exception {
         // Nothing exists yet
         assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "Schema")).isFalse();
@@ -1300,14 +983,12 @@
     }
 
     @Test
-    public void testGetDatabases() throws Exception {
-        // No client databases exist yet, but the VisibilityStore's does
-        assertThat(mAppSearchImpl.getPrefixesLocked())
-                .containsExactly(
-                        AppSearchImpl.createPrefix(
-                                VisibilityStore.PACKAGE_NAME, VisibilityStore.DATABASE_NAME));
+    public void testGetPrefixes() throws Exception {
+        Set<String> existingPrefixes = mAppSearchImpl.getPrefixesLocked();
 
         // Has database1
+        Set<String> expectedPrefixes = new ArraySet<>(existingPrefixes);
+        expectedPrefixes.add(AppSearchImpl.createPrefix("package", "database1"));
         mAppSearchImpl.setSchema(
                 "package",
                 "database1",
@@ -1315,13 +996,10 @@
                 /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
                 /*schemasPackageAccessible=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false);
-        assertThat(mAppSearchImpl.getPrefixesLocked())
-                .containsExactly(
-                        AppSearchImpl.createPrefix(
-                                VisibilityStore.PACKAGE_NAME, VisibilityStore.DATABASE_NAME),
-                        AppSearchImpl.createPrefix("package", "database1"));
+        assertThat(mAppSearchImpl.getPrefixesLocked()).containsExactlyElementsIn(expectedPrefixes);
 
         // Has both databases
+        expectedPrefixes.add(AppSearchImpl.createPrefix("package", "database2"));
         mAppSearchImpl.setSchema(
                 "package",
                 "database2",
@@ -1329,12 +1007,7 @@
                 /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
                 /*schemasPackageAccessible=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false);
-        assertThat(mAppSearchImpl.getPrefixesLocked())
-                .containsExactly(
-                        AppSearchImpl.createPrefix(
-                                VisibilityStore.PACKAGE_NAME, VisibilityStore.DATABASE_NAME),
-                        AppSearchImpl.createPrefix("package", "database1"),
-                        AppSearchImpl.createPrefix("package", "database2"));
+        assertThat(mAppSearchImpl.getPrefixesLocked()).containsExactlyElementsIn(expectedPrefixes);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
index 4ecaac5..79a5ed6 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -17,6 +17,7 @@
 
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -27,6 +28,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
+import android.content.Intent;
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.util.Log;
@@ -58,7 +60,7 @@
     @Mock private AudioService mMockAudioService;
     @Spy private AudioDeviceInventory mSpyDevInventory;
     @Spy private AudioSystemAdapter mSpyAudioSystem;
-    private SystemServerAdapter mSystemServer;
+    @Spy private SystemServerAdapter mSpySystemServer;
 
     private BluetoothDevice mFakeBtDevice;
 
@@ -69,9 +71,9 @@
         mMockAudioService = mock(AudioService.class);
         mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
         mSpyDevInventory = spy(new AudioDeviceInventory(mSpyAudioSystem));
-        mSystemServer = new NoOpSystemServerAdapter();
+        mSpySystemServer = spy(new NoOpSystemServerAdapter());
         mAudioDeviceBroker = new AudioDeviceBroker(mContext, mMockAudioService, mSpyDevInventory,
-                mSystemServer);
+                mSpySystemServer);
         mSpyDevInventory.setDeviceBroker(mAudioDeviceBroker);
 
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -172,6 +174,30 @@
                 true);
     }
 
+    /**
+     * Test that device wired state intents are broadcasted on connection state change
+     * @throws Exception
+     */
+    @Test
+    public void testSetWiredDeviceConnectionState() throws Exception {
+        Log.i(TAG, "starting postSetWiredDeviceConnectionState");
+
+        final String address = "testAddress";
+        final String name = "testName";
+        final String caller = "testCaller";
+
+        doNothing().when(mSpySystemServer).broadcastStickyIntentToCurrentProfileGroup(
+                any(Intent.class));
+
+        mSpyDevInventory.setWiredDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
+                AudioService.CONNECTION_STATE_CONNECTED, address, name, caller);
+        Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
+
+        // Verify that the sticky intent is broadcasted
+        verify(mSpySystemServer, times(1)).broadcastStickyIntentToCurrentProfileGroup(
+                any(Intent.class));
+    }
+
     private void doTestConnectionDisconnectionReconnection(int delayAfterDisconnection,
             boolean mockMediaPlayback, boolean guaranteeSingleConnection) throws Exception {
         when(mMockAudioService.getDeviceForStream(AudioManager.STREAM_MUSIC))
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index cc4541b..832f918 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -34,11 +34,14 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
+import android.util.proto.ProtoOutputStream;
 
 import androidx.annotation.NonNull;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
+import com.android.server.biometrics.nano.BiometricSchedulerProto;
+import com.android.server.biometrics.nano.BiometricsProto;
 import com.android.server.biometrics.sensors.BiometricScheduler.Operation;
 
 import org.junit.Before;
@@ -52,6 +55,7 @@
 
     private static final String TAG = "BiometricSchedulerTest";
     private static final int TEST_SENSOR_ID = 1;
+    private static final int LOG_NUM_RECENT_OPERATIONS = 2;
 
     private BiometricScheduler mScheduler;
     private IBinder mToken;
@@ -66,7 +70,7 @@
         MockitoAnnotations.initMocks(this);
         mToken = new Binder();
         mScheduler = new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */,
-                mBiometricService);
+                mBiometricService, LOG_NUM_RECENT_OPERATIONS);
     }
 
     @Test
@@ -186,6 +190,88 @@
         assertNull(mScheduler.mCurrentOperation);
     }
 
+    @Test
+    public void testProtoDump_singleCurrentOperation() throws Exception {
+        // Nothing so far
+        BiometricSchedulerProto bsp = getDump(true /* clearSchedulerBuffer */);
+        assertEquals(BiometricsProto.CM_NONE, bsp.currentOperation);
+        assertEquals(0, bsp.totalOperations);
+        assertEquals(0, bsp.recentOperations.length);
+
+        // Pretend the scheduler is busy enrolling, and check the proto dump again.
+        final TestClientMonitor2 client = new TestClientMonitor2(mContext, mToken,
+                () -> mock(Object.class), BiometricsProto.CM_ENROLL);
+        mScheduler.scheduleClientMonitor(client);
+        waitForIdle();
+        bsp = getDump(true /* clearSchedulerBuffer */);
+        assertEquals(BiometricsProto.CM_ENROLL, bsp.currentOperation);
+        // No operations have completed yet
+        assertEquals(0, bsp.totalOperations);
+        assertEquals(0, bsp.recentOperations.length);
+        // Finish this operation, so the next scheduled one can start
+        client.getCallback().onClientFinished(client, true);
+    }
+
+    @Test
+    public void testProtoDump_fifo() throws Exception {
+        // Add the first operation
+        final TestClientMonitor2 client = new TestClientMonitor2(mContext, mToken,
+                () -> mock(Object.class), BiometricsProto.CM_ENROLL);
+        mScheduler.scheduleClientMonitor(client);
+        waitForIdle();
+        BiometricSchedulerProto bsp = getDump(false /* clearSchedulerBuffer */);
+        assertEquals(BiometricsProto.CM_ENROLL, bsp.currentOperation);
+        // No operations have completed yet
+        assertEquals(0, bsp.totalOperations);
+        assertEquals(0, bsp.recentOperations.length);
+        // Finish this operation, so the next scheduled one can start
+        client.getCallback().onClientFinished(client, true);
+
+        // Add another operation
+        final TestClientMonitor2 client2 = new TestClientMonitor2(mContext, mToken,
+                () -> mock(Object.class), BiometricsProto.CM_REMOVE);
+        mScheduler.scheduleClientMonitor(client2);
+        waitForIdle();
+        bsp = getDump(false /* clearSchedulerBuffer */);
+        assertEquals(BiometricsProto.CM_REMOVE, bsp.currentOperation);
+        assertEquals(1, bsp.totalOperations); // Enroll finished
+        assertEquals(1, bsp.recentOperations.length);
+        assertEquals(BiometricsProto.CM_ENROLL, bsp.recentOperations[0]);
+        client2.getCallback().onClientFinished(client2, true);
+
+        // And another operation
+        final TestClientMonitor2 client3 = new TestClientMonitor2(mContext, mToken,
+                () -> mock(Object.class), BiometricsProto.CM_AUTHENTICATE);
+        mScheduler.scheduleClientMonitor(client3);
+        waitForIdle();
+        bsp = getDump(false /* clearSchedulerBuffer */);
+        assertEquals(BiometricsProto.CM_AUTHENTICATE, bsp.currentOperation);
+        assertEquals(2, bsp.totalOperations);
+        assertEquals(2, bsp.recentOperations.length);
+        assertEquals(BiometricsProto.CM_ENROLL, bsp.recentOperations[0]);
+        assertEquals(BiometricsProto.CM_REMOVE, bsp.recentOperations[1]);
+
+        // Finish the last operation, and check that the first operation is removed from the FIFO.
+        // The test initializes the scheduler with "LOG_NUM_RECENT_OPERATIONS = 2" :)
+        client3.getCallback().onClientFinished(client3, true);
+        waitForIdle();
+        bsp = getDump(true /* clearSchedulerBuffer */);
+        assertEquals(3, bsp.totalOperations);
+        assertEquals(2, bsp.recentOperations.length);
+        assertEquals(BiometricsProto.CM_REMOVE, bsp.recentOperations[0]);
+        assertEquals(BiometricsProto.CM_AUTHENTICATE, bsp.recentOperations[1]);
+        // Nothing is currently running anymore
+        assertEquals(BiometricsProto.CM_NONE, bsp.currentOperation);
+
+        // RecentOperations queue is cleared (by the previous dump)
+        bsp = getDump(true /* clearSchedulerBuffer */);
+        assertEquals(0, bsp.recentOperations.length);
+    }
+
+    private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception {
+        return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer));
+    }
+
     private static class BiometricPromptClientMonitor extends AuthenticationClient<Object> {
 
         public BiometricPromptClientMonitor(@NonNull Context context, @NonNull IBinder token,
@@ -207,6 +293,21 @@
         }
     }
 
+    private static class TestClientMonitor2 extends TestClientMonitor {
+        private final int mProtoEnum;
+
+        public TestClientMonitor2(@NonNull Context context, @NonNull IBinder token,
+                @NonNull LazyDaemon<Object> lazyDaemon, int protoEnum) {
+            super(context, token, lazyDaemon);
+            mProtoEnum = protoEnum;
+        }
+
+        @Override
+        public int getProtoEnum() {
+            return mProtoEnum;
+        }
+    }
+
     private static class TestClientMonitor extends HalClientMonitor<Object> {
         private boolean mUnableToStart;
         private boolean mStarted;
@@ -230,6 +331,13 @@
         }
 
         @Override
+        public int getProtoEnum() {
+            // Anything other than CM_NONE, which is used to represent "idle". Tests that need
+            // real proto enums should use TestClientMonitor2
+            return BiometricsProto.CM_UPDATE_ACTIVE_USER;
+        }
+
+        @Override
         public void start(@NonNull Callback callback) {
             super.start(callback);
             assertFalse(mStarted);
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 065a2f3..2e97a8d 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -31,6 +31,7 @@
 import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
 import static android.app.admin.PasswordMetrics.computeForPassword;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
+import static android.net.InetAddresses.parseNumericAddress;
 
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
 import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
@@ -66,6 +67,8 @@
 import static org.mockito.hamcrest.MockitoHamcrest.argThat;
 import static org.testng.Assert.assertThrows;
 
+import static java.util.Collections.emptyList;
+
 import android.Manifest.permission;
 import android.app.Activity;
 import android.app.AppOpsManager;
@@ -126,6 +129,8 @@
 import org.mockito.stubbing.Answer;
 
 import java.io.File;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -2360,6 +2365,49 @@
     }
 
     @Test
+    public void testGetProxyParameters() throws Exception {
+        assertThat(dpm.getProxyParameters(inetAddrProxy("192.0.2.1", 1234), emptyList()))
+                .isEqualTo(new Pair<>("192.0.2.1:1234", ""));
+        assertThat(dpm.getProxyParameters(inetAddrProxy("192.0.2.1", 1234),
+                listOf("one.example.com  ", "  two.example.com ")))
+                .isEqualTo(new Pair<>("192.0.2.1:1234", "one.example.com,two.example.com"));
+        assertThat(dpm.getProxyParameters(hostnameProxy("proxy.example.com", 1234), emptyList()))
+                .isEqualTo(new Pair<>("proxy.example.com:1234", ""));
+        assertThat(dpm.getProxyParameters(hostnameProxy("proxy.example.com", 1234),
+                listOf("excluded.example.com")))
+                .isEqualTo(new Pair<>("proxy.example.com:1234", "excluded.example.com"));
+
+        assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters(
+                inetAddrProxy("192.0.2.1", 0), emptyList()));
+        assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters(
+                hostnameProxy("", 1234), emptyList()));
+        assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters(
+                hostnameProxy("", 0), emptyList()));
+        assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters(
+                hostnameProxy("invalid! hostname", 1234), emptyList()));
+        assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters(
+                hostnameProxy("proxy.example.com", 1234), listOf("invalid exclusion")));
+        assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters(
+                hostnameProxy("proxy.example.com", -1), emptyList()));
+        assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters(
+                hostnameProxy("proxy.example.com", 0xFFFF + 1), emptyList()));
+    }
+
+    private static Proxy inetAddrProxy(String inetAddr, int port) {
+        return new Proxy(
+                Proxy.Type.HTTP, new InetSocketAddress(parseNumericAddress(inetAddr), port));
+    }
+
+    private static Proxy hostnameProxy(String hostname, int port) {
+        return new Proxy(
+                Proxy.Type.HTTP, InetSocketAddress.createUnresolved(hostname, port));
+    }
+
+    private static List<String> listOf(String... args) {
+        return Arrays.asList(args);
+    }
+
+    @Test
     public void testSetKeyguardDisabledFeaturesWithDO() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
@@ -3289,7 +3337,7 @@
                 .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(false);
         initializeDpms();
         when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(false);
-        when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+        when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, false))
                 .thenReturn(true);
         setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM);
 
@@ -3331,7 +3379,7 @@
                 .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(false);
         initializeDpms();
         when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(false);
-        when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+        when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, false))
                 .thenReturn(true);
         setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM);
 
@@ -3395,7 +3443,7 @@
         when(getServices().ipackageManager
                 .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(true);
         when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(false);
-        when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+        when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, false))
                 .thenReturn(true);
         setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM);
         when(getServices().userManager.getProfileParent(UserHandle.USER_SYSTEM)).thenReturn(null);
@@ -3439,7 +3487,7 @@
         when(getServices().ipackageManager
                 .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(true);
         when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(false);
-        when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+        when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, false))
                 .thenReturn(true);
         setUserSetupCompleteForUser(true, UserHandle.USER_SYSTEM);
         when(getServices().userManager.getProfileParent(UserHandle.USER_SYSTEM)).thenReturn(null);
@@ -3459,8 +3507,6 @@
         final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 1308);
         when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM,
                 false /* we can't remove a managed profile */)).thenReturn(false);
-        when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM,
-                true)).thenReturn(true);
     }
 
     @Test
@@ -3620,7 +3666,7 @@
         when(getServices().ipackageManager
                 .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(true);
         when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true);
-        when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+        when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, false))
                 .thenReturn(false);
         setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM);
 
@@ -3664,7 +3710,7 @@
         when(getServices().ipackageManager
                 .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(true);
         when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true);
-        when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+        when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, false))
                 .thenReturn(false);
         setUserSetupCompleteForUser(true, UserHandle.USER_SYSTEM);
 
@@ -3711,7 +3757,7 @@
                 .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(true);
         when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true);
         when(getServices().userManager.canAddMoreManagedProfiles(CALLER_USER_HANDLE,
-                true)).thenReturn(true);
+                false)).thenReturn(true);
         setUserSetupCompleteForUser(false, CALLER_USER_HANDLE);
 
         mContext.binder.callingUid = DpmMockContext.CALLER_UID;
@@ -3753,7 +3799,7 @@
                 .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(true);
         when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true);
         when(getServices().userManager.canAddMoreManagedProfiles(CALLER_USER_HANDLE,
-                true)).thenReturn(true);
+                false)).thenReturn(true);
         setUserSetupCompleteForUser(true, CALLER_USER_HANDLE);
 
         mContext.binder.callingUid = DpmMockContext.CALLER_UID;
@@ -3800,7 +3846,7 @@
         when(getServices().ipackageManager
                 .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(true);
         when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true);
-        when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+        when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, false))
                 .thenReturn(false);
         setUserSetupCompleteForUser(true, UserHandle.USER_SYSTEM);
 
@@ -3835,7 +3881,7 @@
         when(getServices().userManager.getProfileParent(CALLER_USER_HANDLE))
             .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
         when(getServices().userManager.canAddMoreManagedProfiles(CALLER_USER_HANDLE,
-                true)).thenReturn(true);
+                false)).thenReturn(true);
         setUserSetupCompleteForUser(false, CALLER_USER_HANDLE);
 
         mContext.binder.callingUid = DpmMockContext.ANOTHER_UID;
@@ -3861,7 +3907,7 @@
                 DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
     }
 
-    private void setup_provisionManagedProfileCantRemoveUser_primaryUser() throws Exception {
+    private void setup_provisionManagedProfileOneAlreadyExist_primaryUser() throws Exception {
         setDeviceOwner();
 
         when(getServices().ipackageManager
@@ -3873,26 +3919,24 @@
                 .thenReturn(true);
         when(getServices().userManager.canAddMoreManagedProfiles(CALLER_USER_HANDLE,
                 false /* we can't remove a managed profile */)).thenReturn(false);
-        when(getServices().userManager.canAddMoreManagedProfiles(CALLER_USER_HANDLE,
-                true)).thenReturn(true);
         setUserSetupCompleteForUser(false, CALLER_USER_HANDLE);
 
         mContext.binder.callingUid = DpmMockContext.CALLER_UID;
     }
 
     @Test
-    public void testIsProvisioningAllowed_provisionManagedProfileCantRemoveUser_primaryUser()
+    public void testIsProvisioningAllowed_provisionManagedProfile_oneAlreadyExists_primaryUser()
             throws Exception {
-        setup_provisionManagedProfileCantRemoveUser_primaryUser();
+        setup_provisionManagedProfileOneAlreadyExist_primaryUser();
         mContext.packageName = admin1.getPackageName();
         setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
     }
 
     @Test
-    public void testCheckProvisioningPreCondition_provisionManagedProfileCantRemoveUser_primaryUser()
+    public void testCheckProvisioningPreCondition_provisionManagedProfile_oneAlreadyExists_primaryUser()
             throws Exception {
-        setup_provisionManagedProfileCantRemoveUser_primaryUser();
+        setup_provisionManagedProfileOneAlreadyExist_primaryUser();
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
@@ -5494,7 +5538,7 @@
         // Attempt to set to empty list (which means no listener is allowlisted)
         mContext.binder.callingUid = adminUid;
         assertThat(dpms.setPermittedCrossProfileNotificationListeners(
-                admin1, Collections.emptyList())).isFalse();
+                admin1, emptyList())).isFalse();
         assertThat(dpms.getPermittedCrossProfileNotificationListeners(admin1)).isNull();
 
         mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
@@ -5588,7 +5632,7 @@
         // Setting an empty allowlist - only system listeners allowed
         mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
         assertThat(dpms.setPermittedCrossProfileNotificationListeners(
-                admin1, Collections.emptyList())).isTrue();
+                admin1, emptyList())).isTrue();
         assertThat(dpms.getPermittedCrossProfileNotificationListeners(admin1).size()).isEqualTo(0);
 
         mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
@@ -5653,7 +5697,7 @@
         // all allowed in primary profile
         mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
         assertThat(dpms.setPermittedCrossProfileNotificationListeners(
-                admin1, Collections.emptyList())).isTrue();
+                admin1, emptyList())).isTrue();
         assertThat(dpms.getPermittedCrossProfileNotificationListeners(admin1).size()).isEqualTo(0);
 
         mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
index 0cf0af3..75bf1e6 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
@@ -19,6 +19,8 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.junit.Assert.fail;
+
 import android.content.Context;
 import android.os.FileUtils;
 import android.platform.test.annotations.Presubmit;
@@ -35,7 +37,12 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 @Presubmit
 @SmallTest
@@ -44,17 +51,63 @@
 
     /**
      * A {@link UpdatableFontDir.FontFileParser} for testing. Instead of using real font files,
-     * this test uses fake font files. A fake font file has its version as its file content.
+     * this test uses fake font files. A fake font file has its PostScript naem and revision as the
+     * file content.
      */
     private static class FakeFontFileParser implements UpdatableFontDir.FontFileParser {
         @Override
-        public long getVersion(File file) throws IOException {
-            return Long.parseLong(FileUtils.readTextFile(file, 100, ""));
+        public String getPostScriptName(File file) throws IOException {
+            String content = FileUtils.readTextFile(file, 100, "");
+            return content.split(",")[0];
+        }
+
+        @Override
+        public long getRevision(File file) throws IOException {
+            String content = FileUtils.readTextFile(file, 100, "");
+            return Long.parseLong(content.split(",")[1]);
+        }
+    }
+
+    // FakeFsverityUtil will successfully set up fake fs-verity if the signature is GOOD_SIGNATURE.
+    private static final String GOOD_SIGNATURE = "Good signature";
+
+    /** A fake FsverityUtil to keep fake verity bit in memory. */
+    private static class FakeFsverityUtil implements UpdatableFontDir.FsverityUtil {
+        private final Set<String> mHasFsverityPaths = new HashSet<>();
+
+        public void remove(String name) {
+            mHasFsverityPaths.remove(name);
+        }
+
+        @Override
+        public boolean hasFsverity(String path) {
+            return mHasFsverityPaths.contains(path);
+        }
+
+        @Override
+        public void setUpFsverity(String path, byte[] pkcs7Signature) throws IOException {
+            String fakeSignature = new String(pkcs7Signature, StandardCharsets.UTF_8);
+            if (GOOD_SIGNATURE.equals(fakeSignature)) {
+                mHasFsverityPaths.add(path);
+            } else {
+                throw new IOException("Failed to set up fake fs-verity");
+            }
+        }
+
+        @Override
+        public boolean rename(File src, File dest) {
+            if (src.renameTo(dest)) {
+                mHasFsverityPaths.remove(src.getAbsolutePath());
+                mHasFsverityPaths.add(dest.getAbsolutePath());
+                return true;
+            }
+            return false;
         }
     }
 
     private File mCacheDir;
     private File mUpdatableFontFilesDir;
+    private List<File> mPreinstalledFontDirs;
 
     @SuppressWarnings("ResultOfMethodCallIgnored")
     @Before
@@ -65,6 +118,12 @@
         mCacheDir.mkdirs();
         mUpdatableFontFilesDir = new File(mCacheDir, "updatable_fonts");
         mUpdatableFontFilesDir.mkdir();
+        mPreinstalledFontDirs = new ArrayList<>();
+        mPreinstalledFontDirs.add(new File(mCacheDir, "system_fonts"));
+        mPreinstalledFontDirs.add(new File(mCacheDir, "product_fonts"));
+        for (File dir : mPreinstalledFontDirs) {
+            dir.mkdir();
+        }
     }
 
     @After
@@ -75,19 +134,22 @@
     @Test
     public void construct() throws Exception {
         FakeFontFileParser parser = new FakeFontFileParser();
-        UpdatableFontDir dirForPreparation = new UpdatableFontDir(mUpdatableFontFilesDir, parser);
-        installFontFile(dirForPreparation, "foo.ttf", "1");
-        installFontFile(dirForPreparation, "bar.ttf", "2");
-        installFontFile(dirForPreparation, "foo.ttf", "3");
-        installFontFile(dirForPreparation, "bar.ttf", "4");
+        FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+        UpdatableFontDir dirForPreparation = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+        installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
+        installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
+        installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
+        installFontFile(dirForPreparation, "bar,4", GOOD_SIGNATURE);
         // Four font dirs are created.
         assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
 
-        UpdatableFontDir dir = new UpdatableFontDir(mUpdatableFontFilesDir, parser);
+        UpdatableFontDir dir = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
         assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
-        assertThat(parser.getVersion(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(3);
+        assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(3);
         assertThat(dir.getFontFileMap()).containsKey("bar.ttf");
-        assertThat(parser.getVersion(dir.getFontFileMap().get("bar.ttf"))).isEqualTo(4);
+        assertThat(parser.getRevision(dir.getFontFileMap().get("bar.ttf"))).isEqualTo(4);
         // Outdated font dir should be deleted.
         assertThat(mUpdatableFontFilesDir.list()).hasLength(2);
     }
@@ -95,66 +157,189 @@
     @Test
     public void construct_empty() {
         FakeFontFileParser parser = new FakeFontFileParser();
-        UpdatableFontDir dir = new UpdatableFontDir(mUpdatableFontFilesDir, parser);
+        FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+        UpdatableFontDir dir = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
         assertThat(dir.getFontFileMap()).isEmpty();
     }
 
     @Test
+    public void construct_missingFsverity() throws Exception {
+        FakeFontFileParser parser = new FakeFontFileParser();
+        FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+        UpdatableFontDir dirForPreparation = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+        installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
+        installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
+        installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
+        installFontFile(dirForPreparation, "bar,4", GOOD_SIGNATURE);
+        // Four font dirs are created.
+        assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
+
+        fakeFsverityUtil.remove(
+                dirForPreparation.getFontFileMap().get("foo.ttf").getAbsolutePath());
+        UpdatableFontDir dir = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+        assertThat(dir.getFontFileMap()).isEmpty();
+        // All font dirs (including dir for "bar.ttf") should be deleted.
+        assertThat(mUpdatableFontFilesDir.list()).hasLength(0);
+    }
+
+    @Test
+    public void construct_fontNameMismatch() throws Exception {
+        FakeFontFileParser parser = new FakeFontFileParser();
+        FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+        UpdatableFontDir dirForPreparation = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+        installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
+        installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
+        installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
+        installFontFile(dirForPreparation, "bar,4", GOOD_SIGNATURE);
+        // Four font dirs are created.
+        assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
+
+        // Overwrite "foo.ttf" with wrong contents.
+        FileUtils.stringToFile(dirForPreparation.getFontFileMap().get("foo.ttf"), "bar,4");
+
+        UpdatableFontDir dir = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+        assertThat(dir.getFontFileMap()).isEmpty();
+        // All font dirs (including dir for "bar.ttf") should be deleted.
+        assertThat(mUpdatableFontFilesDir.list()).hasLength(0);
+    }
+
+    @Test
+    public void construct_olderThanPreinstalledFont() throws Exception {
+        FakeFontFileParser parser = new FakeFontFileParser();
+        FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+        UpdatableFontDir dirForPreparation = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+        installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
+        installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
+        installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
+        installFontFile(dirForPreparation, "bar,4", GOOD_SIGNATURE);
+        // Four font dirs are created.
+        assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
+
+        // Add preinstalled fonts.
+        FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "foo.ttf"), "foo,5");
+        FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "bar.ttf"), "bar,1");
+        FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(1), "bar.ttf"), "bar,2");
+        UpdatableFontDir dir = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+        // For foo.ttf, preinstalled font (revision 5) should be used.
+        assertThat(dir.getFontFileMap()).doesNotContainKey("foo.ttf");
+        // For bar.ttf, updated font (revision 4) should be used.
+        assertThat(dir.getFontFileMap()).containsKey("bar.ttf");
+        assertThat(parser.getRevision(dir.getFontFileMap().get("bar.ttf"))).isEqualTo(4);
+        // Outdated font dir should be deleted.
+        // We don't delete bar.ttf in this case, because it's normal that OTA updates preinstalled
+        // fonts.
+        assertThat(mUpdatableFontFilesDir.list()).hasLength(1);
+    }
+
+    @Test
     public void installFontFile() throws Exception {
         FakeFontFileParser parser = new FakeFontFileParser();
-        UpdatableFontDir dir = new UpdatableFontDir(mUpdatableFontFilesDir, parser);
+        FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+        UpdatableFontDir dir = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
 
-        installFontFile(dir, "test.ttf", "1");
+        installFontFile(dir, "test,1", GOOD_SIGNATURE);
         assertThat(dir.getFontFileMap()).containsKey("test.ttf");
-        assertThat(parser.getVersion(dir.getFontFileMap().get("test.ttf"))).isEqualTo(1);
+        assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(1);
     }
 
     @Test
     public void installFontFile_upgrade() throws Exception {
         FakeFontFileParser parser = new FakeFontFileParser();
-        UpdatableFontDir dir = new UpdatableFontDir(mUpdatableFontFilesDir, parser);
+        FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+        UpdatableFontDir dir = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
 
-        installFontFile(dir, "test.ttf", "1");
+        installFontFile(dir, "test,1", GOOD_SIGNATURE);
         Map<String, File> mapBeforeUpgrade = dir.getFontFileMap();
-        installFontFile(dir, "test.ttf", "2");
+        installFontFile(dir, "test,2", GOOD_SIGNATURE);
         assertThat(dir.getFontFileMap()).containsKey("test.ttf");
-        assertThat(parser.getVersion(dir.getFontFileMap().get("test.ttf"))).isEqualTo(2);
+        assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(2);
         assertThat(mapBeforeUpgrade).containsKey("test.ttf");
         assertWithMessage("Older fonts should not be deleted until next loadFontFileMap")
-                .that(parser.getVersion(mapBeforeUpgrade.get("test.ttf"))).isEqualTo(1);
+                .that(parser.getRevision(mapBeforeUpgrade.get("test.ttf"))).isEqualTo(1);
     }
 
     @Test
     public void installFontFile_downgrade() throws Exception {
         FakeFontFileParser parser = new FakeFontFileParser();
-        UpdatableFontDir dir = new UpdatableFontDir(mUpdatableFontFilesDir, parser);
+        FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+        UpdatableFontDir dir = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
 
-        installFontFile(dir, "test.ttf", "2");
-        installFontFile(dir, "test.ttf", "1");
+        installFontFile(dir, "test,2", GOOD_SIGNATURE);
+        try {
+            installFontFile(dir, "test,1", GOOD_SIGNATURE);
+            fail("Expect IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // Expect
+        }
         assertThat(dir.getFontFileMap()).containsKey("test.ttf");
-        assertWithMessage("Font should not be downgraded to an older version")
-                .that(parser.getVersion(dir.getFontFileMap().get("test.ttf"))).isEqualTo(2);
+        assertWithMessage("Font should not be downgraded to an older revision")
+                .that(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(2);
     }
 
     @Test
     public void installFontFile_multiple() throws Exception {
         FakeFontFileParser parser = new FakeFontFileParser();
-        UpdatableFontDir dir = new UpdatableFontDir(mUpdatableFontFilesDir, parser);
+        FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+        UpdatableFontDir dir = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
 
-        installFontFile(dir, "foo.ttf", "1");
-        installFontFile(dir, "bar.ttf", "2");
+        installFontFile(dir, "foo,1", GOOD_SIGNATURE);
+        installFontFile(dir, "bar,2", GOOD_SIGNATURE);
         assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
-        assertThat(parser.getVersion(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1);
+        assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1);
         assertThat(dir.getFontFileMap()).containsKey("bar.ttf");
-        assertThat(parser.getVersion(dir.getFontFileMap().get("bar.ttf"))).isEqualTo(2);
+        assertThat(parser.getRevision(dir.getFontFileMap().get("bar.ttf"))).isEqualTo(2);
     }
 
-    private void installFontFile(UpdatableFontDir dir, String name, String content)
+    @Test
+    public void installFontFile_invalidSignature() {
+        FakeFontFileParser parser = new FakeFontFileParser();
+        FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+        UpdatableFontDir dir = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+
+        try {
+            installFontFile(dir, "test,1", "Invalid signature");
+            fail("Expect IOException");
+        } catch (IOException e) {
+            // Expect
+        }
+        assertThat(dir.getFontFileMap()).isEmpty();
+    }
+
+    @Test
+    public void installFontFile_olderThanPreinstalledFont() throws Exception {
+        FakeFontFileParser parser = new FakeFontFileParser();
+        FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+        FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test,1");
+        UpdatableFontDir dir = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+
+        try {
+            installFontFile(dir, "test,1", GOOD_SIGNATURE);
+            fail("Expect IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // Expect
+        }
+        assertThat(dir.getFontFileMap()).isEmpty();
+    }
+
+    private void installFontFile(UpdatableFontDir dir, String content, String signature)
             throws IOException {
-        File file = File.createTempFile(name, "", mCacheDir);
+        File file = File.createTempFile("font", "ttf", mCacheDir);
         FileUtils.stringToFile(file, content);
         try (FileInputStream in = new FileInputStream(file)) {
-            dir.installFontFile(name, in.getFD());
+            dir.installFontFile(in.getFD(), signature.getBytes());
         }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/ResumeOnRebootServiceProviderTests.java b/services/tests/servicestests/src/com/android/server/locksettings/ResumeOnRebootServiceProviderTests.java
new file mode 100644
index 0000000..b9af82b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/ResumeOnRebootServiceProviderTests.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.service.resumeonreboot.ResumeOnRebootService;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class ResumeOnRebootServiceProviderTests {
+
+    @Mock
+    Context mMockContext;
+    @Mock
+    PackageManager mMockPackageManager;
+    @Mock
+    ResolveInfo mMockResolvedInfo;
+    @Mock
+    ServiceInfo mMockServiceInfo;
+    @Mock
+    ComponentName mMockComponentName;
+    @Captor
+    ArgumentCaptor<Intent> mIntentArgumentCaptor;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mMockContext.getUserId()).thenReturn(0);
+        when(mMockResolvedInfo.serviceInfo).thenReturn(mMockServiceInfo);
+        when(mMockServiceInfo.getComponentName()).thenReturn(mMockComponentName);
+    }
+
+    @Test
+    public void noServiceFound() throws Exception {
+        when(mMockPackageManager.queryIntentServices(any(),
+                eq(PackageManager.MATCH_SYSTEM_ONLY))).thenReturn(
+                null);
+        assertThat(new ResumeOnRebootServiceProvider(mMockContext,
+                mMockPackageManager).getServiceConnection()).isNull();
+    }
+
+    @Test
+    public void serviceNotGuardedWithPermission() throws Exception {
+        ArrayList<ResolveInfo> resultList = new ArrayList<>();
+        when(mMockServiceInfo.permission).thenReturn("");
+        resultList.add(mMockResolvedInfo);
+        when(mMockPackageManager.queryIntentServices(any(), any())).thenReturn(
+                resultList);
+        assertThat(new ResumeOnRebootServiceProvider(mMockContext,
+                mMockPackageManager).getServiceConnection()).isNull();
+    }
+
+    @Test
+    public void serviceResolved() throws Exception {
+        ArrayList<ResolveInfo> resultList = new ArrayList<>();
+        resultList.add(mMockResolvedInfo);
+        when(mMockServiceInfo.permission).thenReturn(
+                Manifest.permission.BIND_RESUME_ON_REBOOT_SERVICE);
+        when(mMockPackageManager.queryIntentServices(any(),
+                eq(PackageManager.MATCH_SYSTEM_ONLY))).thenReturn(
+                resultList);
+
+        assertThat(new ResumeOnRebootServiceProvider(mMockContext,
+                mMockPackageManager).getServiceConnection()).isNotNull();
+
+        verify(mMockPackageManager).queryIntentServices(mIntentArgumentCaptor.capture(),
+                eq(PackageManager.MATCH_SYSTEM_ONLY));
+        assertThat(mIntentArgumentCaptor.getValue().getAction()).isEqualTo(
+                ResumeOnRebootService.SERVICE_INTERFACE);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
index 391611b..5468fba 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
@@ -78,7 +78,7 @@
     }
 
     @Test
-    public void testImmutableEnabledChange() {
+    public void testImmutableEnabledChange() throws Exception {
         final OverlayManagerServiceImpl impl = getImpl();
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -106,7 +106,7 @@
     }
 
     @Test
-    public void testMutableEnabledChangeHasNoEffect() {
+    public void testMutableEnabledChangeHasNoEffect() throws Exception {
         final OverlayManagerServiceImpl impl = getImpl();
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -134,7 +134,7 @@
     }
 
     @Test
-    public void testMutableEnabledToImmutableEnabled() {
+    public void testMutableEnabledToImmutableEnabled() throws Exception {
         final OverlayManagerServiceImpl impl = getImpl();
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -178,7 +178,7 @@
     }
 
     @Test
-    public void testMutablePriorityChange() {
+    public void testMutablePriorityChange() throws Exception {
         final OverlayManagerServiceImpl impl = getImpl();
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -218,7 +218,7 @@
     }
 
     @Test
-    public void testImmutablePriorityChange() {
+    public void testImmutablePriorityChange() throws Exception {
         final OverlayManagerServiceImpl impl = getImpl();
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET), USER);
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
index 4f882ce..33dbcc0 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
@@ -22,11 +22,14 @@
 import static android.os.OverlayablePolicy.CONFIG_SIGNATURE;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertThrows;
 
 import android.content.om.OverlayInfo;
+import android.util.Pair;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -35,6 +38,7 @@
 
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 
 @RunWith(AndroidJUnit4.class)
 public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase {
@@ -55,7 +59,7 @@
     private static final String CERT_CONFIG_NOK = "config_certificate_nok";
 
     @Test
-    public void testGetOverlayInfo() {
+    public void testGetOverlayInfo() throws Exception {
         installNewPackage(overlay(OVERLAY, TARGET), USER);
 
         final OverlayManagerServiceImpl impl = getImpl();
@@ -67,7 +71,7 @@
     }
 
     @Test
-    public void testGetOverlayInfosForTarget() {
+    public void testGetOverlayInfosForTarget() throws Exception {
         installNewPackage(overlay(OVERLAY, TARGET), USER);
         installNewPackage(overlay(OVERLAY2, TARGET), USER);
         installNewPackage(overlay(OVERLAY3, TARGET), USER2);
@@ -92,7 +96,7 @@
     }
 
     @Test
-    public void testGetOverlayInfosForUser() {
+    public void testGetOverlayInfosForUser() throws Exception {
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET), USER);
         installNewPackage(overlay(OVERLAY2, TARGET), USER);
@@ -119,7 +123,7 @@
     }
 
     @Test
-    public void testPriority() {
+    public void testPriority() throws Exception {
         installNewPackage(overlay(OVERLAY, TARGET), USER);
         installNewPackage(overlay(OVERLAY2, TARGET), USER);
         installNewPackage(overlay(OVERLAY3, TARGET), USER);
@@ -131,18 +135,21 @@
 
         assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
 
-        assertTrue(impl.setLowestPriority(OVERLAY3, USER));
+        assertEquals(impl.setLowestPriority(OVERLAY3, USER),
+                Optional.of(new PackageAndUser(TARGET, USER)));
         assertOverlayInfoForTarget(TARGET, USER, o3, o1, o2);
 
-        assertTrue(impl.setHighestPriority(OVERLAY3, USER));
+        assertEquals(impl.setHighestPriority(OVERLAY3, USER),
+                Optional.of(new PackageAndUser(TARGET, USER)));
         assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
 
-        assertTrue(impl.setPriority(OVERLAY, OVERLAY2, USER));
+        assertEquals(impl.setPriority(OVERLAY, OVERLAY2, USER),
+                Optional.of(new PackageAndUser(TARGET, USER)));
         assertOverlayInfoForTarget(TARGET, USER, o2, o1, o3);
     }
 
     @Test
-    public void testOverlayInfoStateTransitions() {
+    public void testOverlayInfoStateTransitions() throws Exception {
         final OverlayManagerServiceImpl impl = getImpl();
         assertNull(impl.getOverlayInfo(OVERLAY, USER));
 
@@ -153,7 +160,8 @@
         installNewPackage(target, USER);
         assertState(STATE_DISABLED, OVERLAY, USER);
 
-        impl.setEnabled(OVERLAY, true, USER);
+        assertEquals(impl.setEnabled(OVERLAY, true, USER),
+                Optional.of(new PackageAndUser(TARGET, USER)));
         assertState(STATE_ENABLED, OVERLAY, USER);
 
         // target upgrades do not change the state of the overlay
@@ -168,50 +176,40 @@
     }
 
     @Test
-    public void testOnOverlayPackageUpgraded() {
-        final FakeListener listener = getListener();
+    public void testOnOverlayPackageUpgraded() throws Exception {
         final FakeDeviceState.PackageBuilder target = target(TARGET);
         final FakeDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET);
         installNewPackage(target, USER);
         installNewPackage(overlay, USER);
-        listener.count = 0;
         upgradePackage(overlay, USER);
-        assertEquals(2, listener.count);
 
         // upgrade to a version where the overlay has changed its target
-        // expect once for the old target package, once for the new target package
-        listener.count = 0;
         final FakeDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target");
-        upgradePackage(overlay2, USER);
-        assertEquals(3, listener.count);
-
-        listener.count = 0;
-        upgradePackage(overlay2, USER);
-        assertEquals(2, listener.count);
+        final Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> pair =
+                upgradePackage(overlay2, USER);
+        assertEquals(pair.first, Optional.of(new PackageAndUser(TARGET, USER)));
+        assertEquals(pair.second, Optional.of(new PackageAndUser("some.other.target", USER)));
     }
 
     @Test
-    public void testListener() {
+    public void testSetEnabledAtVariousConditions() throws Exception {
         final OverlayManagerServiceImpl impl = getImpl();
-        final FakeListener listener = getListener();
-        installNewPackage(overlay(OVERLAY, TARGET), USER);
-        assertEquals(1, listener.count);
-        listener.count = 0;
+        assertThrows(OverlayManagerServiceImpl.OperationFailedException.class,
+                () -> impl.setEnabled(OVERLAY, true, USER));
 
+        // request succeeded, and there was a change that needs to be
+        // propagated to the rest of the system
         installNewPackage(target(TARGET), USER);
-        assertEquals(1, listener.count);
-        listener.count = 0;
+        installNewPackage(overlay(OVERLAY, TARGET), USER);
+        assertEquals(impl.setEnabled(OVERLAY, true, USER),
+                Optional.of(new PackageAndUser(TARGET, USER)));
 
-        impl.setEnabled(OVERLAY, true, USER);
-        assertEquals(1, listener.count);
-        listener.count = 0;
-
-        impl.setEnabled(OVERLAY, true, USER);
-        assertEquals(0, listener.count);
+        // request succeeded, but nothing changed
+        assertFalse(impl.setEnabled(OVERLAY, true, USER).isPresent());
     }
 
     @Test
-    public void testConfigSignaturePolicyOk() {
+    public void testConfigSignaturePolicyOk() throws Exception {
         setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
         reinitializeImpl();
 
@@ -229,7 +227,7 @@
     }
 
     @Test
-    public void testConfigSignaturePolicyCertNok() {
+    public void testConfigSignaturePolicyCertNok() throws Exception {
         setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
         reinitializeImpl();
 
@@ -247,7 +245,7 @@
     }
 
     @Test
-    public void testConfigSignaturePolicyNoConfig() {
+    public void testConfigSignaturePolicyNoConfig() throws Exception {
         addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER);
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
@@ -262,7 +260,7 @@
     }
 
     @Test
-    public void testConfigSignaturePolicyNoRefPkg() {
+    public void testConfigSignaturePolicyNoRefPkg() throws Exception {
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
 
@@ -276,7 +274,7 @@
     }
 
     @Test
-    public void testConfigSignaturePolicyRefPkgNotSystem() {
+    public void testConfigSignaturePolicyRefPkgNotSystem() throws Exception {
         setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
         reinitializeImpl();
 
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
index 006dda0..2c477c8 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
@@ -16,6 +16,8 @@
 
 package com.android.server.om;
 
+import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
@@ -30,6 +32,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Pair;
 
 import androidx.annotation.Nullable;
 
@@ -43,13 +46,13 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.stream.Collectors;
 
 /** Base class for creating {@link OverlayManagerServiceImplTests} tests. */
 class OverlayManagerServiceImplTestsBase {
     private OverlayManagerServiceImpl mImpl;
     private FakeDeviceState mState;
-    private FakeListener mListener;
     private FakePackageManagerHelper mPackageManager;
     private FakeIdmapDaemon mIdmapDaemon;
     private OverlayConfig mOverlayConfig;
@@ -58,7 +61,6 @@
     @Before
     public void setUp() {
         mState = new FakeDeviceState();
-        mListener = new FakeListener();
         mPackageManager = new FakePackageManagerHelper(mState);
         mIdmapDaemon = new FakeIdmapDaemon(mState);
         mOverlayConfig = mock(OverlayConfig.class);
@@ -73,18 +75,13 @@
                 new IdmapManager(mIdmapDaemon, mPackageManager),
                 new OverlayManagerSettings(),
                 mOverlayConfig,
-                new String[0],
-                mListener);
+                new String[0]);
     }
 
     OverlayManagerServiceImpl getImpl() {
         return mImpl;
     }
 
-    FakeListener getListener() {
-        return mListener;
-    }
-
     FakeIdmapDaemon getIdmapd() {
         return mIdmapDaemon;
     }
@@ -155,7 +152,8 @@
      *
      * @throws IllegalStateException if the package is currently installed
      */
-    void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId) {
+    void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId)
+            throws OperationFailedException {
         if (mState.select(pkg.packageName, userId) != null) {
             throw new IllegalStateException("package " + pkg.packageName + " already installed");
         }
@@ -176,23 +174,30 @@
      * {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast with the
      * {@link android.content.Intent#EXTRA_REPLACING} extra.
      *
+     * @return the two Optional<PackageAndUser> objects from starting and finishing the upgrade
+     *
      * @throws IllegalStateException if the package is not currently installed
      */
-    void upgradePackage(FakeDeviceState.PackageBuilder pkg, int userId) {
+    Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> upgradePackage(
+            FakeDeviceState.PackageBuilder pkg, int userId) throws OperationFailedException {
         final FakeDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId);
         if (replacedPackage == null) {
             throw new IllegalStateException("package " + pkg.packageName + " not installed");
         }
+        Optional<PackageAndUser> opt1 = Optional.empty();
         if (replacedPackage.targetPackageName != null) {
-            mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
+            opt1 = mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
         }
 
         mState.add(pkg, userId);
+        Optional<PackageAndUser> opt2;
         if (pkg.targetPackage == null) {
-            mImpl.onTargetPackageReplaced(pkg.packageName, userId);
+            opt2 = mImpl.onTargetPackageReplaced(pkg.packageName, userId);
         } else {
-            mImpl.onOverlayPackageReplaced(pkg.packageName, userId);
+            opt2 = mImpl.onOverlayPackageReplaced(pkg.packageName, userId);
         }
+
+        return Pair.create(opt1, opt2);
     }
 
     /**
@@ -203,7 +208,7 @@
      *
      * @throws IllegalStateException if the package is not currently installed
      */
-    void uninstallPackage(String packageName, int userId) {
+    void uninstallPackage(String packageName, int userId) throws OperationFailedException {
         final FakeDeviceState.Package pkg = mState.select(packageName, userId);
         if (pkg == null) {
             throw new IllegalStateException("package " + packageName+ " not installed");
@@ -485,12 +490,4 @@
             }
         }
     }
-
-    static class FakeListener implements OverlayManagerServiceImpl.OverlayChangeListener {
-        public int count;
-
-        public void onOverlaysChanged(@NonNull String targetPackage, int userId) {
-            count++;
-        }
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index b190339..395b643 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -219,8 +219,8 @@
         mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_USER, /* value= */ true,
                 asHandle(currentUser));
         try {
-            assertThat(mUserManager.removeUserOrSetEphemeral(user1.id)).isEqualTo(
-                    UserManager.REMOVE_RESULT_ERROR);
+            assertThat(mUserManager.removeUserOrSetEphemeral(user1.id,
+                    /* evenWhenDisallowed= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
         } finally {
             mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_USER, /* value= */ false,
                     asHandle(currentUser));
@@ -232,9 +232,32 @@
 
     @MediumTest
     @Test
+    public void testRemoveUserOrSetEphemeral_evenWhenRestricted() throws Exception {
+        final int currentUser = ActivityManager.getCurrentUser();
+        final UserInfo user1 = createUser("User 1", /* flags= */ 0);
+        mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_USER, /* value= */ true,
+                asHandle(currentUser));
+        try {
+            synchronized (mUserRemoveLock) {
+                assertThat(mUserManager.removeUserOrSetEphemeral(user1.id,
+                        /* evenWhenDisallowed= */ true))
+                                .isEqualTo(UserManager.REMOVE_RESULT_REMOVED);
+                waitForUserRemovalLocked(user1.id);
+            }
+
+        } finally {
+            mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_USER, /* value= */ false,
+                    asHandle(currentUser));
+        }
+
+        assertThat(hasUser(user1.id)).isFalse();
+    }
+
+    @MediumTest
+    @Test
     public void testRemoveUserOrSetEphemeral_systemUserReturnsError() throws Exception {
-        assertThat(mUserManager.removeUserOrSetEphemeral(UserHandle.USER_SYSTEM)).isEqualTo(
-                UserManager.REMOVE_RESULT_ERROR);
+        assertThat(mUserManager.removeUserOrSetEphemeral(UserHandle.USER_SYSTEM,
+                /* evenWhenDisallowed= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
 
         assertThat(hasUser(UserHandle.USER_SYSTEM)).isTrue();
     }
@@ -243,8 +266,8 @@
     @Test
     public void testRemoveUserOrSetEphemeral_invalidUserReturnsError() throws Exception {
         assertThat(hasUser(Integer.MAX_VALUE)).isFalse();
-        assertThat(mUserManager.removeUserOrSetEphemeral(Integer.MAX_VALUE)).isEqualTo(
-                UserManager.REMOVE_RESULT_ERROR);
+        assertThat(mUserManager.removeUserOrSetEphemeral(Integer.MAX_VALUE,
+                /* evenWhenDisallowed= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
     }
 
     @MediumTest
@@ -255,8 +278,8 @@
         // Switch to the user just created.
         switchUser(user1.id, null, /* ignoreHandle= */ true);
 
-        assertThat(mUserManager.removeUserOrSetEphemeral(user1.id)).isEqualTo(
-                UserManager.REMOVE_RESULT_SET_EPHEMERAL);
+        assertThat(mUserManager.removeUserOrSetEphemeral(user1.id, /* evenWhenDisallowed= */ false))
+                .isEqualTo(UserManager.REMOVE_RESULT_SET_EPHEMERAL);
 
         assertThat(hasUser(user1.id)).isTrue();
         assertThat(getUser(user1.id).isEphemeral()).isTrue();
@@ -276,8 +299,8 @@
     public void testRemoveUserOrSetEphemeral_nonCurrentUserRemoved() throws Exception {
         final UserInfo user1 = createUser("User 1", /* flags= */ 0);
         synchronized (mUserRemoveLock) {
-            assertThat(mUserManager.removeUserOrSetEphemeral(user1.id)).isEqualTo(
-                    UserManager.REMOVE_RESULT_REMOVED);
+            assertThat(mUserManager.removeUserOrSetEphemeral(user1.id,
+                    /* evenWhenDisallowed= */ false)).isEqualTo(UserManager.REMOVE_RESULT_REMOVED);
             waitForUserRemovalLocked(user1.id);
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
index 0b44c59..b11bb85 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
@@ -366,36 +366,19 @@
             actualPackages.add(p.packageName);
         }
 
-        // Add auto-generated RRO package to expectedPackages since they are not (supposed to be)
-        // in the allowlist but they should be installed.
+        // Add static overlays to expectedPackages since they are not (supposed to be)
+        // in the allowlist but they should be installed if their target is.
         for (PackageInfo p : packageInfos) {
-            if (p.isOverlayPackage()
-                        && UserSystemPackageInstaller.hasAutoGeneratedRROSuffix(p.packageName)
-                        && expectedPackages.contains(p.overlayTarget)) {
+            if (p.isStaticOverlayPackage() && expectedPackages.contains(p.overlayTarget)) {
                 expectedPackages.add(p.packageName);
             }
         }
         checkPackageDifferences(expectedPackages, actualPackages);
     }
 
-    @Test
-    public void testAutoGeneratedRROMatchesSuffix() {
-        final List<PackageInfo> packageInfos = mContext.getPackageManager()
-                .getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES);
-
-        Log.v(TAG, "Found total packages: " + packageInfos.size());
-
-        for (PackageInfo p : packageInfos) {
-            if (p.packageName.contains(".auto_generated_rro_")) {
-                assertTrue("Auto-generated RRO package name does not match the suffix: "
-                        + p.packageName,
-                        UserSystemPackageInstaller.hasAutoGeneratedRROSuffix(p.packageName));
-            }
-        }
-    }
-
     /**
-     * Test that overlay package not in allowlist should be installed for all user at Explicit mode.
+     * Test that static overlay package not in allowlist should be installed for all users
+     * based on their targets, in Explicit mode.
      */
     @Test
     public void testInstallOverlayPackagesExplicitMode() {
@@ -408,19 +391,32 @@
         final String packageName2 = "nonWhitelistedPkg";
         final String overlayName1 = String.format("%s.auto_generated_rro_product__", packageName1);
         final String overlayName2 = String.format("%s.auto_generated_rro_product__", packageName2);
+        final String overlayName3 = String.format("%s.non_static_overlay", packageName1);
 
+        // Static overlay for allowlisted package1 -> should be installed, like package1
         final AndroidPackage overlayPackage1 = ((ParsedPackage) PackageImpl.forTesting(overlayName1)
                 .setOverlay(true)
+                .setOverlayIsStatic(true)
                 .setOverlayTarget(packageName1)
                 .hideAsParsed())
                 .hideAsFinal();
 
+        // Static overlay for non-allowlisted package2 -> should not be installed, like package2
         final AndroidPackage overlayPackage2 = ((ParsedPackage) PackageImpl.forTesting(overlayName2)
                 .setOverlay(true)
+                .setOverlayIsStatic(true)
                 .setOverlayTarget(packageName2)
                 .hideAsParsed())
                 .hideAsFinal();
 
+        // Non-static overlay for package1 -> not explicitly allowlisted, so shouldn't be installed
+        final AndroidPackage overlayPackage3 = ((ParsedPackage) PackageImpl.forTesting(overlayName3)
+                .setOverlay(true)
+                .setOverlayIsStatic(false) // non-static
+                .setOverlayTarget(packageName1)
+                .hideAsParsed())
+                .hideAsFinal();
+
         final ArrayMap<String, Long> userTypeWhitelist = new ArrayMap<>();
         userTypeWhitelist.put(packageName1, maskOfType);
 
@@ -428,10 +424,12 @@
         userWhitelist.add(packageName1);
 
         boolean implicit = false;
-        assertTrue("Overlay for package1 should be installed", UserSystemPackageInstaller
+        assertTrue("Should install static overlay for package1", UserSystemPackageInstaller
                 .shouldInstallPackage(overlayPackage1, userTypeWhitelist, userWhitelist, implicit));
-        assertFalse("Overlay for package2 should not be installed", UserSystemPackageInstaller
+        assertFalse("Should not install static overlay for package2", UserSystemPackageInstaller
                 .shouldInstallPackage(overlayPackage2, userTypeWhitelist, userWhitelist, implicit));
+        assertFalse("Should not install regular overlay for package1", UserSystemPackageInstaller
+                .shouldInstallPackage(overlayPackage3, userTypeWhitelist, userWhitelist, implicit));
     }
 
     /** Asserts that actual is a subset of expected. */
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 7f35511..f07197c 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -77,6 +77,7 @@
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.display.DisplayGroup;
 import com.android.server.lights.LightsManager;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.power.PowerManagerService.BatteryReceiver;
@@ -97,9 +98,12 @@
 import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Tests for {@link com.android.server.power.PowerManagerService}.
@@ -178,7 +182,8 @@
                 .thenReturn(mPowerSaveState);
         when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(false);
         when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false);
-        when(mDisplayManagerInternalMock.requestPowerState(any(), anyBoolean())).thenReturn(true);
+        when(mDisplayManagerInternalMock.requestPowerState(anyInt(), any(),
+                anyBoolean())).thenReturn(true);
         when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn("");
         when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);
 
@@ -399,30 +404,33 @@
     @Test
     public void testGetDesiredScreenPolicy_WithVR() throws Exception {
         createService();
+        mService.systemReady(null);
         // Brighten up the screen
-        mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_UNKNOWN, 0);
-        assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+        mService.setWakefulnessLocked(DisplayGroup.DEFAULT, WAKEFULNESS_AWAKE, 0, 0, 0, 0, null,
+                null);
+        assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
                 DisplayPowerRequest.POLICY_BRIGHT);
 
         // Move to VR
         mService.setVrModeEnabled(true);
-        assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+        assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
                 DisplayPowerRequest.POLICY_VR);
 
         // Then take a nap
-        mService.setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
-                0);
-        assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+        mService.setWakefulnessLocked(DisplayGroup.DEFAULT, WAKEFULNESS_ASLEEP, 0, 0, 0, 0, null,
+                null);
+        assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
                 DisplayPowerRequest.POLICY_OFF);
 
         // Wake up to VR
-        mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_UNKNOWN, 0);
-        assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+        mService.setWakefulnessLocked(DisplayGroup.DEFAULT, WAKEFULNESS_AWAKE, 0, 0, 0, 0, null,
+                null);
+        assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
                 DisplayPowerRequest.POLICY_VR);
 
         // And back to normal
         mService.setVrModeEnabled(false);
-        assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+        assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
                 DisplayPowerRequest.POLICY_BRIGHT);
     }
 
@@ -673,8 +681,9 @@
     }
 
     @Test
-    public void testForceSuspend_forceSuspendFailurePropogated() {
+    public void testForceSuspend_forceSuspendFailurePropagated() throws Exception {
         createService();
+        startSystem();
         when(mNativeWrapperMock.nativeForceSuspend()).thenReturn(false);
         assertThat(mService.getBinderServiceInstance().forceSuspend()).isFalse();
     }
@@ -838,7 +847,7 @@
         createService();
         startSystem();
 
-        assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+        assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
                 DisplayPowerRequest.POLICY_BRIGHT);
     }
 
@@ -857,11 +866,8 @@
     public void testQuiescentBoot_DesiredScreenPolicyShouldBeOff() throws Exception {
         when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
         createService();
-        assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
-                DisplayPowerRequest.POLICY_OFF);
-
         startSystem();
-        assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+        assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
                 DisplayPowerRequest.POLICY_OFF);
     }
 
@@ -871,7 +877,7 @@
         createService();
         startSystem();
         forceAwake();
-        assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+        assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
                 DisplayPowerRequest.POLICY_BRIGHT);
     }
 
@@ -1143,4 +1149,85 @@
         assertFalse(
                 mService.getBinderServiceInstance().setPowerModeChecked(Mode.INTERACTIVE, false));
     }
+
+    @Test
+    public void testMultiDisplay_wakefulnessUpdates() throws Exception {
+        final int nonDefaultDisplayGroupId = DisplayGroup.DEFAULT + 1;
+        final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
+                new AtomicReference<>();
+        doAnswer((Answer<Void>) invocation -> {
+            listener.set(invocation.getArgument(0));
+            return null;
+        }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());
+
+        createService();
+        startSystem();
+        listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId);
+
+        assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+        mService.setWakefulnessLocked(DisplayGroup.DEFAULT, WAKEFULNESS_ASLEEP, 0, 0, 0, 0, null,
+                null);
+        assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+        mService.setWakefulnessLocked(nonDefaultDisplayGroupId, WAKEFULNESS_ASLEEP, 0, 0, 0, 0,
+                null, null);
+        assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
+
+        mService.setWakefulnessLocked(DisplayGroup.DEFAULT, WAKEFULNESS_AWAKE, 0, 0, 0, 0, null,
+                null);
+        assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+    }
+
+    @Test
+    public void testMultiDisplay_addDisplayGroup_preservesWakefulness() throws Exception {
+        final int nonDefaultDisplayGroupId = DisplayGroup.DEFAULT + 1;
+        final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
+                new AtomicReference<>();
+        doAnswer((Answer<Void>) invocation -> {
+            listener.set(invocation.getArgument(0));
+            return null;
+        }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());
+
+        createService();
+        startSystem();
+
+        assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+        mService.setWakefulnessLocked(DisplayGroup.DEFAULT, WAKEFULNESS_ASLEEP, 0, 0, 0, 0, null,
+                null);
+        assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
+
+        listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId);
+
+        assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
+    }
+
+    @Test
+    public void testMultiDisplay_removeDisplayGroup_updatesWakefulness() throws Exception {
+        final int nonDefaultDisplayGroupId = DisplayGroup.DEFAULT + 1;
+        final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
+                new AtomicReference<>();
+        doAnswer((Answer<Void>) invocation -> {
+            listener.set(invocation.getArgument(0));
+            return null;
+        }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());
+
+        createService();
+        startSystem();
+        listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId);
+
+        assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+        mService.setWakefulnessLocked(DisplayGroup.DEFAULT, WAKEFULNESS_ASLEEP, 0, 0, 0, 0, null,
+                null);
+        assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+        listener.get().onDisplayGroupRemoved(nonDefaultDisplayGroupId);
+        assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
+
+        mService.setWakefulnessLocked(DisplayGroup.DEFAULT, WAKEFULNESS_AWAKE, 0, 0, 0, 0, null,
+                null);
+        assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
index b07b8fa..9b8a2a8 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
@@ -35,6 +35,7 @@
 import android.content.Context;
 import android.content.IntentSender;
 import android.content.pm.PackageManager;
+import android.hardware.boot.V1_2.IBootControl;
 import android.os.Handler;
 import android.os.IPowerManager;
 import android.os.IRecoverySystemProgressListener;
@@ -68,12 +69,13 @@
     private IThermalService mIThermalService;
     private FileWriter mUncryptUpdateFileWriter;
     private LockSettingsInternal mLockSettingsInternal;
+    private IBootControl mIBootControl;
 
     private static final String FAKE_OTA_PACKAGE_NAME = "fake.ota.package";
     private static final String FAKE_OTHER_PACKAGE_NAME = "fake.other.package";
 
     @Before
-    public void setup() {
+    public void setup() throws Exception {
         mContext = mock(Context.class);
         mSystemProperties = new RecoverySystemServiceTestable.FakeSystemProperties();
         mUncryptSocket = mock(RecoverySystemService.UncryptSocket.class);
@@ -88,8 +90,13 @@
         PowerManager powerManager = new PowerManager(mock(Context.class), mIPowerManager,
                 mIThermalService, new Handler(looper));
 
+        mIBootControl = mock(IBootControl.class);
+        when(mIBootControl.getCurrentSlot()).thenReturn(0);
+        when(mIBootControl.getActiveBootSlot()).thenReturn(1);
+
         mRecoverySystemService = new RecoverySystemServiceTestable(mContext, mSystemProperties,
-                powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal);
+                powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal,
+                mIBootControl);
     }
 
     @Test
@@ -332,6 +339,15 @@
         verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
     }
 
+
+    @Test
+    public void rebootWithLskf_slotMismatch_Failure() throws Exception {
+        assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null), is(true));
+        mRecoverySystemService.onPreparedForReboot(true);
+        assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", false),
+                is(false));
+    }
+
     @Test
     public void rebootWithLskf_withoutPrepare_Failure() throws Exception {
         assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true),
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
index 131e4f3..0727e5a 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
@@ -17,6 +17,7 @@
 package com.android.server.recoverysystem;
 
 import android.content.Context;
+import android.hardware.boot.V1_2.IBootControl;
 import android.os.PowerManager;
 
 import com.android.internal.widget.LockSettingsInternal;
@@ -30,16 +31,19 @@
         private final FileWriter mUncryptPackageFileWriter;
         private final UncryptSocket mUncryptSocket;
         private final LockSettingsInternal mLockSettingsInternal;
+        private final IBootControl mIBootControl;
 
         MockInjector(Context context, FakeSystemProperties systemProperties,
                 PowerManager powerManager, FileWriter uncryptPackageFileWriter,
-                UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal) {
+                UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal,
+                IBootControl bootControl) {
             super(context);
             mSystemProperties = systemProperties;
             mPowerManager = powerManager;
             mUncryptPackageFileWriter = uncryptPackageFileWriter;
             mUncryptSocket = uncryptSocket;
             mLockSettingsInternal = lockSettingsInternal;
+            mIBootControl = bootControl;
         }
 
         @Override
@@ -85,13 +89,19 @@
         public LockSettingsInternal getLockSettingsService() {
             return mLockSettingsInternal;
         }
+
+        @Override
+        public IBootControl getBootControl() {
+            return mIBootControl;
+        }
     }
 
     RecoverySystemServiceTestable(Context context, FakeSystemProperties systemProperties,
             PowerManager powerManager, FileWriter uncryptPackageFileWriter,
-            UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal) {
+            UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal,
+            IBootControl bootControl) {
         super(new MockInjector(context, systemProperties, powerManager, uncryptPackageFileWriter,
-                uncryptSocket, lockSettingsInternal));
+                uncryptSocket, lockSettingsInternal, bootControl));
     }
 
     public static class FakeSystemProperties {
@@ -102,6 +112,8 @@
                     || RecoverySystemService.INIT_SERVICE_SETUP_BCB.equals(key)
                     || RecoverySystemService.INIT_SERVICE_CLEAR_BCB.equals(key)) {
                 return null;
+            } else if (RecoverySystemService.AB_UPDATE.equals(key)) {
+                return "true";
             } else {
                 throw new IllegalArgumentException("unexpected test key: " + key);
             }
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index bee7392..0fce4ba 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -213,6 +213,60 @@
     }
 
     @Test
+    public void vibrate_singleVibratorPredefinedCancel_cancelsVibrationImmediately()
+            throws Exception {
+        mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+
+        long vibrationId = 1;
+        VibrationEffect effect = VibrationEffect.startComposition()
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
+                .compose();
+        VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
+
+        Thread.sleep(20);
+        assertTrue(vibrationThread.isAlive());
+        assertTrue(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
+
+        // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
+        // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
+        Thread cancellingThread = new Thread(() -> vibrationThread.cancel());
+        cancellingThread.start();
+
+        waitForCompletion(vibrationThread, 20);
+        waitForCompletion(cancellingThread);
+
+        verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
+        assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
+    }
+
+    @Test
+    public void vibrate_singleVibratorWaveformCancel_cancelsVibrationImmediately()
+            throws Exception {
+        mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+
+        long vibrationId = 1;
+        VibrationEffect effect = VibrationEffect.createWaveform(new long[]{100}, new int[]{100}, 0);
+        VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
+
+        Thread.sleep(20);
+        assertTrue(vibrationThread.isAlive());
+        assertTrue(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
+
+        // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
+        // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
+        Thread cancellingThread = new Thread(() -> vibrationThread.cancel());
+        cancellingThread.start();
+
+        waitForCompletion(vibrationThread, 20);
+        waitForCompletion(cancellingThread);
+
+        verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
+        assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
+    }
+
+    @Test
     public void vibrate_singleVibratorPrebaked_runsVibration() throws Exception {
         mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_THUD);
 
@@ -544,36 +598,70 @@
     }
 
     @Test
-    public void vibrate_multipleCancelled_allVibratorsStopped() throws Exception {
-        mockVibrators(1, 2, 3);
+    public void vibrate_multiplePredefinedCancel_cancelsVibrationImmediately() throws Exception {
+        mockVibrators(1, 2);
+        mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+        mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+
+        long vibrationId = 1;
+        CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
+                .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+                .addVibrator(2, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
+                        .compose())
+                .combine();
+        VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
+
+        Thread.sleep(10);
+        assertTrue(vibrationThread.isAlive());
+        assertTrue(vibrationThread.getVibrators().get(1).isVibrating());
+        assertTrue(vibrationThread.getVibrators().get(2).isVibrating());
+
+        // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
+        // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
+        Thread cancellingThread = new Thread(() -> vibrationThread.cancel());
+        cancellingThread.start();
+
+        waitForCompletion(vibrationThread, 20);
+        waitForCompletion(cancellingThread);
+
+        verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
+        assertFalse(vibrationThread.getVibrators().get(1).isVibrating());
+        assertFalse(vibrationThread.getVibrators().get(2).isVibrating());
+    }
+
+    @Test
+    public void vibrate_multipleWaveformCancel_cancelsVibrationImmediately() throws Exception {
+        mockVibrators(1, 2);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
         mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
-        mVibratorProviders.get(3).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
 
         long vibrationId = 1;
         CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
                 .addVibrator(1, VibrationEffect.createWaveform(
-                        new long[]{5, 10}, new int[]{1, 2}, 0))
-                .addVibrator(2, VibrationEffect.createWaveform(
-                        new long[]{20, 30}, new int[]{3, 4}, 0))
-                .addVibrator(3, VibrationEffect.createWaveform(
-                        new long[]{10, 40}, new int[]{5, 6}, 0))
+                        new long[]{100, 100}, new int[]{1, 2}, 0))
+                .addVibrator(2, VibrationEffect.createOneShot(100, 100))
                 .combine();
-        VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+        VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
 
-        Thread.sleep(15);
-        assertTrue(thread.isAlive());
-        assertTrue(thread.getVibrators().get(1).isVibrating());
-        assertTrue(thread.getVibrators().get(2).isVibrating());
-        assertTrue(thread.getVibrators().get(3).isVibrating());
+        Thread.sleep(10);
+        assertTrue(vibrationThread.isAlive());
+        assertTrue(vibrationThread.getVibrators().get(1).isVibrating());
+        assertTrue(vibrationThread.getVibrators().get(2).isVibrating());
 
-        thread.cancel();
-        waitForCompletion(thread);
-        assertFalse(thread.getVibrators().get(1).isVibrating());
-        assertFalse(thread.getVibrators().get(2).isVibrating());
-        assertFalse(thread.getVibrators().get(3).isVibrating());
+        // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
+        // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
+        Thread cancellingThread = new Thread(() -> vibrationThread.cancel());
+        cancellingThread.start();
+
+        waitForCompletion(vibrationThread, 20);
+        waitForCompletion(cancellingThread);
 
         verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
+        assertFalse(vibrationThread.getVibrators().get(1).isVibrating());
+        assertFalse(vibrationThread.getVibrators().get(2).isVibrating());
     }
 
     @Test
@@ -621,11 +709,11 @@
         return thread;
     }
 
-    private void waitForCompletion(VibrationThread thread) {
+    private void waitForCompletion(Thread thread) {
         waitForCompletion(thread, TEST_TIMEOUT_MILLIS);
     }
 
-    private void waitForCompletion(VibrationThread thread, long timeout) {
+    private void waitForCompletion(Thread thread, long timeout) {
         try {
             thread.join(timeout);
         } catch (InterruptedException e) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 35876e4..e8888f4 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -112,9 +112,9 @@
 import android.content.ComponentName;
 import android.content.ContentUris;
 import android.content.Context;
+import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.IIntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
@@ -1509,20 +1509,6 @@
     }
 
     @Test
-    public void testCancelImmediatelyAfterEnqueueNotifiesListeners_ForegroundServiceFlag()
-            throws Exception {
-        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
-        sbn.getNotification().flags =
-                Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE;
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
-                sbn.getId(), sbn.getNotification(), sbn.getUserId());
-        mBinderService.cancelNotificationWithTag(PKG, PKG, "tag", sbn.getId(), sbn.getUserId());
-        waitForIdle();
-        verify(mListeners, times(1)).notifyPostedLocked(any(), any());
-        verify(mListeners, times(1)).notifyRemovedLocked(any(), anyInt(), any());
-    }
-
-    @Test
     public void testUserInitiatedClearAll_noLeak() throws Exception {
         final NotificationRecord n = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
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 ef96a2d..6ca9de3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1251,8 +1251,7 @@
         topActivity.setState(RESUMED, "true");
         doCallRealMethod().when(mRootWindowContainer).ensureActivitiesVisible(
                 any() /* starting */, anyInt() /* configChanges */,
-                anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */,
-                anyBoolean() /* userLeaving */);
+                anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */);
         topActivity.setShowWhenLocked(true);
 
         // Verify the stack-top activity is occluded keyguard.
@@ -1299,7 +1298,7 @@
         secondActivity.completeFinishing("test");
         verify(secondActivity.mDisplayContent).ensureActivitiesVisible(null /* starting */,
                 0 /* configChanges */ , false /* preserveWindows */,
-                true /* notifyClients */, false /* userLeaving */);
+                true /* notifyClients */);
 
         // Finish the first activity
         firstActivity.finishing = true;
@@ -1307,7 +1306,7 @@
         firstActivity.completeFinishing("test");
         verify(firstActivity.mDisplayContent, times(2)).ensureActivitiesVisible(null /* starting */,
                 0 /* configChanges */ , false /* preserveWindows */,
-                true /* notifyClients */, false /* userLeaving */);
+                true /* notifyClients */);
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index fc96b69..22ee886 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -1151,7 +1151,7 @@
         final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
         topActivity.info.flags |= FLAG_RESUME_WHILE_PAUSING;
 
-        task.startPausingLocked(false /* userLeaving */, false /* uiSleeping */, topActivity,
+        task.startPausingLocked(false /* uiSleeping */, topActivity,
                 "test");
         verify(task).completePauseLocked(anyBoolean(), eq(topActivity));
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 11be74d..30c1008 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -89,6 +89,7 @@
 import android.annotation.SuppressLint;
 import android.app.ActivityTaskManager;
 import android.app.WindowConfiguration;
+import android.app.servertransaction.FixedRotationAdjustmentsItem;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -1468,6 +1469,33 @@
     }
 
     @Test
+    public void testClearIntermediateFixedRotation() throws RemoteException {
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        mDisplayContent.setFixedRotationLaunchingApp(activity,
+                (mDisplayContent.getRotation() + 1) % 4);
+        // Create a window so FixedRotationAdjustmentsItem can be sent.
+        createWindow(null, TYPE_APPLICATION_STARTING, activity, "AppWin");
+        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        activity2.setVisible(false);
+        clearInvocations(mAtm.getLifecycleManager());
+        // The first activity has applied fixed rotation but the second activity becomes the top
+        // before the transition is done and it has the same rotation as display, so the dispatched
+        // rotation adjustment of first activity must be cleared.
+        mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(activity2,
+                false /* checkOpening */);
+
+        final ArgumentCaptor<FixedRotationAdjustmentsItem> adjustmentsCaptor =
+                ArgumentCaptor.forClass(FixedRotationAdjustmentsItem.class);
+        verify(mAtm.getLifecycleManager(), atLeastOnce()).scheduleTransaction(
+                eq(activity.app.getThread()), adjustmentsCaptor.capture());
+        assertFalse(activity.hasFixedRotationTransform());
+        final FixedRotationAdjustmentsItem clearAdjustments = FixedRotationAdjustmentsItem.obtain(
+                activity.token, null /* fixedRotationAdjustments */);
+        // The captor may match other items. The first one must be the item to clear adjustments.
+        assertEquals(clearAdjustments, adjustmentsCaptor.getAllValues().get(0));
+    }
+
+    @Test
     public void testRemoteRotation() {
         DisplayContent dc = createNewDisplay();
 
@@ -1613,12 +1641,11 @@
             // The assertion will fail if DisplayArea#ensureActivitiesVisible is called twice.
             assertFalse(called[0]);
             called[0] = true;
-            mDisplayContent.ensureActivitiesVisible(null, 0, false, false, false);
+            mDisplayContent.ensureActivitiesVisible(null, 0, false, false);
             return null;
-        }).when(mockTda).ensureActivitiesVisible(any(), anyInt(), anyBoolean(), anyBoolean(),
-                anyBoolean());
+        }).when(mockTda).ensureActivitiesVisible(any(), anyInt(), anyBoolean(), anyBoolean());
 
-        mDisplayContent.ensureActivitiesVisible(null, 0, false, false, false);
+        mDisplayContent.ensureActivitiesVisible(null, 0, false, false);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 63ee5d0..d017c19 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -100,8 +100,7 @@
 
         doCallRealMethod().when(mRootWindowContainer).ensureActivitiesVisible(
                 any() /* starting */, anyInt() /* configChanges */,
-                anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */,
-                anyBoolean() /* userLeaving */);
+                anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */);
 
         RecentsAnimationCallbacks recentsAnimation = startRecentsActivity(
                 mRecentsComponent, true /* getRecentsAnimation */);
@@ -192,8 +191,7 @@
 
         doCallRealMethod().when(mRootWindowContainer).ensureActivitiesVisible(
                 any() /* starting */, anyInt() /* configChanges */,
-                anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */,
-                anyBoolean() /* userLeaving */);
+                anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */);
         doReturn(app).when(mAtm).getProcessController(eq(recentActivity.processName), anyInt());
         ClientLifecycleManager lifecycleManager = mAtm.getLifecycleManager();
         doNothing().when(lifecycleManager).scheduleTransaction(any());
@@ -349,8 +347,7 @@
         doReturn(TEST_USER_ID).when(mAtm).getCurrentUserId();
         doCallRealMethod().when(mRootWindowContainer).ensureActivitiesVisible(
                 any() /* starting */, anyInt() /* configChanges */,
-                anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */,
-                anyBoolean() /* userLeaving */);
+                anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */);
 
         startRecentsActivity(otherUserHomeActivity.getTask().getBaseIntent().getComponent(),
                 true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index e1bc90a..afaf1b1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -297,7 +297,7 @@
         doReturn(true).when(mWmService.mRoot).hasAwakeDisplay();
         // Called when moving activity to pinned stack.
         doNothing().when(mWmService.mRoot).ensureActivitiesVisible(any(),
-                anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
+                anyInt(), anyBoolean(), anyBoolean());
         spyOn(mWmService.mDisplayWindowSettings);
         spyOn(mWmService.mDisplayWindowSettingsProvider);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 10d2da0..d83e9c2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -352,7 +352,8 @@
             boolean reuseCandidate) {
         final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea();
         final Task rootTask = taskDisplayArea.getOrCreateRootTask(windowingMode, activityType,
-                false /* onTop */, null /* intent */, candidateTask /* candidateTask */);
+                false /* onTop */, null /* intent */, candidateTask /* candidateTask */,
+                null /* activityOptions */);
         assertEquals(reuseCandidate, rootTask == candidateTask);
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index ccf2394..e4b865f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -28,6 +28,7 @@
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.PowerManager.GoToSleepReason;
 import android.os.PowerManager.WakeReason;
 import android.os.RemoteException;
 import android.util.proto.ProtoOutputStream;
@@ -177,19 +178,19 @@
     }
 
     @Override
-    public void startedWakingUp(@WakeReason int reason) {
+    public void startedWakingUp(@WakeReason int wakeReason) {
     }
 
     @Override
-    public void finishedWakingUp(@WakeReason int reason) {
+    public void finishedWakingUp(@WakeReason int wakeReason) {
     }
 
     @Override
-    public void startedGoingToSleep(int why) {
+    public void startedGoingToSleep(@GoToSleepReason int sleepReason) {
     }
 
     @Override
-    public void finishedGoingToSleep(int why) {
+    public void finishedGoingToSleep(@GoToSleepReason int sleepReason) {
     }
 
     @Override
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 8041fa9..e40bd8a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -5212,7 +5212,7 @@
         sDefaults.putString(KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING, "");
         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.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, true);
         sDefaults.putInt(KEY_DEFAULT_RTT_MODE_INT, 0);
     }
 
diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java
index 250d9e8..3b4cf75 100644
--- a/telephony/java/android/telephony/PreciseDisconnectCause.java
+++ b/telephony/java/android/telephony/PreciseDisconnectCause.java
@@ -121,7 +121,7 @@
     public static final int BEARER_CAPABILITY_NOT_AUTHORIZED                 = 57;
     /** The requested bearer capability is not available at this time. */
     public static final int BEARER_NOT_AVAIL                                 = 58;
-    /** The service option is not availble at this time. */
+    /** The service option is not available at this time. */
     public static final int SERVICE_OPTION_NOT_AVAILABLE                     = 63;
     /** The equipment sending this cause does not support the bearer capability requested. */
     public static final int BEARER_SERVICE_NOT_IMPLEMENTED                   = 65;
diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.aidl b/telephony/java/android/telephony/SignalStrengthUpdateRequest.aidl
new file mode 100644
index 0000000..a45de2e
--- /dev/null
+++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright (C) 2020 The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.telephony;
+
+parcelable SignalStrengthUpdateRequest;
diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
index 2a16402..af67ed2 100644
--- a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
+++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
@@ -17,6 +17,8 @@
 package android.telephony;
 
 import android.annotation.NonNull;
+import android.os.Binder;
+import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -56,6 +58,11 @@
      */
     private final boolean mIsSystemThresholdReportingRequestedWhileIdle;
 
+    /**
+     * A IBinder object as a token for server side to check if the request client is still living.
+     */
+    private final IBinder mLiveToken;
+
     private SignalStrengthUpdateRequest(
             @NonNull List<SignalThresholdInfo> signalThresholdInfos,
             boolean isReportingRequestedWhileIdle,
@@ -66,6 +73,7 @@
         mIsReportingRequestedWhileIdle = isReportingRequestedWhileIdle;
         mIsSystemThresholdReportingRequestedWhileIdle =
                 isSystemThresholdReportingRequestedWhileIdle;
+        mLiveToken = new Binder();
     }
 
     /**
@@ -148,6 +156,7 @@
         mSignalThresholdInfos = in.createTypedArrayList(SignalThresholdInfo.CREATOR);
         mIsReportingRequestedWhileIdle = in.readBoolean();
         mIsSystemThresholdReportingRequestedWhileIdle = in.readBoolean();
+        mLiveToken = in.readStrongBinder();
     }
 
     /**
@@ -178,6 +187,15 @@
         return mIsSystemThresholdReportingRequestedWhileIdle;
     }
 
+    /*
+     * @return the live token of the request
+     *
+     * @hide
+     */
+    public @NonNull IBinder getLiveToken() {
+        return mLiveToken;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -188,6 +206,7 @@
         dest.writeTypedList(mSignalThresholdInfos);
         dest.writeBoolean(mIsReportingRequestedWhileIdle);
         dest.writeBoolean(mIsSystemThresholdReportingRequestedWhileIdle);
+        dest.writeStrongBinder(mLiveToken);
     }
 
     @Override
@@ -199,10 +218,10 @@
         }
 
         SignalStrengthUpdateRequest request = (SignalStrengthUpdateRequest) other;
-        return request.mSignalThresholdInfos.equals(mSignalThresholdInfos)
-                && request.mIsReportingRequestedWhileIdle == mIsReportingRequestedWhileIdle
-                && request.mIsSystemThresholdReportingRequestedWhileIdle
-                == mIsSystemThresholdReportingRequestedWhileIdle;
+        return mSignalThresholdInfos.equals(request.mSignalThresholdInfos)
+                && mIsReportingRequestedWhileIdle == request.mIsReportingRequestedWhileIdle
+                && mIsSystemThresholdReportingRequestedWhileIdle
+                    == request.mIsSystemThresholdReportingRequestedWhileIdle;
     }
 
     @Override
@@ -233,6 +252,8 @@
                 .append(mIsReportingRequestedWhileIdle)
                 .append(" mIsSystemThresholdReportingRequestedWhileIdle=")
                 .append(mIsSystemThresholdReportingRequestedWhileIdle)
+                .append(" mLiveToken")
+                .append(mLiveToken)
                 .append("}").toString();
     }
 
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index d4c2bc9..af82991 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2609,14 +2609,45 @@
      *            requested state until explicitly cleared, or the next reboot,
      *            whichever happens first.
      * @throws SecurityException if the caller doesn't meet the requirements
-     *             outlined above.
+     *            outlined above.
      */
     public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
             @DurationMillisLong long timeoutMillis) {
+        setSubscriptionOverrideUnmetered(subId, overrideUnmetered,
+                TelephonyManager.getAllNetworkTypes(), timeoutMillis);
+    }
 
+    /**
+     * Temporarily override the billing relationship plan between a carrier and
+     * a specific subscriber to be considered unmetered. This will be reflected
+     * to apps via {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED}.
+     * <p>
+     * This method is only accessible to the following narrow set of apps:
+     * <ul>
+     * <li>The carrier app for this subscriberId, as determined by
+     * {@link TelephonyManager#hasCarrierPrivileges()}.
+     * <li>The carrier app explicitly delegated access through
+     * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
+     * </ul>
+     *
+     * @param subId the subscriber this override applies to.
+     * @param overrideUnmetered set if the billing relationship should be
+     *            considered unmetered.
+     * @param networkTypes the network types this override applies to.
+     *            {@see TelephonyManager#getAllNetworkTypes()}
+     * @param timeoutMillis the timeout after which the requested override will
+     *            be automatically cleared, or {@code 0} to leave in the
+     *            requested state until explicitly cleared, or the next reboot,
+     *            whichever happens first.
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *            outlined above.
+     */
+    public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
+            @NonNull @Annotation.NetworkType int[] networkTypes,
+            @DurationMillisLong long timeoutMillis) {
         final int overrideValue = overrideUnmetered ? SUBSCRIPTION_OVERRIDE_UNMETERED : 0;
         getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_UNMETERED,
-                overrideValue, timeoutMillis, mContext.getOpPackageName());
+                overrideValue, networkTypes, timeoutMillis, mContext.getOpPackageName());
     }
 
     /**
@@ -2641,13 +2672,46 @@
      *            requested state until explicitly cleared, or the next reboot,
      *            whichever happens first.
      * @throws SecurityException if the caller doesn't meet the requirements
-     *             outlined above.
+     *            outlined above.
      */
     public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
             @DurationMillisLong long timeoutMillis) {
+        setSubscriptionOverrideCongested(subId, overrideCongested,
+                TelephonyManager.getAllNetworkTypes(), timeoutMillis);
+    }
+
+    /**
+     * Temporarily override the billing relationship plan between a carrier and
+     * a specific subscriber to be considered congested. This will cause the
+     * device to delay certain network requests when possible, such as developer
+     * jobs that are willing to run in a flexible time window.
+     * <p>
+     * This method is only accessible to the following narrow set of apps:
+     * <ul>
+     * <li>The carrier app for this subscriberId, as determined by
+     * {@link TelephonyManager#hasCarrierPrivileges()}.
+     * <li>The carrier app explicitly delegated access through
+     * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
+     * </ul>
+     *
+     * @param subId the subscriber this override applies to.
+     * @param overrideCongested set if the subscription should be considered
+     *            congested.
+     * @param networkTypes the network types this override applies to.
+     *            {@see TelephonyManager#getAllNetworkTypes()}
+     * @param timeoutMillis the timeout after which the requested override will
+     *            be automatically cleared, or {@code 0} to leave in the
+     *            requested state until explicitly cleared, or the next reboot,
+     *            whichever happens first.
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *            outlined above.
+     */
+    public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
+            @NonNull @Annotation.NetworkType int[] networkTypes,
+            @DurationMillisLong long timeoutMillis) {
         final int overrideValue = overrideCongested ? SUBSCRIPTION_OVERRIDE_CONGESTED : 0;
         getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_CONGESTED,
-                overrideValue, timeoutMillis, mContext.getOpPackageName());
+                overrideValue, networkTypes, timeoutMillis, mContext.getOpPackageName());
     }
 
     /**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 2f577a9..216413b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7805,18 +7805,23 @@
     }
 
     /**
-     * Set IMS registration state
+     * Set IMS registration state on all active subscriptions.
+     * <p/>
+     * Use {@link android.telephony.ims.stub.ImsRegistrationImplBase#onRegistered} and
+     * {@link android.telephony.ims.stub.ImsRegistrationImplBase#onDeregistered} to set Ims
+     * registration state instead.
      *
-     * @param Registration state
+     * @param registered whether ims is registered
+     *
      * @hide
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public void setImsRegistrationState(boolean registered) {
+    public void setImsRegistrationState(final boolean registered) {
         try {
-            ITelephony telephony = getITelephony();
+            final ITelephony telephony = getITelephony();
             if (telephony != null)
                 telephony.setImsRegistrationState(registered);
-        } catch (RemoteException e) {
+        } catch (final RemoteException e) {
         }
     }
 
@@ -9806,9 +9811,16 @@
      * @return true if mobile data is enabled.
      */
     @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
-            android.Manifest.permission.MODIFY_PHONE_STATE})
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+            android.Manifest.permission.READ_PHONE_STATE})
     public boolean isDataEnabled() {
-        return getDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
+        try {
+            return isDataEnabledForReason(DATA_ENABLED_REASON_USER);
+        } catch (IllegalStateException ise) {
+            // TODO(b/176163590): Remove this catch once TelephonyManager is booting safely.
+            Log.e(TAG, "Error calling #isDataEnabled, returning default (false).", ise);
+            return false;
+        }
     }
 
     /**
@@ -10053,7 +10065,7 @@
     @SystemApi
     public boolean getDataEnabled(int subId) {
         try {
-            return isDataEnabledForReason(DATA_ENABLED_REASON_USER);
+            return isDataEnabledForReason(subId, DATA_ENABLED_REASON_USER);
         } catch (RuntimeException e) {
             Log.e(TAG, "Error calling isDataEnabledForReason e:" + e);
         }
@@ -15200,4 +15212,85 @@
         return networkType >= TelephonyManager.NETWORK_TYPE_UNKNOWN &&
                 networkType <= TelephonyManager.NETWORK_TYPE_NR;
     }
+
+    /**
+     * Set a {@link SignalStrengthUpdateRequest} to receive notification when signal quality
+     * measurements breach the specified thresholds.
+     *
+     * To be notified, set the signal strength update request and then register
+     * {@link TelephonyManager#listen(PhoneStateListener, int)} with
+     * {@link PhoneStateListener#LISTEN_SIGNAL_STRENGTHS}. The notification will arrive through
+     * {@link PhoneStateListener#onSignalStrengthsChanged(SignalStrength)}.
+     *
+     * To stop receiving the notification over the specified thresholds, pass the same
+     * {@link SignalStrengthUpdateRequest} object to
+     * {@link #clearSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)}.
+     *
+     * System will clean up the {@link SignalStrengthUpdateRequest} if the caller process died
+     * without calling {@link #clearSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)}.
+     *
+     * If this TelephonyManager object has been created with {@link #createForSubscriptionId},
+     * applies to the given subId. Otherwise, applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}. To request for multiple subIds,
+     * pass a request object to each TelephonyManager object created with
+     * {@link #createForSubscriptionId}.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * Note that the thresholds in the request will be used on a best-effort basis; the system may
+     * modify requests to multiplex various request sources or to optimize power consumption. The
+     * caller should not expect to be notified with the exactly the same thresholds.
+     *
+     * @see #clearSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)
+     *
+     * @param request the SignalStrengthUpdateRequest to be set into the System
+     *
+     * @throws IllegalStateException if a new request is set with same subId from the same caller
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setSignalStrengthUpdateRequest(@NonNull SignalStrengthUpdateRequest request) {
+        Objects.requireNonNull(request, "request must not be null");
+
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                service.setSignalStrengthUpdateRequest(getSubId(), request, getOpPackageName());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#setSignalStrengthUpdateRequest", e);
+        }
+    }
+
+    /**
+     * Clear a {@link SignalStrengthUpdateRequest} from the system.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * <p>If the given request was not set before, this operation is a no-op.
+     *
+     * @see #setSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)
+     *
+     * @param request the SignalStrengthUpdateRequest to be cleared from the System
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void clearSignalStrengthUpdateRequest(@NonNull SignalStrengthUpdateRequest request) {
+        Objects.requireNonNull(request, "request must not be null");
+
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                service.clearSignalStrengthUpdateRequest(getSubId(), request, getOpPackageName());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#clearSignalStrengthUpdateRequest", e);
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.aidl b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.aidl
new file mode 100644
index 0000000..da31f98
--- /dev/null
+++ b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package android.telephony.data;
+
+ parcelable EpsBearerQosSessionAttributes;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
new file mode 100644
index 0000000..041edc0
--- /dev/null
+++ b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.net.QosSessionAttributes;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Provides Qos attributes of an EPS bearer.
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class EpsBearerQosSessionAttributes implements Parcelable, QosSessionAttributes {
+    private static final String TAG = EpsBearerQosSessionAttributes.class.getSimpleName();
+    private final int mQci;
+    private final long mMaxUplinkBitRate;
+    private final long mMaxDownlinkBitRate;
+    private final long mGuaranteedUplinkBitRate;
+    private final long mGuaranteedDownlinkBitRate;
+    @NonNull private final List<InetSocketAddress> mRemoteAddresses;
+
+    /**
+     * Quality of Service Class Identifier (QCI), see 3GPP TS 23.203 and 29.212.
+     * The allowed values are standard values(1-9, 65-68, 69-70, 75, 79-80, 82-85)
+     * defined in the spec and operator specific values in the range 128-254.
+     *
+     * @return the qci of the session
+     */
+    public int getQci() {
+        return mQci;
+    }
+
+    /**
+     * Minimum bit rate in kbps that is guaranteed to be provided by the network on the uplink.
+     *
+     * see 3GPP TS 23.107 section 6.4.3.1
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the guaranteed bit rate in kbps
+     */
+    public long getGuaranteedUplinkBitRate() {
+        return mGuaranteedUplinkBitRate;
+    }
+
+    /**
+     * Minimum bit rate in kbps that is guaranteed to be provided by the network on the downlink.
+     *
+     * see 3GPP TS 23.107 section 6.4.3.1
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the guaranteed bit rate in kbps
+     */
+    public long getGuaranteedDownlinkBitRate() {
+        return mGuaranteedDownlinkBitRate;
+    }
+
+    /**
+     * The maximum kbps that the network will accept.
+     *
+     * see 3GPP TS 23.107 section 6.4.3.1
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the max uplink bit rate in kbps
+     */
+    public long getMaxUplinkBitRate() {
+        return mMaxUplinkBitRate;
+    }
+
+    /**
+     * The maximum kbps that the network can provide.
+     *
+     * see 3GPP TS 23.107 section 6.4.3.1
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the max downlink bit rate in kbps
+     */
+    public long getMaxDownlinkBitRate() {
+        return mMaxDownlinkBitRate;
+    }
+
+    /**
+     * List of remote addresses associated with the Qos Session.  The given uplink bit rates apply
+     * to this given list of remote addresses.
+     *
+     * Note: In the event that the list is empty, it is assumed that the uplink bit rates apply to
+     * all remote addresses that are not contained in a different set of attributes.
+     *
+     * @return list of remote socket addresses that the attributes apply to
+     */
+    @NonNull
+    public List<InetSocketAddress> getRemoteAddresses() {
+        return mRemoteAddresses;
+    }
+
+    /**
+     * ..ctor for attributes
+     *
+     * @param qci quality class indicator
+     * @param maxDownlinkBitRate the max downlink bit rate in kbps
+     * @param maxUplinkBitRate the max uplink bit rate in kbps
+     * @param guaranteedDownlinkBitRate the guaranteed downlink bit rate in kbps
+     * @param guaranteedUplinkBitRate the guaranteed uplink bit rate in kbps
+     * @param remoteAddresses the remote addresses that the uplink bit rates apply to
+     *
+     * @hide
+     */
+    public EpsBearerQosSessionAttributes(final int qci,
+            final long maxDownlinkBitRate, final long maxUplinkBitRate,
+            final long guaranteedDownlinkBitRate, final long guaranteedUplinkBitRate,
+            @NonNull final List<InetSocketAddress> remoteAddresses) {
+        Objects.requireNonNull(remoteAddresses, "remoteAddress must be non-null");
+        mQci = qci;
+        mMaxDownlinkBitRate = maxDownlinkBitRate;
+        mMaxUplinkBitRate = maxUplinkBitRate;
+        mGuaranteedDownlinkBitRate = guaranteedDownlinkBitRate;
+        mGuaranteedUplinkBitRate = guaranteedUplinkBitRate;
+
+        final List<InetSocketAddress> remoteAddressesTemp = copySocketAddresses(remoteAddresses);
+        mRemoteAddresses = Collections.unmodifiableList(remoteAddressesTemp);
+    }
+
+    private static List<InetSocketAddress> copySocketAddresses(
+            @NonNull final List<InetSocketAddress> remoteAddresses) {
+        final List<InetSocketAddress> remoteAddressesTemp = new ArrayList<>();
+        for (final InetSocketAddress socketAddress : remoteAddresses) {
+            if (socketAddress != null && socketAddress.getAddress() != null) {
+                remoteAddressesTemp.add(socketAddress);
+            }
+        }
+        return remoteAddressesTemp;
+    }
+
+    private EpsBearerQosSessionAttributes(@NonNull final Parcel in) {
+        mQci = in.readInt();
+        mMaxDownlinkBitRate = in.readLong();
+        mMaxUplinkBitRate = in.readLong();
+        mGuaranteedDownlinkBitRate = in.readLong();
+        mGuaranteedUplinkBitRate = in.readLong();
+
+        final int size = in.readInt();
+        final List<InetSocketAddress> remoteAddresses = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
+            final byte[] addressBytes = in.createByteArray();
+            final int port = in.readInt();
+            try {
+                remoteAddresses.add(
+                        new InetSocketAddress(InetAddress.getByAddress(addressBytes), port));
+            } catch (final UnknownHostException e) {
+                // Impossible case since we filter out null values in the ..ctor
+                Log.e(TAG, "unable to unparcel remote address at index: " + i, e);
+            }
+        }
+        mRemoteAddresses = Collections.unmodifiableList(remoteAddresses);
+    }
+
+    /**
+     * Creates attributes based off of a parcel
+     * @param in the parcel
+     * @return the attributes
+     */
+    @NonNull
+    public static EpsBearerQosSessionAttributes create(@NonNull final Parcel in) {
+        return new EpsBearerQosSessionAttributes(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull final Parcel dest, final int flags) {
+        dest.writeInt(mQci);
+        dest.writeLong(mMaxDownlinkBitRate);
+        dest.writeLong(mMaxUplinkBitRate);
+        dest.writeLong(mGuaranteedDownlinkBitRate);
+        dest.writeLong(mGuaranteedUplinkBitRate);
+
+        final int size = mRemoteAddresses.size();
+        dest.writeInt(size);
+        for (int i = 0; i < size; i++) {
+            final InetSocketAddress address = mRemoteAddresses.get(i);
+            dest.writeByteArray(address.getAddress().getAddress());
+            dest.writeInt(address.getPort());
+        }
+    }
+
+    @NonNull
+    public static final Creator<EpsBearerQosSessionAttributes> CREATOR =
+            new Creator<EpsBearerQosSessionAttributes>() {
+        @NonNull
+        @Override
+        public EpsBearerQosSessionAttributes createFromParcel(@NonNull final Parcel in) {
+            return new EpsBearerQosSessionAttributes(in);
+        }
+
+        @NonNull
+        @Override
+        public EpsBearerQosSessionAttributes[] newArray(final int size) {
+            return new EpsBearerQosSessionAttributes[size];
+        }
+    };
+}
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index c140249..dda021e 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -891,6 +891,12 @@
     public static final int CODE_WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 1623;
 
     /**
+     * Call failed because of network congestion, resource is not available,
+     * or no circuit or channel available, etc.
+     */
+    public static final int CODE_NETWORK_CONGESTION = 1624;
+
+    /**
      * The dialed RTT call should be retried without RTT
      * @hide
      */
@@ -1076,6 +1082,7 @@
             CODE_REJECT_VT_AVPF_NOT_ALLOWED,
             CODE_REJECT_ONGOING_ENCRYPTED_CALL,
             CODE_REJECT_ONGOING_CS_CALL,
+            CODE_NETWORK_CONGESTION,
             CODE_RETRY_ON_IMS_WITHOUT_RTT,
             CODE_OEM_CAUSE_1,
             CODE_OEM_CAUSE_2,
@@ -1268,6 +1275,7 @@
         sImsCodeMap.put(CODE_REJECT_VT_AVPF_NOT_ALLOWED, "CODE_REJECT_VT_AVPF_NOT_ALLOWED");
         sImsCodeMap.put(CODE_REJECT_ONGOING_ENCRYPTED_CALL, "CODE_REJECT_ONGOING_ENCRYPTED_CALL");
         sImsCodeMap.put(CODE_REJECT_ONGOING_CS_CALL, "CODE_REJECT_ONGOING_CS_CALL");
+        sImsCodeMap.put(CODE_NETWORK_CONGESTION, "CODE_NETWORK_CONGESTION");
         sImsCodeMap.put(CODE_RETRY_ON_IMS_WITHOUT_RTT, "CODE_RETRY_ON_IMS_WITHOUT_RTT");
         sImsCodeMap.put(CODE_OEM_CAUSE_1, "CODE_OEM_CAUSE_1");
         sImsCodeMap.put(CODE_OEM_CAUSE_2, "CODE_OEM_CAUSE_2");
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
index 8ed4838..b430bef 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -26,6 +26,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -114,6 +115,22 @@
                         AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
             }};
 
+    /** @hide */
+    @NonNull
+    static String registrationStateToString(
+            final @NetworkRegistrationInfo.RegistrationState int value) {
+        switch (value) {
+            case REGISTRATION_STATE_NOT_REGISTERED:
+                return "REGISTRATION_STATE_NOT_REGISTERED";
+            case REGISTRATION_STATE_REGISTERING:
+                return "REGISTRATION_STATE_REGISTERING";
+            case REGISTRATION_STATE_REGISTERED:
+                return "REGISTRATION_STATE_REGISTERED";
+            default:
+                return Integer.toString(value);
+        }
+    }
+
     /**
      * Callback class for receiving IMS network Registration callback events.
      * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 46c9dde..15f6c41 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -49,6 +49,7 @@
 import android.telephony.RadioAccessSpecifier;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
+import android.telephony.SignalStrengthUpdateRequest;
 import android.telephony.TelephonyHistogram;
 import android.telephony.VisualVoicemailSmsFilterSettings;
 import android.telephony.emergency.EmergencyNumber;
@@ -2388,4 +2389,17 @@
      *  their mobile plan.
      */
     String getMobileProvisioningUrl();
+
+    /**
+     * Set a SignalStrengthUpdateRequest to receive notification when Signal Strength breach the
+     * specified thresholds.
+     */
+    void setSignalStrengthUpdateRequest(int subId, in SignalStrengthUpdateRequest request,
+            String callingPackage);
+
+    /**
+     * Clear a SignalStrengthUpdateRequest from system.
+     */
+    void clearSignalStrengthUpdateRequest(int subId, in SignalStrengthUpdateRequest request,
+            String callingPackage);
 }
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index 1110790..d1a68d4 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -117,6 +117,7 @@
     method public void sendOrderedBroadcast(android.content.Intent, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle);
     method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle);
     method public void sendStickyBroadcast(android.content.Intent);
+    method public void sendStickyBroadcast(android.content.Intent, android.os.Bundle);
     method public void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
     method public void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle);
     method public void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle);
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 98e7b53..5391bd8 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -493,6 +493,11 @@
     }
 
     @Override
+    public void sendStickyBroadcast(Intent intent, Bundle options) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public void sendStickyOrderedBroadcast(Intent intent,
             BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData,
            Bundle initialExtras) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index 412a3c3..907c9b5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.server.wm.flicker.ime
 
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -51,6 +52,7 @@
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 178015460)
 class CloseImeAutoOpenWindowToAppTest(
     testName: String,
     flickerSpec: Flicker
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 663d456..4fd88f1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.server.wm.flicker.ime
 
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -51,6 +52,7 @@
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 178015460)
 class CloseImeWindowToAppTest(
     testName: String,
     flickerSpec: Flicker
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index bfe5264..765bf6d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.server.wm.flicker.ime
 
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -52,6 +53,7 @@
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 178015460)
 class CloseImeWindowToHomeTest(
     testName: String,
     flickerSpec: Flicker
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index 28a8bd3..b937f08 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.server.wm.flicker.ime
 
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -53,6 +54,7 @@
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 178015460)
 class OpenImeWindowTest(
     testName: String,
     flickerSpec: Flicker
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 3d8deb5..401d87a 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -88,7 +88,7 @@
      * Enable rollback phase.
      */
     @Test
-    public void testBadApkOnly_Phase1() throws Exception {
+    public void testBadApkOnly_Phase1_Install() throws Exception {
         Uninstall.packages(TestApp.A);
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
 
@@ -104,7 +104,7 @@
      * Confirm that rollback was successfully enabled.
      */
     @Test
-    public void testBadApkOnly_Phase2() throws Exception {
+    public void testBadApkOnly_Phase2_VerifyInstall() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
         InstallUtils.processUserData(TestApp.A);
 
@@ -129,7 +129,7 @@
      * Trigger rollback phase.
      */
     @Test
-    public void testBadApkOnly_Phase3() throws Exception {
+    public void testBadApkOnly_Phase3_Crash() throws Exception {
         // One more crash to trigger rollback
         RollbackUtils.sendCrashBroadcast(TestApp.A, 1);
     }
@@ -139,7 +139,7 @@
      * Confirm rollback phase.
      */
     @Test
-    public void testBadApkOnly_Phase4() throws Exception {
+    public void testBadApkOnly_Phase4_VerifyRollback() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
         InstallUtils.processUserData(TestApp.A);
 
@@ -158,7 +158,7 @@
      * Stage install an apk with rollback that will be later triggered by unattributable crash.
      */
     @Test
-    public void testNativeWatchdogTriggersRollback_Phase1() throws Exception {
+    public void testNativeWatchdogTriggersRollback_Phase1_Install() throws Exception {
         Uninstall.packages(TestApp.A);
         Install.single(TestApp.A1).commit();
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
@@ -170,7 +170,7 @@
      * Verify the rollback is available.
      */
     @Test
-    public void testNativeWatchdogTriggersRollback_Phase2() throws Exception {
+    public void testNativeWatchdogTriggersRollback_Phase2_VerifyInstall() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
         RollbackManager rm = RollbackUtils.getRollbackManager();
         assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
@@ -181,7 +181,7 @@
      * Verify the rollback is committed after crashing.
      */
     @Test
-    public void testNativeWatchdogTriggersRollback_Phase3() throws Exception {
+    public void testNativeWatchdogTriggersRollback_Phase3_VerifyRollback() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
         RollbackManager rm = RollbackUtils.getRollbackManager();
         assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
@@ -192,7 +192,7 @@
      * Stage install an apk with rollback that will be later triggered by unattributable crash.
      */
     @Test
-    public void testNativeWatchdogTriggersRollbackForAll_Phase1() throws Exception {
+    public void testNativeWatchdogTriggersRollbackForAll_Phase1_InstallA() throws Exception {
         Uninstall.packages(TestApp.A);
         Install.single(TestApp.A1).commit();
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
@@ -204,7 +204,7 @@
      * Verify the rollback is available and then install another package with rollback.
      */
     @Test
-    public void testNativeWatchdogTriggersRollbackForAll_Phase2() throws Exception {
+    public void testNativeWatchdogTriggersRollbackForAll_Phase2_InstallB() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
         RollbackManager rm = RollbackUtils.getRollbackManager();
         assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
@@ -222,7 +222,7 @@
      * Verify the rollbacks are available.
      */
     @Test
-    public void testNativeWatchdogTriggersRollbackForAll_Phase3() throws Exception {
+    public void testNativeWatchdogTriggersRollbackForAll_Phase3_VerifyInstall() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
         assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
         RollbackManager rm = RollbackUtils.getRollbackManager();
@@ -236,7 +236,7 @@
      * Verify the rollbacks are committed after crashing.
      */
     @Test
-    public void testNativeWatchdogTriggersRollbackForAll_Phase4() throws Exception {
+    public void testNativeWatchdogTriggersRollbackForAll_Phase4_VerifyRollback() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
         assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1);
         RollbackManager rm = RollbackUtils.getRollbackManager();
@@ -247,7 +247,7 @@
     }
 
     @Test
-    public void testPreviouslyAbandonedRollbacks_Phase1() throws Exception {
+    public void testPreviouslyAbandonedRollbacks_Phase1_InstallAndAbandon() throws Exception {
         Uninstall.packages(TestApp.A);
         Install.single(TestApp.A1).commit();
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
@@ -261,7 +261,7 @@
     }
 
     @Test
-    public void testPreviouslyAbandonedRollbacks_Phase2() throws Exception {
+    public void testPreviouslyAbandonedRollbacks_Phase2_Rollback() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
         InstallUtils.processUserData(TestApp.A);
 
@@ -272,7 +272,7 @@
     }
 
     @Test
-    public void testPreviouslyAbandonedRollbacks_Phase3() throws Exception {
+    public void testPreviouslyAbandonedRollbacks_Phase3_VerifyRollback() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
         InstallUtils.processUserData(TestApp.A);
         Uninstall.packages(TestApp.A);
@@ -284,7 +284,7 @@
     }
 
     @Test
-    public void testRollbackWhitelistedApp_Phase1() throws Exception {
+    public void testRollbackAllowlistedApp_Phase1_Install() throws Exception {
         // Remove available rollbacks
         String pkgName = getModuleMetadataPackageName();
         RollbackUtils.getRollbackManager().expireRollbackForPackage(pkgName);
@@ -296,7 +296,7 @@
                 Manifest.permission.INSTALL_PACKAGES,
                 Manifest.permission.MANAGE_ROLLBACKS);
 
-        // Re-install a whitelisted app with rollbacks enabled
+        // Re-install a allowlisted app with rollbacks enabled
         String filePath = InstrumentationRegistry.getInstrumentation().getContext()
                 .getPackageManager().getPackageInfo(pkgName, 0).applicationInfo.sourceDir;
         TestApp app = new TestApp("ModuleMetadata", pkgName, -1, false, new File(filePath));
@@ -305,12 +305,12 @@
     }
 
     @Test
-    public void testRollbackWhitelistedApp_Phase2() throws Exception {
+    public void testRollbackAllowlistedApp_Phase2_VerifyInstall() throws Exception {
         assertThat(RollbackUtils.getAvailableRollback(getModuleMetadataPackageName())).isNotNull();
     }
 
     @Test
-    public void testRollbackDataPolicy_Phase1() throws Exception {
+    public void testRollbackDataPolicy_Phase1_Install() throws Exception {
         Uninstall.packages(TestApp.A, TestApp.B, TestApp.C);
         Install.multi(TestApp.A1, TestApp.B1, TestApp.C1).commit();
         // Write user data version = 1
@@ -328,7 +328,7 @@
     }
 
     @Test
-    public void testRollbackDataPolicy_Phase2() throws Exception {
+    public void testRollbackDataPolicy_Phase2_Rollback() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
         assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
         // Write user data version = 2
@@ -341,7 +341,7 @@
     }
 
     @Test
-    public void testRollbackDataPolicy_Phase3() throws Exception {
+    public void testRollbackDataPolicy_Phase3_VerifyRollback() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
         assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1);
         assertThat(InstallUtils.getInstalledVersion(TestApp.C)).isEqualTo(1);
@@ -355,7 +355,7 @@
     }
 
     @Test
-    public void testCleanUp() throws Exception {
+    public void expireRollbacks() throws Exception {
         // testNativeWatchdogTriggersRollback will fail if multiple staged sessions are
         // committed on a device which doesn't support checkpoint. Let's clean up all rollbacks
         // so there is only one rollback to commit when testing native crashes.
@@ -378,7 +378,7 @@
             APK_IN_APEX_TESTAPEX_NAME + "_v2Crashing.apex");
 
     @Test
-    public void testRollbackApexWithApk_Phase1() throws Exception {
+    public void testRollbackApexWithApk_Phase1_Install() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
         InstallUtils.processUserData(TestApp.A);
 
@@ -388,7 +388,7 @@
     }
 
     @Test
-    public void testRollbackApexWithApk_Phase2() throws Exception {
+    public void testRollbackApexWithApk_Phase2_Rollback() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(2);
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
         InstallUtils.processUserData(TestApp.A);
@@ -416,7 +416,7 @@
     }
 
     @Test
-    public void testRollbackApexWithApk_Phase3() throws Exception {
+    public void testRollbackApexWithApk_Phase3_VerifyRollback() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(1);
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
         InstallUtils.processUserData(TestApp.A);
@@ -426,7 +426,7 @@
      * Installs an apex with an apk that can crash.
      */
     @Test
-    public void testRollbackApexWithApkCrashing_Phase1() throws Exception {
+    public void testRollbackApexWithApkCrashing_Phase1_Install() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
         int sessionId = Install.single(TEST_APEX_WITH_APK_V2_CRASHING).setStaged()
                 .setEnableRollback().commit();
@@ -437,7 +437,7 @@
      * Verifies rollback has been enabled successfully. Then makes TestApp.A crash.
      */
     @Test
-    public void testRollbackApexWithApkCrashing_Phase2() throws Exception {
+    public void testRollbackApexWithApkCrashing_Phase2_Crash() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(2);
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
 
@@ -452,20 +452,20 @@
     }
 
     @Test
-    public void testRollbackApexWithApkCrashing_Phase3() throws Exception {
+    public void testRollbackApexWithApkCrashing_Phase3_VerifyRollback() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(1);
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
     }
 
     @Test
-    public void testRollbackApexDataDirectories_Phase1() throws Exception {
+    public void testRollbackApexDataDirectories_Phase1_Install() throws Exception {
         int sessionId = Install.single(TEST_APEX_WITH_APK_V2).setStaged().setEnableRollback()
                 .commit();
         InstallUtils.waitForSessionReady(sessionId);
     }
 
     @Test
-    public void testRollbackApexDataDirectories_Phase2() throws Exception {
+    public void testRollbackApexDataDirectories_Phase2_Rollback() throws Exception {
         RollbackInfo available = RollbackUtils.getAvailableRollback(APK_IN_APEX_TESTAPEX_NAME);
 
         RollbackUtils.rollback(available.getRollbackId(), TEST_APEX_WITH_APK_V2);
@@ -477,19 +477,19 @@
     }
 
     @Test
-    public void testRollbackApkDataDirectories_Phase1() throws Exception {
+    public void testRollbackApkDataDirectories_Phase1_InstallV1() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
         Install.single(TestApp.A1).commit();
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
     }
 
     @Test
-    public void testRollbackApkDataDirectories_Phase2() throws Exception {
+    public void testRollbackApkDataDirectories_Phase2_InstallV2() throws Exception {
         Install.single(TestApp.A2).setStaged().setEnableRollback().commit();
     }
 
     @Test
-    public void testRollbackApkDataDirectories_Phase3() throws Exception {
+    public void testRollbackApkDataDirectories_Phase3_Rollback() throws Exception {
         RollbackInfo available = RollbackUtils.getAvailableRollback(TestApp.A);
         RollbackUtils.rollback(available.getRollbackId(), TestApp.A2);
         RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId());
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 94950dc..1d5730f 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
@@ -97,14 +97,14 @@
     public void setUp() throws Exception {
         deleteFiles("/system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex",
                 "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex");
-        runPhase("testCleanUp");
+        runPhase("expireRollbacks");
         mLogger.start(getDevice());
     }
 
     @After
     public void tearDown() throws Exception {
         mLogger.stop();
-        runPhase("testCleanUp");
+        runPhase("expireRollbacks");
         deleteFiles("/system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex",
                 "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex",
                 apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + "*",
@@ -139,22 +139,27 @@
         }
     }
 
+    private void waitForDeviceNotAvailable(long timeout, TimeUnit unit) {
+        assertWithMessage("waitForDeviceNotAvailable() timed out in %s %s", timeout, unit)
+                .that(getDevice().waitForDeviceNotAvailable(unit.toMillis(timeout))).isTrue();
+    }
+
     /**
      * Tests watchdog triggered staged rollbacks involving only apks.
      */
     @Test
     public void testBadApkOnly() throws Exception {
-        runPhase("testBadApkOnly_Phase1");
+        runPhase("testBadApkOnly_Phase1_Install");
         getDevice().reboot();
-        runPhase("testBadApkOnly_Phase2");
+        runPhase("testBadApkOnly_Phase2_VerifyInstall");
 
         // Trigger rollback and wait for reboot to happen
-        runPhase("testBadApkOnly_Phase3");
-        assertThat(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(2))).isTrue();
+        runPhase("testBadApkOnly_Phase3_Crash");
+        waitForDeviceNotAvailable(2, TimeUnit.MINUTES);
 
         getDevice().waitForDeviceAvailable();
 
-        runPhase("testBadApkOnly_Phase4");
+        runPhase("testBadApkOnly_Phase4_VerifyRollback");
 
         assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_APP_CRASH, TESTAPP_A);
         assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null);
@@ -163,12 +168,12 @@
 
     @Test
     public void testNativeWatchdogTriggersRollback() throws Exception {
-        runPhase("testNativeWatchdogTriggersRollback_Phase1");
+        runPhase("testNativeWatchdogTriggersRollback_Phase1_Install");
 
         // Reboot device to activate staged package
         getDevice().reboot();
 
-        runPhase("testNativeWatchdogTriggersRollback_Phase2");
+        runPhase("testNativeWatchdogTriggersRollback_Phase2_VerifyInstall");
 
         // crash system_server enough times to trigger a rollback
         crashProcess("system_server", NATIVE_CRASHES_THRESHOLD);
@@ -181,11 +186,11 @@
         // 3. Staged rollback session becomes ready.
         // 4. Device actually reboots.
         // So we give a generous timeout here.
-        assertThat(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5))).isTrue();
+        waitForDeviceNotAvailable(5, TimeUnit.MINUTES);
         getDevice().waitForDeviceAvailable();
 
         // verify rollback committed
-        runPhase("testNativeWatchdogTriggersRollback_Phase3");
+        runPhase("testNativeWatchdogTriggersRollback_Phase3_VerifyRollback");
 
         assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_NATIVE_CRASH, null);
         assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null);
@@ -198,15 +203,15 @@
         assumeTrue(isCheckpointSupported());
 
         // Install a package with rollback enabled.
-        runPhase("testNativeWatchdogTriggersRollbackForAll_Phase1");
+        runPhase("testNativeWatchdogTriggersRollbackForAll_Phase1_InstallA");
         getDevice().reboot();
 
         // Once previous staged install is applied, install another package
-        runPhase("testNativeWatchdogTriggersRollbackForAll_Phase2");
+        runPhase("testNativeWatchdogTriggersRollbackForAll_Phase2_InstallB");
         getDevice().reboot();
 
         // Verify the new staged install has also been applied successfully.
-        runPhase("testNativeWatchdogTriggersRollbackForAll_Phase3");
+        runPhase("testNativeWatchdogTriggersRollbackForAll_Phase3_VerifyInstall");
 
         // crash system_server enough times to trigger a rollback
         crashProcess("system_server", NATIVE_CRASHES_THRESHOLD);
@@ -219,11 +224,11 @@
         // 3. Staged rollback session becomes ready.
         // 4. Device actually reboots.
         // So we give a generous timeout here.
-        assertThat(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5))).isTrue();
+        waitForDeviceNotAvailable(5, TimeUnit.MINUTES);
         getDevice().waitForDeviceAvailable();
 
         // verify all available rollbacks have been committed
-        runPhase("testNativeWatchdogTriggersRollbackForAll_Phase4");
+        runPhase("testNativeWatchdogTriggersRollbackForAll_Phase4_VerifyRollback");
 
         assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_NATIVE_CRASH, null);
         assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null);
@@ -235,33 +240,33 @@
      */
     @Test
     public void testPreviouslyAbandonedRollbacks() throws Exception {
-        runPhase("testPreviouslyAbandonedRollbacks_Phase1");
+        runPhase("testPreviouslyAbandonedRollbacks_Phase1_InstallAndAbandon");
         getDevice().reboot();
-        runPhase("testPreviouslyAbandonedRollbacks_Phase2");
+        runPhase("testPreviouslyAbandonedRollbacks_Phase2_Rollback");
         getDevice().reboot();
-        runPhase("testPreviouslyAbandonedRollbacks_Phase3");
+        runPhase("testPreviouslyAbandonedRollbacks_Phase3_VerifyRollback");
     }
 
     /**
-     * Tests we can enable rollback for a whitelisted app.
+     * Tests we can enable rollback for a allowlisted app.
      */
     @Test
-    public void testRollbackWhitelistedApp() throws Exception {
+    public void testRollbackAllowlistedApp() throws Exception {
         assumeTrue(hasMainlineModule());
-        runPhase("testRollbackWhitelistedApp_Phase1");
+        runPhase("testRollbackAllowlistedApp_Phase1_Install");
         getDevice().reboot();
-        runPhase("testRollbackWhitelistedApp_Phase2");
+        runPhase("testRollbackAllowlistedApp_Phase2_VerifyInstall");
     }
 
     @Test
     public void testRollbackDataPolicy() throws Exception {
         List<String> before = getSnapshotDirectories("/data/misc_ce/0/rollback");
 
-        runPhase("testRollbackDataPolicy_Phase1");
+        runPhase("testRollbackDataPolicy_Phase1_Install");
         getDevice().reboot();
-        runPhase("testRollbackDataPolicy_Phase2");
+        runPhase("testRollbackDataPolicy_Phase2_Rollback");
         getDevice().reboot();
-        runPhase("testRollbackDataPolicy_Phase3");
+        runPhase("testRollbackDataPolicy_Phase3_VerifyRollback");
 
         // Verify snapshots are deleted after restoration
         List<String> after = getSnapshotDirectories("/data/misc_ce/0/rollback");
@@ -279,11 +284,11 @@
     public void testRollbackApexWithApk() throws Exception {
         getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A");
         pushTestApex();
-        runPhase("testRollbackApexWithApk_Phase1");
+        runPhase("testRollbackApexWithApk_Phase1_Install");
         getDevice().reboot();
-        runPhase("testRollbackApexWithApk_Phase2");
+        runPhase("testRollbackApexWithApk_Phase2_Rollback");
         getDevice().reboot();
-        runPhase("testRollbackApexWithApk_Phase3");
+        runPhase("testRollbackApexWithApk_Phase3_VerifyRollback");
     }
 
     /**
@@ -295,15 +300,15 @@
         pushTestApex();
 
         // Install an apex with apk that crashes
-        runPhase("testRollbackApexWithApkCrashing_Phase1");
+        runPhase("testRollbackApexWithApkCrashing_Phase1_Install");
         getDevice().reboot();
         // Verify apex was installed and then crash the apk
-        runPhase("testRollbackApexWithApkCrashing_Phase2");
+        runPhase("testRollbackApexWithApkCrashing_Phase2_Crash");
         // Wait for crash to trigger rollback
-        assertThat(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5))).isTrue();
+        waitForDeviceNotAvailable(5, TimeUnit.MINUTES);
         getDevice().waitForDeviceAvailable();
         // Verify rollback occurred due to crash of apk-in-apex
-        runPhase("testRollbackApexWithApkCrashing_Phase3");
+        runPhase("testRollbackApexWithApkCrashing_Phase3_VerifyRollback");
 
         assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_APP_CRASH, TESTAPP_A);
         assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null);
@@ -323,12 +328,12 @@
         String oldFilePath2 =
                 apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + TEST_SUBDIR + TEST_FILENAME_2;
         runAsRoot(() -> {
-            assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue();
-            assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue();
+            pushString(TEST_STRING_1, oldFilePath1);
+            pushString(TEST_STRING_2, oldFilePath2);
         });
 
         // Install new version of the APEX with rollback enabled
-        runPhase("testRollbackApexDataDirectories_Phase1");
+        runPhase("testRollbackApexDataDirectories_Phase1_Install");
         getDevice().reboot();
 
         // Replace files in data directory
@@ -338,20 +343,20 @@
         runAsRoot(() -> {
             getDevice().deleteFile(oldFilePath1);
             getDevice().deleteFile(oldFilePath2);
-            assertThat(getDevice().pushString(TEST_STRING_3, newFilePath3)).isTrue();
-            assertThat(getDevice().pushString(TEST_STRING_4, newFilePath4)).isTrue();
+            pushString(TEST_STRING_3, newFilePath3);
+            pushString(TEST_STRING_4, newFilePath4);
         });
 
         // Roll back the APEX
-        runPhase("testRollbackApexDataDirectories_Phase2");
+        runPhase("testRollbackApexDataDirectories_Phase2_Rollback");
         getDevice().reboot();
 
         // Verify that old files have been restored and new files are gone
         runAsRoot(() -> {
-            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();
+            assertFileContents(TEST_STRING_1, oldFilePath1);
+            assertFileContents(TEST_STRING_2, oldFilePath2);
+            assertFileNotExists(newFilePath3);
+            assertFileNotExists(newFilePath4);
         });
 
         // Verify snapshots are deleted after restoration
@@ -377,12 +382,12 @@
         String oldFilePath2 =
                 apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
         runAsRoot(() -> {
-            assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue();
-            assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue();
+            pushString(TEST_STRING_1, oldFilePath1);
+            pushString(TEST_STRING_2, oldFilePath2);
         });
 
         // Install new version of the APEX with rollback enabled
-        runPhase("testRollbackApexDataDirectories_Phase1");
+        runPhase("testRollbackApexDataDirectories_Phase1_Install");
         getDevice().reboot();
 
         // Replace files in data directory
@@ -393,20 +398,20 @@
         runAsRoot(() -> {
             getDevice().deleteFile(oldFilePath1);
             getDevice().deleteFile(oldFilePath2);
-            assertThat(getDevice().pushString(TEST_STRING_3, newFilePath3)).isTrue();
-            assertThat(getDevice().pushString(TEST_STRING_4, newFilePath4)).isTrue();
+            pushString(TEST_STRING_3, newFilePath3);
+            pushString(TEST_STRING_4, newFilePath4);
         });
 
         // Roll back the APEX
-        runPhase("testRollbackApexDataDirectories_Phase2");
+        runPhase("testRollbackApexDataDirectories_Phase2_Rollback");
         getDevice().reboot();
 
         // Verify that old files have been restored and new files are gone
         runAsRoot(() -> {
-            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();
+            assertFileContents(TEST_STRING_1, oldFilePath1);
+            assertFileContents(TEST_STRING_2, oldFilePath2);
+            assertFileNotExists(newFilePath3);
+            assertFileNotExists(newFilePath4);
         });
 
         // Verify snapshots are deleted after restoration
@@ -431,12 +436,12 @@
         String oldFilePath2 =
                 apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
         runAsRoot(() -> {
-            assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue();
-            assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue();
+            pushString(TEST_STRING_1, oldFilePath1);
+            pushString(TEST_STRING_2, oldFilePath2);
         });
 
         // Install new version of the APEX with rollback enabled
-        runPhase("testRollbackApexDataDirectories_Phase1");
+        runPhase("testRollbackApexDataDirectories_Phase1_Install");
         getDevice().reboot();
 
         // Replace files in data directory
@@ -446,20 +451,20 @@
         runAsRoot(() -> {
             getDevice().deleteFile(oldFilePath1);
             getDevice().deleteFile(oldFilePath2);
-            assertThat(getDevice().pushString(TEST_STRING_3, newFilePath3)).isTrue();
-            assertThat(getDevice().pushString(TEST_STRING_4, newFilePath4)).isTrue();
+            pushString(TEST_STRING_3, newFilePath3);
+            pushString(TEST_STRING_4, newFilePath4);
         });
 
         // Roll back the APEX
-        runPhase("testRollbackApexDataDirectories_Phase2");
+        runPhase("testRollbackApexDataDirectories_Phase2_Rollback");
         getDevice().reboot();
 
         // Verify that old files have been restored and new files are gone
         runAsRoot(() -> {
-            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();
+            assertFileContents(TEST_STRING_1, oldFilePath1);
+            assertFileContents(TEST_STRING_2, oldFilePath2);
+            assertFileNotExists(newFilePath3);
+            assertFileNotExists(newFilePath4);
         });
 
         // Verify snapshots are deleted after restoration
@@ -477,18 +482,18 @@
     @Test
     public void testRollbackApkDataDirectories_De() throws Exception {
         // Install version 1 of TESTAPP_A
-        runPhase("testRollbackApkDataDirectories_Phase1");
+        runPhase("testRollbackApkDataDirectories_Phase1_InstallV1");
 
         // Push files to apk data directory
         String oldFilePath1 = apkDataDirDe(TESTAPP_A, 0) + "/" + TEST_FILENAME_1;
         String oldFilePath2 = apkDataDirDe(TESTAPP_A, 0) + TEST_SUBDIR + TEST_FILENAME_2;
         runAsRoot(() -> {
-            assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue();
-            assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue();
+            pushString(TEST_STRING_1, oldFilePath1);
+            pushString(TEST_STRING_2, oldFilePath2);
         });
 
         // Install version 2 of TESTAPP_A with rollback enabled
-        runPhase("testRollbackApkDataDirectories_Phase2");
+        runPhase("testRollbackApkDataDirectories_Phase2_InstallV2");
         getDevice().reboot();
 
         // Replace files in data directory
@@ -497,20 +502,20 @@
         runAsRoot(() -> {
             getDevice().deleteFile(oldFilePath1);
             getDevice().deleteFile(oldFilePath2);
-            assertThat(getDevice().pushString(TEST_STRING_3, newFilePath3)).isTrue();
-            assertThat(getDevice().pushString(TEST_STRING_4, newFilePath4)).isTrue();
+            pushString(TEST_STRING_3, newFilePath3);
+            pushString(TEST_STRING_4, newFilePath4);
         });
 
         // Roll back the APK
-        runPhase("testRollbackApkDataDirectories_Phase3");
+        runPhase("testRollbackApkDataDirectories_Phase3_Rollback");
         getDevice().reboot();
 
         // Verify that old files have been restored and new files are gone
         runAsRoot(() -> {
-            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();
+            assertFileContents(TEST_STRING_1, oldFilePath1);
+            assertFileContents(TEST_STRING_2, oldFilePath2);
+            assertFileNotExists(newFilePath3);
+            assertFileNotExists(newFilePath4);
         });
     }
 
@@ -524,12 +529,12 @@
         String oldFilePath2 =
                 apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
         runAsRoot(() -> {
-            assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue();
-            assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue();
+            pushString(TEST_STRING_1, oldFilePath1);
+            pushString(TEST_STRING_2, oldFilePath2);
         });
 
         // Install new version of the APEX with rollback enabled
-        runPhase("testRollbackApexDataDirectories_Phase1");
+        runPhase("testRollbackApexDataDirectories_Phase1_Install");
         getDevice().reboot();
 
         List<String> after = getSnapshotDirectories("/data/misc_ce/0/apexrollback");
@@ -538,10 +543,10 @@
         // There should be only one /data/misc_ce/0/apexrollback/<rollbackId> created during test
         assertThat(after).hasSize(1);
         // Expire all rollbacks and check CE snapshot directories are deleted
-        runPhase("testCleanUp");
+        runPhase("expireRollbacks");
         runAsRoot(() -> {
             for (String dir : after) {
-                assertThat(getDevice().getFileEntry(dir)).isNull();
+                assertFileNotExists(dir);
             }
         });
     }
@@ -560,6 +565,23 @@
         getDevice().reboot();
     }
 
+    private void pushString(String contents, String path) throws Exception {
+        assertWithMessage("Failed to push file to device, content=%s path=%s", contents, path)
+                .that(getDevice().pushString(contents, path)).isTrue();
+    }
+
+    private void assertFileContents(String expectedContents, String path) throws Exception {
+        String actualContents = getDevice().pullFileContents(path);
+        assertWithMessage("Failed to retrieve file=%s", path).that(actualContents).isNotNull();
+        assertWithMessage("Mismatched file contents, path=%s", path)
+                .that(actualContents).isEqualTo(expectedContents);
+    }
+
+    private void assertFileNotExists(String path) throws Exception {
+        assertWithMessage("File shouldn't exist, path=%s", path)
+                .that(getDevice().getFileEntry(path)).isNull();
+    }
+
     private static String apexDataDirDeSys(String apexName) {
         return String.format("/data/misc/apexdata/%s", apexName);
     }
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index 3d4dc4d..dc9e587 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -31,6 +31,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.LinkProperties;
@@ -40,6 +41,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkProvider;
 import android.net.NetworkSpecifier;
+import android.net.QosFilter;
 import android.net.SocketKeepalive;
 import android.net.UidRange;
 import android.os.ConditionVariable;
@@ -47,10 +49,12 @@
 import android.os.Message;
 import android.util.Log;
 
+import com.android.net.module.util.ArrayTrackRecord;
 import com.android.server.connectivity.ConnectivityConstants;
 import com.android.testutils.HandlerUtils;
 import com.android.testutils.TestableNetworkCallback;
 
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -71,6 +75,8 @@
     // start/stop. Useful when simulate KeepaliveTracker is waiting for response from modem.
     private long mKeepaliveResponseDelay = 0L;
     private Integer mExpectedKeepaliveSlot = null;
+    private final ArrayTrackRecord<CallbackType>.ReadHead mCallbackHistory =
+            new ArrayTrackRecord<CallbackType>().newReadHead();
 
     public NetworkAgentWrapper(int transport, LinkProperties linkProperties,
             NetworkCapabilities ncTemplate, Context context) throws Exception {
@@ -157,6 +163,20 @@
         }
 
         @Override
+        public void onQosCallbackRegistered(final int qosCallbackId,
+                final @NonNull QosFilter filter) {
+            Log.i(mWrapper.mLogTag, "onQosCallbackRegistered");
+            mWrapper.mCallbackHistory.add(
+                    new CallbackType.OnQosCallbackRegister(qosCallbackId, filter));
+        }
+
+        @Override
+        public void onQosCallbackUnregistered(final int qosCallbackId) {
+            Log.i(mWrapper.mLogTag, "onQosCallbackUnregistered");
+            mWrapper.mCallbackHistory.add(new CallbackType.OnQosCallbackUnregister(qosCallbackId));
+        }
+
+        @Override
         protected void preventAutomaticReconnect() {
             mWrapper.mPreventReconnectReceived.open();
         }
@@ -279,7 +299,60 @@
         return mNetworkCapabilities;
     }
 
+    public @NonNull ArrayTrackRecord<CallbackType>.ReadHead getCallbackHistory() {
+        return mCallbackHistory;
+    }
+
     public void waitForIdle(long timeoutMs) {
         HandlerUtils.waitForIdle(mHandlerThread, timeoutMs);
     }
+
+    abstract static class CallbackType {
+        final int mQosCallbackId;
+
+        protected CallbackType(final int qosCallbackId) {
+            mQosCallbackId = qosCallbackId;
+        }
+
+        static class OnQosCallbackRegister extends CallbackType {
+            final QosFilter mFilter;
+            OnQosCallbackRegister(final int qosCallbackId, final QosFilter filter) {
+                super(qosCallbackId);
+                mFilter = filter;
+            }
+
+            @Override
+            public boolean equals(final Object o) {
+                if (this == o) return true;
+                if (o == null || getClass() != o.getClass()) return false;
+                final OnQosCallbackRegister that = (OnQosCallbackRegister) o;
+                return mQosCallbackId == that.mQosCallbackId
+                        && Objects.equals(mFilter, that.mFilter);
+            }
+
+            @Override
+            public int hashCode() {
+                return Objects.hash(mQosCallbackId, mFilter);
+            }
+        }
+
+        static class OnQosCallbackUnregister extends CallbackType {
+            OnQosCallbackUnregister(final int qosCallbackId) {
+                super(qosCallbackId);
+            }
+
+            @Override
+            public boolean equals(final Object o) {
+                if (this == o) return true;
+                if (o == null || getClass() != o.getClass()) return false;
+                final OnQosCallbackUnregister that = (OnQosCallbackUnregister) o;
+                return mQosCallbackId == that.mQosCallbackId;
+            }
+
+            @Override
+            public int hashCode() {
+                return Objects.hash(mQosCallbackId);
+            }
+        }
+    }
 }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 4630269..c5e6c35 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -167,6 +167,7 @@
 import android.net.INetworkMonitorCallbacks;
 import android.net.INetworkPolicyListener;
 import android.net.INetworkStatsService;
+import android.net.IQosCallback;
 import android.net.InetAddresses;
 import android.net.InterfaceConfigurationParcel;
 import android.net.IpPrefix;
@@ -190,6 +191,9 @@
 import android.net.NetworkState;
 import android.net.NetworkTestResultParcelable;
 import android.net.ProxyInfo;
+import android.net.QosCallbackException;
+import android.net.QosFilter;
+import android.net.QosSession;
 import android.net.ResolverParamsParcel;
 import android.net.RouteInfo;
 import android.net.RouteInfoParcel;
@@ -218,6 +222,7 @@
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.ServiceSpecificException;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -226,10 +231,12 @@
 import android.security.KeyStore;
 import android.system.Os;
 import android.telephony.TelephonyManager;
+import android.telephony.data.EpsBearerQosSessionAttributes;
 import android.test.mock.MockContentResolver;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Pair;
 import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
@@ -251,6 +258,7 @@
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
 import com.android.server.connectivity.ProxyTracker;
+import com.android.server.connectivity.QosCallbackTracker;
 import com.android.server.connectivity.Vpn;
 import com.android.server.net.NetworkPinner;
 import com.android.server.net.NetworkPolicyManagerInternal;
@@ -368,6 +376,8 @@
     private WrappedMultinetworkPolicyTracker mPolicyTracker;
     private HandlerThread mAlarmManagerThread;
     private TestNetIdManager mNetIdManager;
+    private QosCallbackMockHelper mQosCallbackMockHelper;
+    private QosCallbackTracker mQosCallbackTracker;
 
     @Mock DeviceIdleInternal mDeviceIdleInternal;
     @Mock INetworkManagementService mNetworkManagementService;
@@ -1395,6 +1405,7 @@
         mService.systemReadyInternal();
         mockVpn(Process.myUid());
         mCm.bindProcessToNetwork(null);
+        mQosCallbackTracker = mock(QosCallbackTracker.class);
 
         // Ensure that the default setting for Captive Portals is used for most tests
         setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
@@ -1470,6 +1481,11 @@
             mEthernetNetworkAgent.disconnect();
             mEthernetNetworkAgent = null;
         }
+
+        if (mQosCallbackMockHelper != null) {
+            mQosCallbackMockHelper.tearDown();
+            mQosCallbackMockHelper = null;
+        }
         mMockVpn.disconnect();
         waitForIdle();
 
@@ -4379,7 +4395,7 @@
     }
 
     private Network connectKeepaliveNetwork(LinkProperties lp) throws Exception {
-        // Ensure the network is disconnected before we do anything.
+        // Ensure the network is disconnected before anything else occurs
         if (mWiFiNetworkAgent != null) {
             assertNull(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()));
         }
@@ -8512,7 +8528,7 @@
                 TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE));
         return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(),
                 nc, 0, mServiceContext, null, new NetworkAgentConfig(), mService, null, null, null,
-                0, INVALID_UID);
+                0, INVALID_UID, mQosCallbackTracker);
     }
 
     @Test
@@ -8890,7 +8906,7 @@
 
     @Test
     public void testInvalidRequestTypes() {
-        final int[] invalidReqTypeInts = new int[] {-1, NetworkRequest.Type.NONE.ordinal(),
+        final int[] invalidReqTypeInts = new int[]{-1, NetworkRequest.Type.NONE.ordinal(),
                 NetworkRequest.Type.LISTEN.ordinal(), NetworkRequest.Type.values().length};
         final NetworkCapabilities nc = new NetworkCapabilities().addTransportType(TRANSPORT_WIFI);
 
@@ -8903,4 +8919,151 @@
             );
         }
     }
+
+    private class QosCallbackMockHelper {
+        @NonNull public final QosFilter mFilter;
+        @NonNull public final IQosCallback mCallback;
+        @NonNull public final TestNetworkAgentWrapper mAgentWrapper;
+        @NonNull private final List<IQosCallback> mCallbacks = new ArrayList();
+
+        QosCallbackMockHelper() throws Exception {
+            Log.d(TAG, "QosCallbackMockHelper: ");
+            mFilter = mock(QosFilter.class);
+
+            // Ensure the network is disconnected before anything else occurs
+            assertNull(mCellNetworkAgent);
+
+            mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+            mCellNetworkAgent.connect(true);
+
+            verifyActiveNetwork(TRANSPORT_CELLULAR);
+            waitForIdle();
+            final Network network = mCellNetworkAgent.getNetwork();
+
+            final Pair<IQosCallback, IBinder> pair = createQosCallback();
+            mCallback = pair.first;
+
+            when(mFilter.getNetwork()).thenReturn(network);
+            when(mFilter.validate()).thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+            mAgentWrapper = mCellNetworkAgent;
+        }
+
+        void registerQosCallback(@NonNull final QosFilter filter,
+                @NonNull final IQosCallback callback) {
+            mCallbacks.add(callback);
+            final NetworkAgentInfo nai =
+                    mService.getNetworkAgentInfoForNetwork(filter.getNetwork());
+            mService.registerQosCallbackInternal(filter, callback, nai);
+        }
+
+        void tearDown() {
+            for (int i = 0; i < mCallbacks.size(); i++) {
+                mService.unregisterQosCallback(mCallbacks.get(i));
+            }
+        }
+    }
+
+    private Pair<IQosCallback, IBinder> createQosCallback() {
+        final IQosCallback callback = mock(IQosCallback.class);
+        final IBinder binder = mock(Binder.class);
+        when(callback.asBinder()).thenReturn(binder);
+        when(binder.isBinderAlive()).thenReturn(true);
+        return new Pair<>(callback, binder);
+    }
+
+
+    @Test
+    public void testQosCallbackRegistration() throws Exception {
+        mQosCallbackMockHelper = new QosCallbackMockHelper();
+        final NetworkAgentWrapper wrapper = mQosCallbackMockHelper.mAgentWrapper;
+
+        when(mQosCallbackMockHelper.mFilter.validate())
+                .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+        mQosCallbackMockHelper.registerQosCallback(
+                mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
+
+        final NetworkAgentWrapper.CallbackType.OnQosCallbackRegister cbRegister1 =
+                (NetworkAgentWrapper.CallbackType.OnQosCallbackRegister)
+                        wrapper.getCallbackHistory().poll(1000, x -> true);
+        assertNotNull(cbRegister1);
+
+        final int registerCallbackId = cbRegister1.mQosCallbackId;
+        mService.unregisterQosCallback(mQosCallbackMockHelper.mCallback);
+        final NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister cbUnregister;
+        cbUnregister = (NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister)
+                wrapper.getCallbackHistory().poll(1000, x -> true);
+        assertNotNull(cbUnregister);
+        assertEquals(registerCallbackId, cbUnregister.mQosCallbackId);
+        assertNull(wrapper.getCallbackHistory().poll(200, x -> true));
+    }
+
+    @Test
+    public void testQosCallbackNoRegistrationOnValidationError() throws Exception {
+        mQosCallbackMockHelper = new QosCallbackMockHelper();
+
+        when(mQosCallbackMockHelper.mFilter.validate())
+                .thenReturn(QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED);
+        mQosCallbackMockHelper.registerQosCallback(
+                mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
+        waitForIdle();
+        verify(mQosCallbackMockHelper.mCallback)
+                .onError(eq(QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED));
+    }
+
+    @Test
+    public void testQosCallbackAvailableAndLost() throws Exception {
+        mQosCallbackMockHelper = new QosCallbackMockHelper();
+        final int sessionId = 10;
+        final int qosCallbackId = 1;
+
+        when(mQosCallbackMockHelper.mFilter.validate())
+                .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+        mQosCallbackMockHelper.registerQosCallback(
+                mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
+        waitForIdle();
+
+        final EpsBearerQosSessionAttributes attributes = new EpsBearerQosSessionAttributes(
+                1, 2, 3, 4, 5, new ArrayList<>());
+        mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent()
+                .sendQosSessionAvailable(qosCallbackId, sessionId, attributes);
+        waitForIdle();
+
+        verify(mQosCallbackMockHelper.mCallback).onQosEpsBearerSessionAvailable(argThat(session ->
+                session.getSessionId() == sessionId
+                        && session.getSessionType() == QosSession.TYPE_EPS_BEARER), eq(attributes));
+
+        mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent()
+                .sendQosSessionLost(qosCallbackId, sessionId);
+        waitForIdle();
+        verify(mQosCallbackMockHelper.mCallback).onQosSessionLost(argThat(session ->
+                session.getSessionId() == sessionId
+                        && session.getSessionType() == QosSession.TYPE_EPS_BEARER));
+    }
+
+    @Test
+    public void testQosCallbackTooManyRequests() throws Exception {
+        mQosCallbackMockHelper = new QosCallbackMockHelper();
+
+        when(mQosCallbackMockHelper.mFilter.validate())
+                .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+        for (int i = 0; i < 100; i++) {
+            final Pair<IQosCallback, IBinder> pair = createQosCallback();
+
+            try {
+                mQosCallbackMockHelper.registerQosCallback(
+                        mQosCallbackMockHelper.mFilter, pair.first);
+            } catch (ServiceSpecificException e) {
+                assertEquals(e.errorCode, ConnectivityManager.Errors.TOO_MANY_REQUESTS);
+                if (i < 50) {
+                    fail("TOO_MANY_REQUESTS thrown too early, the count is " + i);
+                }
+
+                // As long as there is at least 50 requests, it is safe to assume it works.
+                // Note: The count isn't being tested precisely against 100 because the counter
+                // is shared with request network.
+                return;
+            }
+        }
+        fail("TOO_MANY_REQUESTS never thrown");
+    }
 }
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 4d151af..52cb836 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -78,6 +78,7 @@
     @Mock Context mCtx;
     @Mock NetworkNotificationManager mNotifier;
     @Mock Resources mResources;
+    @Mock QosCallbackTracker mQosCallbackTracker;
 
     @Before
     public void setUp() {
@@ -358,7 +359,7 @@
         NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info,
                 new LinkProperties(), caps, 50, mCtx, null, new NetworkAgentConfig() /* config */,
                 mConnService, mNetd, mDnsResolver, mNMS, NetworkProvider.ID_NONE,
-                Binder.getCallingUid());
+                Binder.getCallingUid(), mQosCallbackTracker);
         nai.everValidated = true;
         return nai;
     }
diff --git a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java b/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
index 25bd7c0..1102624 100644
--- a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
+++ b/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
@@ -29,7 +29,6 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
 import java.util.concurrent.FutureTask;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -197,6 +196,11 @@
     }
 
     @Override
+    public void sendStickyBroadcast(Intent intent, Bundle options) {
+        sendBroadcast(intent);
+    }
+
+    @Override
     public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
         sendBroadcast(intent);
     }
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 696110f..29cfdb6f 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -23,10 +23,15 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.argThat;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -35,8 +40,10 @@
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.net.ConnectivityManager;
+import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
 import android.net.vcn.VcnConfig;
 import android.net.vcn.VcnConfigTest;
+import android.os.IBinder;
 import android.os.ParcelUuid;
 import android.os.PersistableBundle;
 import android.os.Process;
@@ -126,6 +133,10 @@
 
     private final VcnManagementService mVcnMgmtSvc;
 
+    private final IVcnUnderlyingNetworkPolicyListener mMockPolicyListener =
+            mock(IVcnUnderlyingNetworkPolicyListener.class);
+    private final IBinder mMockIBinder = mock(IBinder.class);
+
     public VcnManagementServiceTest() throws Exception {
         setupSystemService(mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
         setupSystemService(mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class);
@@ -169,6 +180,8 @@
         setupMockedCarrierPrivilege(true);
         mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps);
 
+        doReturn(mMockIBinder).when(mMockPolicyListener).asBinder();
+
         // Make sure the profiles are loaded.
         mTestLooper.dispatchAll();
     }
@@ -438,4 +451,40 @@
         mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2);
         verify(vcnInstance).teardownAsynchronously();
     }
+
+    @Test
+    public void testAddVcnUnderlyingNetworkPolicyListener() throws Exception {
+        doNothing()
+                .when(mMockContext)
+                .enforceCallingPermission(eq(android.Manifest.permission.NETWORK_FACTORY), any());
+
+        mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+        verify(mMockIBinder).linkToDeath(any(), anyInt());
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testAddVcnUnderlyingNetworkPolicyListenerInvalidPermission() {
+        doThrow(new SecurityException())
+                .when(mMockContext)
+                .enforceCallingPermission(eq(android.Manifest.permission.NETWORK_FACTORY), any());
+
+        mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+    }
+
+    @Test
+    public void testRemoveVcnUnderlyingNetworkPolicyListener() {
+        // verify listener added
+        doNothing()
+                .when(mMockContext)
+                .enforceCallingPermission(eq(android.Manifest.permission.NETWORK_FACTORY), any());
+        mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+        mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+    }
+
+    @Test
+    public void testRemoveVcnUnderlyingNetworkPolicyListenerNeverRegistered() {
+        mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+    }
 }