Merge "Single-finger multi-tap: complete immediately rather than after a delay."
diff --git a/Android.bp b/Android.bp
index 4e2b156..0a7bdb1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -509,7 +509,8 @@
     static_libs: [
         "exoplayer2-core",
         "android.hardware.wifi-V1.0-java-constants",
-     ],
+    ],
+    libs: ["icing-java-proto-lite"],
     apex_available: ["//apex_available:platform"],
 }
 
@@ -636,6 +637,7 @@
     name: "framework-ike-shared-srcs",
     visibility: ["//frameworks/opt/net/ike"],
     srcs: [
+        "core/java/android/annotation/StringDef.java",
         "core/java/android/net/annotations/PolicyDirection.java",
         "core/java/com/android/internal/util/IState.java",
         "core/java/com/android/internal/util/State.java",
diff --git a/ApiDocs.bp b/ApiDocs.bp
index e373db6..c40004c 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -121,8 +121,10 @@
 
 doc_defaults {
     name: "framework-docs-default",
-    libs: framework_docs_only_libs +
-        ["stub-annotations"],
+    libs: framework_docs_only_libs + [
+        "stub-annotations",
+        "unsupportedappusage",
+    ],
     html_dirs: [
         "docs/html",
     ],
diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp
index 1f30dda..2e88115 100644
--- a/apex/appsearch/framework/Android.bp
+++ b/apex/appsearch/framework/Android.bp
@@ -13,29 +13,32 @@
 // limitations under the License.
 
 filegroup {
-  name: "framework-appsearch-sources",
-  srcs: [
-    "java/**/*.java",
-    "java/**/*.aidl",
-  ],
-  path: "java",
+    name: "framework-appsearch-sources",
+    srcs: [
+        "java/**/*.java",
+        "java/**/*.aidl",
+    ],
+    path: "java",
 }
 
 java_library {
-  name: "framework-appsearch",
-  installable: true,
-  sdk_version: "core_platform", // TODO(b/146218515) should be core_current
-  srcs: [":framework-appsearch-sources"],
-  hostdex: true, // for hiddenapi check
-  libs: [
-    "framework-minus-apex",  // TODO(b/146218515) should be framework-system-stubs
-  ],
-  visibility: [
-      "//frameworks/base/apex/appsearch:__subpackages__",
-      // TODO(b/146218515) remove this when framework is built with the stub of appsearch
-      "//frameworks/base",
-  ],
-  apex_available: ["com.android.appsearch"],
+    name: "framework-appsearch",
+    installable: true,
+    sdk_version: "core_platform", // TODO(b/146218515) should be core_current
+    srcs: [":framework-appsearch-sources"],
+    hostdex: true, // for hiddenapi check
+    libs: [
+        "framework-minus-apex",  // TODO(b/146218515) should be framework-system-stubs
+    ],
+    static_libs: [
+        "icing-java-proto-lite",
+    ],
+    visibility: [
+        "//frameworks/base/apex/appsearch:__subpackages__",
+        // TODO(b/146218515) remove this when framework is built with the stub of appsearch
+        "//frameworks/base",
+    ],
+    apex_available: ["com.android.appsearch"],
 }
 
 metalava_appsearch_docs_args =
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index a8ee35c..58bb605 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -15,21 +15,84 @@
  */
 package android.app.appsearch;
 
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.os.RemoteException;
+
+import com.android.internal.infra.AndroidFuture;
+
+import com.google.android.icing.proto.SchemaProto;
+
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
- * TODO(b/142567528): add comments when implement this class
+ * This class provides access to the centralized AppSearch index maintained by the system.
+ *
+ * <p>Apps can index structured text documents with AppSearch, which can then be retrieved through
+ * the query API.
+ *
  * @hide
  */
 @SystemService(Context.APP_SEARCH_SERVICE)
 public class AppSearchManager {
     private final IAppSearchManager mService;
+
+    /** @hide */
+    public AppSearchManager(@NonNull IAppSearchManager service) {
+        mService = service;
+    }
+
     /**
-     * TODO(b/142567528): add comments when implement this class
+     * Sets the schema being used by documents provided to the #put method.
+     *
+     * <p>This operation is performed asynchronously. On success, the provided callback will be
+     * called with {@code null}. On failure, the provided callback will be called with a
+     * {@link Throwable} describing the failure.
+     *
+     * <p>It is a no-op to set the same schema as has been previously set; this is handled
+     * efficiently.
+     *
+     * <p>AppSearch automatically handles the following types of schema changes:
+     * <ul>
+     *     <li>Addition of new types (No changes to storage or index)
+     *     <li>Removal of an existing type (All documents of the removed type are deleted)
+     *     <li>Addition of new 'optional' property to a type (No changes to storage or index)
+     *     <li>Removal of existing property of any cardinality (All documents reindexed)
+     * </ul>
+     *
+     * <p>This method will return an error when attempting to make the following types of changes:
+     * <ul>
+     *     <li>Changing the type of an existing property
+     *     <li>Adding a 'required' property
+     * </ul>
+     *
+     * @param schema The schema config for this app.
+     * @param executor Executor on which to invoke the callback.
+     * @param callback Callback to receive errors resulting from setting the schema. If the
+     *                 operation succeeds, the callback will be invoked with {@code null}.
+     *
      * @hide
      */
-    public AppSearchManager(IAppSearchManager service) {
-        mService = service;
+    // TODO(b/143789408): linkify #put after that API is created
+    // TODO(b/145635424): add a 'force' param to setSchema after the corresponding API is finalized
+    //     in Icing Library
+    // TODO(b/145635424): Update the documentation above once the Schema mutation APIs of Icing
+    //     Library are finalized
+    public void setSchema(
+            @NonNull AppSearchSchema schema,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<? super Throwable> callback) {
+        SchemaProto schemaProto = schema.getProto();
+        byte[] schemaBytes = schemaProto.toByteArray();
+        AndroidFuture<Void> future = new AndroidFuture<>();
+        try {
+            mService.setSchema(schemaBytes, future);
+        } catch (RemoteException e) {
+            future.completeExceptionally(e);
+        }
+        future.whenCompleteAsync((noop, err) -> callback.accept(err), executor);
     }
 }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java
new file mode 100644
index 0000000..7e5f187
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java
@@ -0,0 +1,426 @@
+/*
+ * 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.app.appsearch;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import com.google.android.icing.proto.PropertyConfigProto;
+import com.google.android.icing.proto.SchemaProto;
+import com.google.android.icing.proto.SchemaTypeConfigProto;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Representation of the AppSearch Schema.
+ *
+ * <p>The schema is the set of document types, properties, and config (like tokenization type)
+ * understood by AppSearch for this app.
+ *
+ * @hide
+ */
+public final class AppSearchSchema {
+    private final SchemaProto mProto;
+
+    private AppSearchSchema(SchemaProto proto) {
+        mProto = proto;
+    }
+
+    /** Creates a new {@link AppSearchSchema.Builder}. */
+    @NonNull
+    public static AppSearchSchema.Builder newBuilder() {
+        return new AppSearchSchema.Builder();
+    }
+
+    /** Creates a new {@link SchemaType.Builder}. */
+    @NonNull
+    public static SchemaType.Builder newSchemaTypeBuilder(@NonNull String typeName) {
+        return new SchemaType.Builder(typeName);
+    }
+
+    /** Creates a new {@link PropertyConfig.Builder}. */
+    @NonNull
+    public static PropertyConfig.Builder newPropertyBuilder(@NonNull String propertyName) {
+        return new PropertyConfig.Builder(propertyName);
+    }
+
+    /** Creates a new {@link IndexingConfig.Builder}. */
+    @NonNull
+    public static IndexingConfig.Builder newIndexingConfigBuilder() {
+        return new IndexingConfig.Builder();
+    }
+
+    /**
+     * Returns the schema proto populated by the {@link AppSearchSchema} builders.
+     * @hide
+     */
+    @NonNull
+    @VisibleForTesting
+    public SchemaProto getProto() {
+        return mProto;
+    }
+
+    /** Builder for {@link AppSearchSchema objects}. */
+    public static final class Builder {
+        private final SchemaProto.Builder mProtoBuilder = SchemaProto.newBuilder();
+
+        private Builder() {}
+
+        /** Adds a supported type to this app's AppSearch schema. */
+        @NonNull
+        public AppSearchSchema.Builder addType(@NonNull SchemaType schemaType) {
+            mProtoBuilder.addTypes(schemaType.mProto);
+            return this;
+        }
+
+        /**
+         * Constructs a new {@link AppSearchSchema} from the contents of this builder.
+         *
+         * <p>After calling this method, the builder must no longer be used.
+         */
+        @NonNull
+        public AppSearchSchema build() {
+            return new AppSearchSchema(mProtoBuilder.build());
+        }
+    }
+
+    /**
+     * Represents a type of a document.
+     *
+     * <p>For example, an e-mail message or a music recording could be a schema type.
+     */
+    public static final class SchemaType {
+        private final SchemaTypeConfigProto mProto;
+
+        private SchemaType(SchemaTypeConfigProto proto) {
+            mProto = proto;
+        }
+
+        /** Builder for {@link SchemaType} objects. */
+        public static final class Builder {
+            private final SchemaTypeConfigProto.Builder mProtoBuilder =
+                    SchemaTypeConfigProto.newBuilder();
+
+            private Builder(@NonNull String typeName) {
+                mProtoBuilder.setSchemaType(typeName);
+            }
+
+            /** Adds a property to the given type. */
+            @NonNull
+            public SchemaType.Builder addProperty(@NonNull PropertyConfig propertyConfig) {
+                mProtoBuilder.addProperties(propertyConfig.mProto);
+                return this;
+            }
+
+            /**
+             * Constructs a new {@link SchemaType} from the contents of this builder.
+             *
+             * <p>After calling this method, the builder must no longer be used.
+             */
+            @NonNull
+            public SchemaType build() {
+                return new SchemaType(mProtoBuilder.build());
+            }
+        }
+    }
+
+    /**
+     * Configuration for a single property (field) of a document type.
+     *
+     * <p>For example, an {@code EmailMessage} would be a type and the {@code subject} would be
+     * a property.
+     */
+    public static final class PropertyConfig {
+        /** Physical data-types of the contents of the property. */
+        // NOTE: The integer values of these constants must match the proto enum constants in
+        // com.google.android.icing.proto.PropertyConfigProto.DataType.Code.
+        @IntDef(prefix = {"DATA_TYPE_"}, value = {
+                DATA_TYPE_STRING,
+                DATA_TYPE_INT64,
+                DATA_TYPE_DOUBLE,
+                DATA_TYPE_BOOLEAN,
+                DATA_TYPE_BYTES,
+                DATA_TYPE_DOCUMENT,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface DataType {}
+
+        public static final int DATA_TYPE_STRING = 1;
+        public static final int DATA_TYPE_INT64 = 2;
+        public static final int DATA_TYPE_DOUBLE = 3;
+        public static final int DATA_TYPE_BOOLEAN = 4;
+
+        /** Unstructured BLOB. */
+        public static final int DATA_TYPE_BYTES = 5;
+
+        /**
+         * Indicates that the property itself is an Document, making it part a hierarchical
+         * Document schema. Any property using this DataType MUST have a valid
+         * {@code schemaType}.
+         */
+        public static final int DATA_TYPE_DOCUMENT = 6;
+
+        /** The cardinality of the property (whether it is required, optional or repeated). */
+        // NOTE: The integer values of these constants must match the proto enum constants in
+        // com.google.android.icing.proto.PropertyConfigProto.Cardinality.Code.
+        @IntDef(prefix = {"CARDINALITY_"}, value = {
+                CARDINALITY_REPEATED,
+                CARDINALITY_OPTIONAL,
+                CARDINALITY_REQUIRED,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface Cardinality {}
+
+        /** Any number of items (including zero) [0...*]. */
+        public static final int CARDINALITY_REPEATED = 1;
+
+        /** Zero or one value [0,1]. */
+        public static final int CARDINALITY_OPTIONAL = 2;
+
+        /** Exactly one value [1]. */
+        public static final int CARDINALITY_REQUIRED = 3;
+
+        private final PropertyConfigProto mProto;
+
+        private PropertyConfig(PropertyConfigProto proto) {
+            mProto = proto;
+        }
+
+        /**
+         * Builder for {@link PropertyConfig}.
+         *
+         * <p>The following properties must be set, or {@link PropertyConfig} construction will
+         * fail:
+         * <ul>
+         *     <li>dataType
+         *     <li>cardinality
+         * </ul>
+         *
+         * <p>In addition, if {@code schemaType} is {@link #DATA_TYPE_DOCUMENT}, {@code schemaType}
+         * is also required.
+         */
+        public static final class Builder {
+            private final PropertyConfigProto.Builder mProtoBuilder =
+                    PropertyConfigProto.newBuilder();
+
+            private Builder(String propertyName) {
+                mProtoBuilder.setPropertyName(propertyName);
+            }
+
+            /**
+             * Type of data the property contains (e.g. string, int, bytes, etc).
+             *
+             * <p>This property must be set.
+             */
+            @NonNull
+            public PropertyConfig.Builder setDataType(@DataType int dataType) {
+                PropertyConfigProto.DataType.Code dataTypeProto =
+                        PropertyConfigProto.DataType.Code.forNumber(dataType);
+                if (dataTypeProto == null) {
+                    throw new IllegalArgumentException("Invalid dataType: " + dataType);
+                }
+                mProtoBuilder.setDataType(dataTypeProto);
+                return this;
+            }
+
+            /**
+             * The logical schema-type of the contents of this property.
+             *
+             * <p>Only required when {@link #setDataType(int)} is set to
+             * {@link #DATA_TYPE_DOCUMENT}. Otherwise, it is ignored.
+             */
+            @NonNull
+            public PropertyConfig.Builder setSchemaType(@NonNull String schemaType) {
+                mProtoBuilder.setSchemaType(schemaType);
+                return this;
+            }
+
+            /**
+             * The cardinality of the property (whether it is optional, required or repeated).
+             *
+             * <p>This property must be set.
+             */
+            @NonNull
+            public PropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
+                PropertyConfigProto.Cardinality.Code cardinalityProto =
+                        PropertyConfigProto.Cardinality.Code.forNumber(cardinality);
+                if (cardinalityProto == null) {
+                    throw new IllegalArgumentException("Invalid cardinality: " + cardinality);
+                }
+                mProtoBuilder.setCardinality(cardinalityProto);
+                return this;
+            }
+
+            /**
+             * Configures how this property should be indexed.
+             *
+             * <p>If this is not supplied, the property will not be indexed at all.
+             */
+            @NonNull
+            public PropertyConfig.Builder setIndexingConfig(
+                    @NonNull IndexingConfig indexingConfig) {
+                mProtoBuilder.setIndexingConfig(indexingConfig.mProto);
+                return this;
+            }
+
+            /**
+             * Constructs a new {@link PropertyConfig} from the contents of this builder.
+             *
+             * <p>After calling this method, the builder must no longer be used.
+             *
+             * @throws IllegalSchemaException If the property is not correctly populated (e.g.
+             *     missing {@code dataType}).
+             */
+            @NonNull
+            public PropertyConfig build() {
+                if (mProtoBuilder.getDataType() == PropertyConfigProto.DataType.Code.UNKNOWN) {
+                    throw new IllegalSchemaException("Missing field: dataType");
+                }
+                if (mProtoBuilder.getSchemaType().isEmpty()
+                        && mProtoBuilder.getDataType()
+                                == PropertyConfigProto.DataType.Code.DOCUMENT) {
+                    throw new IllegalSchemaException(
+                            "Missing field: schemaType (required for configs with "
+                                    + "dataType = DOCUMENT)");
+                }
+                if (mProtoBuilder.getCardinality()
+                        == PropertyConfigProto.Cardinality.Code.UNKNOWN) {
+                    throw new IllegalSchemaException("Missing field: cardinality");
+                }
+                return new PropertyConfig(mProtoBuilder.build());
+            }
+        }
+    }
+
+    /** Configures how a property should be indexed so that it can be retrieved by queries. */
+    public static final class IndexingConfig {
+        /** Encapsulates the configurations on how AppSearch should query/index these terms. */
+        // NOTE: The integer values of these constants must match the proto enum constants in
+        // com.google.android.icing.proto.TermMatchType.Code.
+        @IntDef(prefix = {"TERM_MATCH_TYPE_"}, value = {
+                TERM_MATCH_TYPE_UNKNOWN,
+                TERM_MATCH_TYPE_EXACT_ONLY,
+                TERM_MATCH_TYPE_PREFIX,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface TermMatchType {}
+
+        /**
+         * Content in this property will not be tokenized or indexed.
+         *
+         * <p>Useful if the data type is not made up of terms (e.g.
+         * {@link PropertyConfig#DATA_TYPE_DOCUMENT} or {@link PropertyConfig#DATA_TYPE_BYTES}
+         * type). All the properties inside the nested property won't be indexed regardless of the
+         * value of {@code termMatchType} for the nested properties.
+         */
+        public static final int TERM_MATCH_TYPE_UNKNOWN = 0;
+
+        /**
+         * Content in this property should only be returned for queries matching the exact tokens
+         * appearing in this property.
+         *
+         * <p>Ex. A property with "fool" should NOT match a query for "foo".
+         */
+        public static final int TERM_MATCH_TYPE_EXACT_ONLY = 1;
+
+        /**
+         * Content in this property should be returned for queries that are either exact matches or
+         * query matches of the tokens appearing in this property.
+         *
+         * <p>Ex. A property with "fool" <b>should</b> match a query for "foo".
+         */
+        public static final int TERM_MATCH_TYPE_PREFIX = 2;
+
+        /** Configures how tokens should be extracted from this property. */
+        // NOTE: The integer values of these constants must match the proto enum constants in
+        // com.google.android.icing.proto.IndexingConfig.TokenizerType.Code.
+        @IntDef(prefix = {"TOKENIZER_TYPE_"}, value = {
+                TOKENIZER_TYPE_NONE,
+                TOKENIZER_TYPE_PLAIN,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface TokenizerType {}
+
+        /**
+         * It is only valid for tokenizer_type to be 'NONE' if the data type is
+         * {@link PropertyConfig#DATA_TYPE_DOCUMENT}.
+         */
+        public static final int TOKENIZER_TYPE_NONE = 0;
+
+        /** Tokenization for plain text. */
+        public static final int TOKENIZER_TYPE_PLAIN = 1;
+
+        private final com.google.android.icing.proto.IndexingConfig mProto;
+
+        private IndexingConfig(com.google.android.icing.proto.IndexingConfig proto) {
+            mProto = proto;
+        }
+
+        /**
+         * Builder for {@link IndexingConfig} objects.
+         *
+         * <p>You may skip adding an {@link IndexingConfig} for a property, which is equivalent to
+         * an {@link IndexingConfig} having {@code termMatchType} equal to
+         * {@link #TERM_MATCH_TYPE_UNKNOWN}. In this case the property will not be indexed.
+         */
+        public static final class Builder {
+            private final com.google.android.icing.proto.IndexingConfig.Builder mProtoBuilder =
+                    com.google.android.icing.proto.IndexingConfig.newBuilder();
+
+            private Builder() {}
+
+            /** Configures how the content of this property should be matched in the index. */
+            @NonNull
+            public IndexingConfig.Builder setTermMatchType(@TermMatchType int termMatchType) {
+                com.google.android.icing.proto.TermMatchType.Code termMatchTypeProto =
+                        com.google.android.icing.proto.TermMatchType.Code.forNumber(termMatchType);
+                if (termMatchTypeProto == null) {
+                    throw new IllegalArgumentException("Invalid termMatchType: " + termMatchType);
+                }
+                mProtoBuilder.setTermMatchType(termMatchTypeProto);
+                return this;
+            }
+
+            /** Configures how this property should be tokenized (split into words). */
+            @NonNull
+            public IndexingConfig.Builder setTokenizerType(@TokenizerType int tokenizerType) {
+                com.google.android.icing.proto.IndexingConfig.TokenizerType.Code
+                        tokenizerTypeProto =
+                            com.google.android.icing.proto.IndexingConfig
+                                    .TokenizerType.Code.forNumber(tokenizerType);
+                if (tokenizerTypeProto == null) {
+                    throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType);
+                }
+                mProtoBuilder.setTokenizerType(tokenizerTypeProto);
+                return this;
+            }
+
+            /**
+             * Constructs a new {@link IndexingConfig} from the contents of this builder.
+             *
+             * <p>After calling this method, the builder must no longer be used.
+             */
+            @NonNull
+            public IndexingConfig build() {
+                return new IndexingConfig(mProtoBuilder.build());
+            }
+        }
+    }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index f0f4f51..8085aa8 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -14,6 +14,18 @@
  * limitations under the License.
  */
 package android.app.appsearch;
+
+import com.android.internal.infra.AndroidFuture;
+
 /** {@hide} */
 interface IAppSearchManager {
+    /**
+     * Sets the schema.
+     *
+     * @param schemaProto serialized SchemaProto
+     * @param callback {@link AndroidFuture}&lt;{@link Void}&gt;. Will be completed with
+     *     {@code null} upon successful completion of the setSchema call, or completed exceptionally
+     *     if setSchema fails.
+     */
+    void setSchema(in byte[] schemaProto, in AndroidFuture callback);
 }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IllegalSchemaException.java b/apex/appsearch/framework/java/android/app/appsearch/IllegalSchemaException.java
new file mode 100644
index 0000000..f9e528c
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/IllegalSchemaException.java
@@ -0,0 +1,36 @@
+/*
+ * 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.app.appsearch;
+
+import android.annotation.NonNull;
+
+/**
+ * Indicates that a {@link android.app.appsearch.AppSearchSchema} has logical inconsistencies such
+ * as unpopulated mandatory fields or illegal combinations of parameters.
+ *
+ * @hide
+ */
+public class IllegalSchemaException extends IllegalArgumentException {
+    /**
+     * Constructs a new {@link IllegalSchemaException}.
+     *
+     * @param message A developer-readable description of the issue with the bundle.
+     */
+    public IllegalSchemaException(@NonNull String message) {
+        super(message);
+    }
+}
diff --git a/apex/appsearch/service/Android.bp b/apex/appsearch/service/Android.bp
index 8aed5d0..7327231 100644
--- a/apex/appsearch/service/Android.bp
+++ b/apex/appsearch/service/Android.bp
@@ -12,17 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 java_library {
-  name: "service-appsearch",
-  installable: true,
-  srcs: [
-    "java/**/*.java",
-  ],
-  libs: [
-    "framework",
-    "services.core",
-  ],
-  static_libs: [
-    "icing-java-proto-lite",
-  ],
-  apex_available: [ "com.android.appsearch" ],
+    name: "service-appsearch",
+    installable: true,
+    srcs: ["java/**/*.java"],
+    libs: [
+        "framework",
+        "framework-appsearch",
+        "services.core",
+    ],
+    static_libs: ["icing-java-proto-lite"],
+    apex_available: ["com.android.appsearch"],
 }
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 4d44d9d..96316b3 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -18,8 +18,11 @@
 import android.app.appsearch.IAppSearchManager;
 import android.content.Context;
 
+import com.android.internal.infra.AndroidFuture;
 import com.android.server.SystemService;
 
+import com.google.android.icing.proto.SchemaProto;
+
 /**
  * TODO(b/142567528): add comments when implement this class
  */
@@ -35,5 +38,14 @@
     }
 
     private class Stub extends IAppSearchManager.Stub {
+        @Override
+        public void setSchema(byte[] schemaBytes, AndroidFuture callback) {
+            try {
+                SchemaProto schema = SchemaProto.parseFrom(schemaBytes);
+                throw new UnsupportedOperationException("setSchema not yet implemented: " + schema);
+            } catch (Throwable t) {
+                callback.completeExceptionally(t);
+            }
+        }
     }
 }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING b/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING
index 08811f8..ca5b884 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING
+++ b/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING
@@ -10,6 +10,14 @@
           "include-filter": "com.android.server.appsearch"
         }
       ]
+    },
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+           "include-filter": "android.app.appsearch"
+        }
+      ]
     }
   ]
 }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java b/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
index 3dbb5cf..02a79a1 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
@@ -36,8 +36,6 @@
  * <p>
  * Currently, only queries by single exact term are supported. There is no support for persistence,
  * namespaces, i18n tokenization, or schema.
- *
- * @hide
  */
 public class FakeIcing {
     private final AtomicInteger mNextDocId = new AtomicInteger();
diff --git a/apex/permission/Android.bp b/apex/permission/Android.bp
index 5945fb3..f232e62 100644
--- a/apex/permission/Android.bp
+++ b/apex/permission/Android.bp
@@ -22,6 +22,7 @@
     name: "com.android.permission-defaults",
     key: "com.android.permission.key",
     certificate: ":com.android.permission.certificate",
+    apps: ["PermissionController"],
 }
 
 apex_key {
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index bbb87ab..6444b4e 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -744,97 +744,6 @@
         return null;
     }
 
-    private void pullKernelWakelock(
-            int tagId, long elapsedNanos, long wallClockNanos,
-            List<StatsLogEventWrapper> pulledData) {
-        final KernelWakelockStats wakelockStats =
-                mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
-        for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
-            String name = ent.getKey();
-            KernelWakelockStats.Entry kws = ent.getValue();
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-            e.writeString(name);
-            e.writeInt(kws.mCount);
-            e.writeInt(kws.mVersion);
-            e.writeLong(kws.mTotalTime);
-            pulledData.add(e);
-        }
-    }
-
-    private void pullCpuTimePerFreq(
-            int tagId, long elapsedNanos, long wallClockNanos,
-            List<StatsLogEventWrapper> pulledData) {
-        for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
-            long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute();
-            if (clusterTimeMs != null) {
-                for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
-                    StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
-                            wallClockNanos);
-                    e.writeInt(cluster);
-                    e.writeInt(speed);
-                    e.writeLong(clusterTimeMs[speed]);
-                    pulledData.add(e);
-                }
-            }
-        }
-    }
-
-    private void pullKernelUidCpuTime(
-            int tagId, long elapsedNanos, long wallClockNanos,
-            List<StatsLogEventWrapper> pulledData) {
-        mCpuUidUserSysTimeReader.readAbsolute((uid, timesUs) -> {
-            long userTimeUs = timesUs[0], systemTimeUs = timesUs[1];
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-            e.writeInt(uid);
-            e.writeLong(userTimeUs);
-            e.writeLong(systemTimeUs);
-            pulledData.add(e);
-        });
-    }
-
-    private void pullKernelUidCpuFreqTime(
-            int tagId, long elapsedNanos, long wallClockNanos,
-            List<StatsLogEventWrapper> pulledData) {
-        mCpuUidFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> {
-            for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) {
-                if (cpuFreqTimeMs[freqIndex] != 0) {
-                    StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
-                            wallClockNanos);
-                    e.writeInt(uid);
-                    e.writeInt(freqIndex);
-                    e.writeLong(cpuFreqTimeMs[freqIndex]);
-                    pulledData.add(e);
-                }
-            }
-        });
-    }
-
-    private void pullKernelUidCpuClusterTime(
-            int tagId, long elapsedNanos, long wallClockNanos,
-            List<StatsLogEventWrapper> pulledData) {
-        mCpuUidClusterTimeReader.readAbsolute((uid, cpuClusterTimesMs) -> {
-            for (int i = 0; i < cpuClusterTimesMs.length; i++) {
-                StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
-                        wallClockNanos);
-                e.writeInt(uid);
-                e.writeInt(i);
-                e.writeLong(cpuClusterTimesMs[i]);
-                pulledData.add(e);
-            }
-        });
-    }
-
-    private void pullKernelUidCpuActiveTime(
-            int tagId, long elapsedNanos, long wallClockNanos,
-            List<StatsLogEventWrapper> pulledData) {
-        mCpuUidActiveTimeReader.readAbsolute((uid, cpuActiveTimesMs) -> {
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-            e.writeInt(uid);
-            e.writeLong((long) cpuActiveTimesMs);
-            pulledData.add(e);
-        });
-    }
-
     private void pullWifiActivityInfo(
             int tagId, long elapsedNanos, long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
@@ -918,13 +827,6 @@
         pulledData.add(e);
     }
 
-    private void pullSystemUpTime(int tagId, long elapsedNanos, long wallClockNanos,
-            List<StatsLogEventWrapper> pulledData) {
-        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-        e.writeLong(SystemClock.uptimeMillis());
-        pulledData.add(e);
-    }
-
     private void pullProcessMemoryState(
             int tagId, long elapsedNanos, long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
@@ -1502,21 +1404,6 @@
         }
     }
 
-    private void pullBuildInformation(int tagId,
-            long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
-        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-        e.writeString(Build.FINGERPRINT);
-        e.writeString(Build.BRAND);
-        e.writeString(Build.PRODUCT);
-        e.writeString(Build.DEVICE);
-        e.writeString(Build.VERSION.RELEASE);
-        e.writeString(Build.ID);
-        e.writeString(Build.VERSION.INCREMENTAL);
-        e.writeString(Build.TYPE);
-        e.writeString(Build.TAGS);
-        pulledData.add(e);
-    }
-
     private BatteryStatsHelper getBatteryStatsHelper() {
         if (mBatteryStatsHelper == null) {
             final long callingToken = Binder.clearCallingIdentity();
@@ -2108,36 +1995,6 @@
         long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
         switch (tagId) {
 
-            case StatsLog.KERNEL_WAKELOCK: {
-                pullKernelWakelock(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
-            case StatsLog.CPU_TIME_PER_FREQ: {
-                pullCpuTimePerFreq(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
-            case StatsLog.CPU_TIME_PER_UID: {
-                pullKernelUidCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
-            case StatsLog.CPU_TIME_PER_UID_FREQ: {
-                pullKernelUidCpuFreqTime(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
-            case StatsLog.CPU_CLUSTER_TIME: {
-                pullKernelUidCpuClusterTime(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
-            case StatsLog.CPU_ACTIVE_TIME: {
-                pullKernelUidCpuActiveTime(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
             case StatsLog.WIFI_ACTIVITY_INFO: {
                 pullWifiActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
@@ -2148,11 +2005,6 @@
                 break;
             }
 
-            case StatsLog.SYSTEM_UPTIME: {
-                pullSystemUpTime(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
             case StatsLog.SYSTEM_ELAPSED_REALTIME: {
                 pullSystemElapsedRealtime(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
@@ -2246,11 +2098,6 @@
                 break;
             }
 
-            case StatsLog.BUILD_INFORMATION: {
-                pullBuildInformation(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
             case StatsLog.PROCESS_CPU_TIME: {
                 pullProcessCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
diff --git a/api/current.txt b/api/current.txt
index ba14b4e6..5cef398 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -386,6 +386,7 @@
     field public static final int canRequestFingerprintGestures = 16844109; // 0x101054d
     field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
     field public static final int canRetrieveWindowContent = 16843653; // 0x1010385
+    field public static final int canTakeScreenshot = 16844304; // 0x1010610
     field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230
     field public static final int cantSaveState = 16844142; // 0x101056e
     field @Deprecated public static final int capitalize = 16843113; // 0x1010169
@@ -2864,6 +2865,7 @@
     method protected void onServiceConnected();
     method public final boolean performGlobalAction(int);
     method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
+    method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.graphics.Bitmap>);
     field public static final int GESTURE_SWIPE_DOWN = 2; // 0x2
     field public static final int GESTURE_SWIPE_DOWN_AND_LEFT = 15; // 0xf
     field public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 16; // 0x10
@@ -2960,6 +2962,7 @@
     field public static final int CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES = 64; // 0x40
     field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
     field public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1; // 0x1
+    field public static final int CAPABILITY_CAN_TAKE_SCREENSHOT = 128; // 0x80
     field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.AccessibilityServiceInfo> CREATOR;
     field public static final int DEFAULT = 1; // 0x1
     field public static final int FEEDBACK_ALL_MASK = -1; // 0xffffffff
@@ -11418,6 +11421,7 @@
   public final class InstallSourceInfo implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public String getInitiatingPackageName();
+    method @Nullable public android.content.pm.SigningInfo getInitiatingPackageSigningInfo();
     method @Nullable public String getInstallingPackageName();
     method @Nullable public String getOriginatingPackageName();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -11784,6 +11788,7 @@
     method @NonNull public abstract CharSequence getApplicationLabel(@NonNull android.content.pm.ApplicationInfo);
     method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull android.content.pm.ApplicationInfo);
     method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @NonNull public CharSequence getBackgroundPermissionButtonLabel();
     method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int);
     method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName);
     method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
@@ -16852,6 +16857,8 @@
     method @Nullable public CharSequence getSubtitle();
     method @NonNull public CharSequence getTitle();
     method public boolean isConfirmationRequired();
+    field public static final int AUTHENTICATION_RESULT_TYPE_BIOMETRIC = 2; // 0x2
+    field public static final int AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL = 1; // 0x1
     field public static final int BIOMETRIC_ACQUIRED_GOOD = 0; // 0x0
     field public static final int BIOMETRIC_ACQUIRED_IMAGER_DIRTY = 3; // 0x3
     field public static final int BIOMETRIC_ACQUIRED_INSUFFICIENT = 2; // 0x2
@@ -16881,6 +16888,7 @@
   }
 
   public static class BiometricPrompt.AuthenticationResult {
+    method public int getAuthenticationType();
     method public android.hardware.biometrics.BiometricPrompt.CryptoObject getCryptoObject();
   }
 
@@ -27138,9 +27146,11 @@
 
   public abstract class VolumeProvider {
     ctor public VolumeProvider(int, int, int);
+    ctor public VolumeProvider(int, int, int, @Nullable String);
     method public final int getCurrentVolume();
     method public final int getMaxVolume();
     method public final int getVolumeControl();
+    method @Nullable public final String getVolumeControlId();
     method public void onAdjustVolume(int);
     method public void onSetVolumeTo(int);
     method public final void setCurrentVolume(int);
@@ -28000,6 +28010,7 @@
     method public int getMaxVolume();
     method public int getPlaybackType();
     method public int getVolumeControl();
+    method @Nullable public String getVolumeControlId();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.media.session.MediaController.PlaybackInfo> CREATOR;
     field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
@@ -29641,7 +29652,7 @@
     method @NonNull public android.net.NetworkRequest.Builder clearCapabilities();
     method public android.net.NetworkRequest.Builder removeCapability(int);
     method public android.net.NetworkRequest.Builder removeTransportType(int);
-    method public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
+    method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
     method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
   }
 
@@ -29740,6 +29751,19 @@
     method public void onStopped();
   }
 
+  public final class TelephonyNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getSubscriptionId();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.TelephonyNetworkSpecifier> CREATOR;
+  }
+
+  public static final class TelephonyNetworkSpecifier.Builder {
+    ctor public TelephonyNetworkSpecifier.Builder();
+    method @NonNull public android.net.TelephonyNetworkSpecifier build();
+    method @NonNull public android.net.TelephonyNetworkSpecifier.Builder setSubscriptionId(int);
+  }
+
   public class TrafficStats {
     ctor public TrafficStats();
     method public static void clearThreadStatsTag();
@@ -39517,7 +39541,8 @@
     field public static final String DISPLAY_NAME = "_display_name";
     field public static final String DOCUMENT_ID = "document_id";
     field public static final String DURATION = "duration";
-    field public static final String GENERATION = "generation";
+    field public static final String GENERATION_ADDED = "generation_added";
+    field public static final String GENERATION_MODIFIED = "generation_modified";
     field public static final String GENRE = "genre";
     field public static final String HEIGHT = "height";
     field public static final String INSTANCE_ID = "instance_id";
@@ -58897,7 +58922,7 @@
     method @android.view.ViewDebug.ExportedProperty public CharSequence getFormat24Hour();
     method public String getTimeZone();
     method public boolean is24HourModeEnabled();
-    method public void refresh();
+    method public void refreshTime();
     method public void setFormat12Hour(CharSequence);
     method public void setFormat24Hour(CharSequence);
     method public void setTimeZone(String);
@@ -59212,6 +59237,7 @@
 
   public class Toast {
     ctor public Toast(android.content.Context);
+    method public void addCallback(@NonNull android.widget.Toast.Callback);
     method public void cancel();
     method public int getDuration();
     method public int getGravity();
@@ -59222,6 +59248,7 @@
     method public int getYOffset();
     method public static android.widget.Toast makeText(android.content.Context, CharSequence, int);
     method public static android.widget.Toast makeText(android.content.Context, @StringRes int, int) throws android.content.res.Resources.NotFoundException;
+    method public void removeCallback(@NonNull android.widget.Toast.Callback);
     method public void setDuration(int);
     method public void setGravity(int, int, int);
     method public void setMargin(float, float);
@@ -59233,6 +59260,12 @@
     field public static final int LENGTH_SHORT = 0; // 0x0
   }
 
+  public abstract static class Toast.Callback {
+    ctor public Toast.Callback();
+    method public void onToastHidden();
+    method public void onToastShown();
+  }
+
   public class ToggleButton extends android.widget.CompoundButton {
     ctor public ToggleButton(android.content.Context, android.util.AttributeSet, int, int);
     ctor public ToggleButton(android.content.Context, android.util.AttributeSet, int);
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 0a67065..c8253a0 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -79,3 +79,48 @@
 
 }
 
+package android.timezone {
+
+  public final class CountryTimeZones {
+    method @Nullable public android.icu.util.TimeZone getDefaultTimeZone();
+    method @Nullable public String getDefaultTimeZoneId();
+    method @NonNull public java.util.List<android.timezone.CountryTimeZones.TimeZoneMapping> getEffectiveTimeZoneMappingsAt(long);
+    method public boolean hasUtcZone(long);
+    method public boolean isDefaultTimeZoneBoosted();
+    method public boolean isForCountryCode(@NonNull String);
+    method @Nullable public android.timezone.CountryTimeZones.OffsetResult lookupByOffsetWithBias(int, @Nullable Boolean, @Nullable Integer, long, @Nullable android.icu.util.TimeZone);
+  }
+
+  public static final class CountryTimeZones.OffsetResult {
+    ctor public CountryTimeZones.OffsetResult(@NonNull android.icu.util.TimeZone, boolean);
+    method @NonNull public android.icu.util.TimeZone getTimeZone();
+    method public boolean isOnlyMatch();
+  }
+
+  public static final class CountryTimeZones.TimeZoneMapping {
+    method @Nullable public android.icu.util.TimeZone getTimeZone();
+    method @NonNull public String getTimeZoneId();
+  }
+
+  public class TelephonyLookup {
+    method @NonNull public static android.timezone.TelephonyLookup getInstance();
+    method @Nullable public android.timezone.TelephonyNetworkFinder getTelephonyNetworkFinder();
+  }
+
+  public class TelephonyNetwork {
+    method @NonNull public String getCountryIsoCode();
+    method @NonNull public String getMcc();
+    method @NonNull public String getMnc();
+  }
+
+  public class TelephonyNetworkFinder {
+    method @Nullable public android.timezone.TelephonyNetwork findNetworkByMccMnc(@NonNull String, @NonNull String);
+  }
+
+  public final class TimeZoneFinder {
+    method @NonNull public static android.timezone.TimeZoneFinder getInstance();
+    method @Nullable public android.timezone.CountryTimeZones lookupCountryTimeZones(@NonNull String);
+  }
+
+}
+
diff --git a/api/system-current.txt b/api/system-current.txt
index 71d1a38..b421807 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -791,6 +791,7 @@
 package android.app.admin {
 
   public class DevicePolicyManager {
+    method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwnerNameOnAnyUser();
@@ -1037,6 +1038,7 @@
     field public static final int AGENT_ERROR = -1003; // 0xfffffc15
     field public static final int AGENT_UNKNOWN = -1004; // 0xfffffc14
     field public static final String EXTRA_TRANSPORT_REGISTRATION = "android.app.backup.extra.TRANSPORT_REGISTRATION";
+    field public static final int FLAG_DATA_NOT_CHANGED = 8; // 0x8
     field public static final int FLAG_INCREMENTAL = 2; // 0x2
     field public static final int FLAG_NON_INCREMENTAL = 4; // 0x4
     field public static final int FLAG_USER_INITIATED = 1; // 0x1
@@ -1703,7 +1705,7 @@
     method public abstract void sendBroadcast(android.content.Intent, @Nullable String, @Nullable android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle);
     method public abstract void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
-    method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
+    method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
     field public static final String APP_INTEGRITY_SERVICE = "app_integrity";
     field public static final String APP_PREDICTION_SERVICE = "app_prediction";
     field public static final String BACKUP_SERVICE = "backup";
@@ -4802,6 +4804,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
     method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
+    method @Deprecated public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int, int, @NonNull android.os.Handler);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
     method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
@@ -4819,6 +4822,7 @@
     field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
     field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
     field public static final int TYPE_NONE = -1; // 0xffffffff
+    field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd
   }
 
   public abstract static class ConnectivityManager.OnStartTetheringCallback {
@@ -4967,6 +4971,27 @@
     field public final int netId;
   }
 
+  public abstract class NetworkAgent {
+    method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
+    method public void onAutomaticReconnectDisabled();
+    method public void onBandwidthUpdateRequested();
+    method public void onNetworkUnwanted();
+    method public void onRemoveKeepalivePacketFilter(int);
+    method public void onSaveAcceptUnvalidated(boolean);
+    method public void onSignalStrengthThresholdsUpdated(@NonNull int[]);
+    method public void onStartSocketKeepalive(int, int, @NonNull android.net.KeepalivePacketData);
+    method public void onStopSocketKeepalive(int);
+    method public void onValidationStatus(int, @Nullable String);
+    method public void sendLinkProperties(@NonNull android.net.LinkProperties);
+    method public void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
+    method public void sendNetworkScore(int);
+    method public void sendSocketKeepaliveEvent(int, int);
+    field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2
+    field public static final int VALIDATION_STATUS_VALID = 1; // 0x1
+    field @NonNull public final android.net.Network network;
+    field public final int providerId;
+  }
+
   public final class NetworkAgentConfig implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public String getSubscriberId();
@@ -5022,6 +5047,10 @@
     method public abstract void onRequestScores(android.net.NetworkKey[]);
   }
 
+  public class NetworkRequest implements android.os.Parcelable {
+    method public boolean satisfiedBy(@Nullable android.net.NetworkCapabilities);
+  }
+
   public static class NetworkRequest.Builder {
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int);
   }
@@ -5128,6 +5157,10 @@
     field public final android.net.RssiCurve rssiCurve;
   }
 
+  public abstract class SocketKeepalive implements java.lang.AutoCloseable {
+    field public static final int SUCCESS = 0; // 0x0
+  }
+
   public final class StaticIpConfiguration implements android.os.Parcelable {
     ctor public StaticIpConfiguration();
     ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration);
@@ -5161,6 +5194,10 @@
     field @NonNull public final String specifier;
   }
 
+  public final class TelephonyNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
+    method public boolean satisfiedBy(android.net.NetworkSpecifier);
+  }
+
   public class TrafficStats {
     method public static void setThreadStatsTagApp();
     method public static void setThreadStatsTagBackup();
@@ -10567,6 +10604,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(int);
     method public String getCdmaPrlVersion();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaRoamingMode();
     method public int getCurrentPhoneType();
     method public int getCurrentPhoneType(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getDataActivationState();
@@ -10640,6 +10678,8 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAlwaysAllowMmsData(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCdmaRoamingMode(int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCdmaSubscriptionMode(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
@@ -10683,6 +10723,9 @@
     field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
     field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
     field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
+    field public static final int CDMA_SUBSCRIPTION_NV = 1; // 0x1
+    field public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; // 0x0
+    field public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1; // 0xffffffff
     field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION";
     field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID";
     field @Deprecated public static final String EXTRA_APN_PROTOCOL = "apnProto";
@@ -11596,6 +11639,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public String getProvisioningStringValue(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, boolean);
@@ -11860,6 +11904,7 @@
     method public String getConfigString(int);
     method public final void notifyProvisionedValueChanged(int, int);
     method public final void notifyProvisionedValueChanged(int, String);
+    method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
     method public int setConfig(int, int);
     method public int setConfig(int, String);
     field public static final int CONFIG_RESULT_FAILED = 1; // 0x1
@@ -12237,6 +12282,12 @@
     method public void onJsResultComplete(android.webkit.JsResult);
   }
 
+  public interface PacProcessor {
+    method @NonNull public static android.webkit.PacProcessor getInstance();
+    method @Nullable public String makeProxyRequest(@NonNull String);
+    method public boolean setProxyScript(@NonNull String);
+  }
+
   public class SslErrorHandler extends android.os.Handler {
     ctor public SslErrorHandler();
   }
@@ -12371,6 +12422,7 @@
     method public android.webkit.WebViewProvider createWebView(android.webkit.WebView, android.webkit.WebView.PrivateAccess);
     method public android.webkit.CookieManager getCookieManager();
     method public android.webkit.GeolocationPermissions getGeolocationPermissions();
+    method @NonNull public default android.webkit.PacProcessor getPacProcessor();
     method public android.webkit.ServiceWorkerController getServiceWorkerController();
     method public android.webkit.WebViewFactoryProvider.Statics getStatics();
     method @Deprecated public android.webkit.TokenBindingService getTokenBindingService();
diff --git a/api/test-current.txt b/api/test-current.txt
index 1db4c9b..10028f2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -471,7 +471,10 @@
   }
 
   public class WallpaperManager {
+    method @Nullable public android.graphics.Bitmap getBitmap();
     method @RequiresPermission("android.permission.SET_WALLPAPER_COMPONENT") public boolean setWallpaperComponent(android.content.ComponentName);
+    method public boolean shouldEnableWideColorGamut();
+    method @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public boolean wallpaperSupportsWcg(int);
   }
 
   public class WindowConfiguration implements java.lang.Comparable<android.app.WindowConfiguration> android.os.Parcelable {
@@ -3827,6 +3830,7 @@
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public boolean getProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
     method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public String getProvisioningStringValue(int);
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public boolean getRcsProvisioningStatusForCapability(int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, boolean);
@@ -4050,6 +4054,7 @@
     method public String getConfigString(int);
     method public final void notifyProvisionedValueChanged(int, int);
     method public final void notifyProvisionedValueChanged(int, String);
+    method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
     method public int setConfig(int, int);
     method public int setConfig(int, String);
     field public static final int CONFIG_RESULT_FAILED = 1; // 0x1
@@ -4379,6 +4384,7 @@
   }
 
   public final class Display {
+    method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut();
     method public boolean hasAccess(int);
   }
 
@@ -4496,6 +4502,8 @@
   }
 
   public class AccessibilityNodeInfo implements android.os.Parcelable {
+    method public void addChild(@NonNull android.os.IBinder);
+    method public void setLeashedParent(@Nullable android.os.IBinder, int);
     method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
     method public void writeToParcelNoRecycle(android.os.Parcel, int);
   }
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index 6033655..c2ee6dc 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -103,6 +103,10 @@
             runSetVirtualDisk();
         } else if ("set-isolated-storage".equals(op)) {
             runIsolatedStorage();
+        } else if ("start-checkpoint".equals(op)) {
+            runStartCheckpoint();
+        } else if ("supports-checkpoint".equals(op)) {
+            runSupportsCheckpoint();
         } else {
             throw new IllegalArgumentException();
         }
@@ -313,6 +317,27 @@
         }
     }
 
+    private void runStartCheckpoint() throws RemoteException {
+        final String numRetriesString = nextArg();
+        if (numRetriesString == null) {
+            throw new IllegalArgumentException("Expected <num-retries>");
+        }
+        int numRetries;
+        try {
+            numRetries = Integer.parseInt(numRetriesString);
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException("<num-retries> must be a positive integer");
+        }
+        if (numRetries <= 0) {
+            throw new IllegalArgumentException("<num-retries> must be a positive integer");
+        }
+        mSm.startCheckpoint(numRetries);
+    }
+
+    private void runSupportsCheckpoint() throws RemoteException {
+        System.out.println(mSm.supportsCheckpoint());
+    }
+
     private String nextArg() {
         if (mNextArg >= mArgs.length) {
             return null;
@@ -344,6 +369,10 @@
         System.err.println("");
         System.err.println("       sm set-isolated-storage [on|off|default]");
         System.err.println("");
+        System.err.println("       sm start-checkpoint <num-retries>");
+        System.err.println("");
+        System.err.println("       sm supports-checkpoint");
+        System.err.println("");
         return 1;
     }
 }
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 1441d48..cbece78 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -338,6 +338,7 @@
         BootTimeEventElapsedTime boot_time_event_elapsed_time_reported = 240;
         BootTimeEventUtcTime boot_time_event_utc_time_reported = 241;
         BootTimeEventErrorCode boot_time_event_error_code_reported = 242;
+        UserspaceRebootReported userspace_reboot_reported = 243;
     }
 
     // Pulled events will start at field 10000.
@@ -7904,4 +7905,42 @@
     optional SamplingStrategy sampling_strategy = 6;
 }
 
-
+/*
+ * Logs userspace reboot outcome and duration.
+ *
+ * Logged from:
+ *   frameworks/base/core/java/com/android/server/BootReceiver.java
+ */
+message UserspaceRebootReported {
+    // Possible outcomes of userspace reboot.
+    enum Outcome {
+        // Default value in case platform failed to determine the outcome.
+        OUTCOME_UNKNOWN = 0;
+        // Userspace reboot succeeded (i.e. boot completed without a fall back to hard reboot).
+        SUCCESS = 1;
+        // Userspace reboot shutdown sequence was aborted.
+        FAILED_SHUTDOWN_SEQUENCE_ABORTED = 2;
+        // Remounting userdata into checkpointing mode failed.
+        FAILED_USERDATA_REMOUNT = 3;
+        // Device didn't finish booting before timeout and userspace reboot watchdog issued a hard
+        // reboot.
+        FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED = 4;
+    }
+    // Outcome of userspace reboot. Always set.
+    optional Outcome outcome = 1;
+    // Duration of userspace reboot in case it has a successful outcome.
+    // Duration is measured as time between userspace reboot was initiated and until boot completed
+    // (e.g. sys.boot_completed=1).
+    optional int64 duration_millis = 2;
+    // State of primary user's (user0) credential encryption storage.
+    enum UserEncryptionState {
+        // Default value.
+        USER_ENCRYPTION_STATE_UNKNOWN = 0;
+        // Credential encrypted storage is unlocked.
+        UNLOCKED = 1;
+        // Credential encrypted storage is locked.
+        LOCKED = 2;
+    }
+    // State of primary user's encryption storage at the moment boot completed. Always set.
+    optional UserEncryptionState user_encryption_state = 3;
+}
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 731afe8..b5920cb 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -60,10 +60,6 @@
 
 std::map<PullerKey, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
 
-        // kernel_wakelock
-        {{.atomTag = android::util::KERNEL_WAKELOCK},
-         {.puller = new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
-
         // subsystem_sleep_state
         {{.atomTag = android::util::SUBSYSTEM_SLEEP_STATE},
          {.puller = new SubsystemSleepStatePuller()}},
@@ -72,37 +68,6 @@
         {{.atomTag = android::util::ON_DEVICE_POWER_MEASUREMENT},
          {.puller = new PowerStatsPuller()}},
 
-        // cpu_time_per_freq
-        {{.atomTag = android::util::CPU_TIME_PER_FREQ},
-         {.additiveFields = {3},
-          .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
-
-        // cpu_time_per_uid
-        {{.atomTag = android::util::CPU_TIME_PER_UID},
-         {.additiveFields = {2, 3},
-          .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
-
-        // cpu_time_per_uid_freq
-        // the throttling is 3sec, handled in
-        // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
-        {{.atomTag = android::util::CPU_TIME_PER_UID_FREQ},
-         {.additiveFields = {4},
-          .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
-
-        // cpu_active_time
-        // the throttling is 3sec, handled in
-        // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
-        {{.atomTag = android::util::CPU_ACTIVE_TIME},
-         {.additiveFields = {2},
-          .puller = new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
-
-        // cpu_cluster_time
-        // the throttling is 3sec, handled in
-        // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
-        {{.atomTag = android::util::CPU_CLUSTER_TIME},
-         {.additiveFields = {3},
-          .puller = new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
-
         // wifi_activity_energy_info
         {{.atomTag = android::util::WIFI_ACTIVITY_INFO},
          {.puller = new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
@@ -118,10 +83,6 @@
           .pullTimeoutNs = NS_PER_SEC / 2,
          }},
 
-        // system_uptime
-        {{.atomTag = android::util::SYSTEM_UPTIME},
-         {.puller = new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
-
         // remaining_battery_capacity
         {{.atomTag = android::util::REMAINING_BATTERY_CAPACITY},
          {.puller = new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
@@ -256,10 +217,6 @@
          {.additiveFields = {1, 2, 3, 4},
           .puller = new StatsCompanionServicePuller(android::util::DEBUG_FAILING_ELAPSED_CLOCK)}},
 
-        // BuildInformation.
-        {{.atomTag = android::util::BUILD_INFORMATION},
-         {.puller = new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}},
-
         // RoleHolder.
         {{.atomTag = android::util::ROLE_HOLDER},
          {.puller = new StatsCompanionServicePuller(android::util::ROLE_HOLDER)}},
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 7b651df..e0aecce 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -617,19 +617,6 @@
 
 void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid) {
     EXPECT_EQ(value.field(), atomId);
-    // Attribution field.
-    EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1);
-    // Uid only.
-    EXPECT_EQ(value.value_tuple().dimensions_value(0)
-        .value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(value.value_tuple().dimensions_value(0)
-        .value_tuple().dimensions_value(0).field(), 1);
-    EXPECT_EQ(value.value_tuple().dimensions_value(0)
-        .value_tuple().dimensions_value(0).value_int(), uid);
-}
-
-void ValidateUidDimension(const DimensionsValue& value, int atomId, int uid) {
-    EXPECT_EQ(value.field(), atomId);
     EXPECT_EQ(value.value_tuple().dimensions_value_size(), 1);
     // Attribution field.
     EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1);
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index e46840c..0bd8ce6 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -17,6 +17,7 @@
 package android.accessibilityservice;
 
 import android.accessibilityservice.GestureDescription.MotionEventGenerator;
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -26,12 +27,15 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
+import android.graphics.Bitmap;
 import android.graphics.Region;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -48,10 +52,13 @@
 
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.SomeArgs;
+import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
  * Accessibility services should only be used to assist users with disabilities in using
@@ -483,6 +490,9 @@
 
     private FingerprintGestureController mFingerprintGestureController;
 
+    /** @hide */
+    public static final String KEY_ACCESSIBILITY_SCREENSHOT = "screenshot";
+
     /**
      * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
      *
@@ -1761,6 +1771,51 @@
     }
 
     /**
+     * Takes a screenshot of the specified display and returns it by {@link Bitmap.Config#HARDWARE}
+     * format.
+     * <p>
+     * <strong>Note:</strong> In order to take screenshot your service has
+     * to declare the capability to take screenshot by setting the
+     * {@link android.R.styleable#AccessibilityService_canTakeScreenshot}
+     * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
+     * Besides, This API is only supported for default display now
+     * {@link Display#DEFAULT_DISPLAY}.
+     * </p>
+     *
+     * @param displayId The logic display id, must be {@link Display#DEFAULT_DISPLAY} for
+     *                  default display.
+     * @param executor Executor on which to run the callback.
+     * @param callback The callback invoked when the taking screenshot is done.
+     *
+     * @return {@code true} if the taking screenshot accepted, {@code false} if not.
+     */
+    public boolean takeScreenshot(int displayId, @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<Bitmap> callback) {
+        Preconditions.checkNotNull(executor, "executor cannot be null");
+        Preconditions.checkNotNull(callback, "callback cannot be null");
+        final IAccessibilityServiceConnection connection =
+                AccessibilityInteractionClient.getInstance().getConnection(
+                        mConnectionId);
+        if (connection == null) {
+            return false;
+        }
+        try {
+            connection.takeScreenshotWithCallback(displayId, new RemoteCallback((result) -> {
+                final Bitmap screenshot = result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT);
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> callback.accept(screenshot));
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }));
+        } catch (RemoteException re) {
+            throw new RuntimeException(re);
+        }
+        return true;
+    }
+
+    /**
      * Implement to return the implementation of the internal accessibility
      * service interface.
      */
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 5e2c1fa..12f2c3b 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -86,6 +86,7 @@
  * @attr ref android.R.styleable#AccessibilityService_settingsActivity
  * @attr ref android.R.styleable#AccessibilityService_nonInteractiveUiTimeout
  * @attr ref android.R.styleable#AccessibilityService_interactiveUiTimeout
+ * @attr ref android.R.styleable#AccessibilityService_canTakeScreenshot
  * @see AccessibilityService
  * @see android.view.accessibility.AccessibilityEvent
  * @see android.view.accessibility.AccessibilityManager
@@ -136,6 +137,12 @@
      */
     public static final int CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES = 0x00000040;
 
+    /**
+     * Capability: This accessibility service can take screenshot.
+     * @see android.R.styleable#AccessibilityService_canTakeScreenshot
+     */
+    public static final int CAPABILITY_CAN_TAKE_SCREENSHOT = 0x00000080;
+
     private static SparseArray<CapabilityInfo> sAvailableCapabilityInfos;
 
     /**
@@ -625,6 +632,10 @@
                     .AccessibilityService_canRequestFingerprintGestures, false)) {
                 mCapabilities |= CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES;
             }
+            if (asAttributes.getBoolean(com.android.internal.R.styleable
+                    .AccessibilityService_canTakeScreenshot, false)) {
+                mCapabilities |= CAPABILITY_CAN_TAKE_SCREENSHOT;
+            }
             TypedValue peekedValue = asAttributes.peekValue(
                     com.android.internal.R.styleable.AccessibilityService_description);
             if (peekedValue != null) {
@@ -794,6 +805,7 @@
      * @see #CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS
      * @see #CAPABILITY_CAN_CONTROL_MAGNIFICATION
      * @see #CAPABILITY_CAN_PERFORM_GESTURES
+     * @see #CAPABILITY_CAN_TAKE_SCREENSHOT
      */
     public int getCapabilities() {
         return mCapabilities;
@@ -810,6 +822,7 @@
      * @see #CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS
      * @see #CAPABILITY_CAN_CONTROL_MAGNIFICATION
      * @see #CAPABILITY_CAN_PERFORM_GESTURES
+     * @see #CAPABILITY_CAN_TAKE_SCREENSHOT
      *
      * @hide
      */
@@ -1253,6 +1266,8 @@
                 return "CAPABILITY_CAN_PERFORM_GESTURES";
             case CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES:
                 return "CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES";
+            case CAPABILITY_CAN_TAKE_SCREENSHOT:
+                return "CAPABILITY_CAN_TAKE_SCREENSHOT";
             default:
                 return "UNKNOWN";
         }
@@ -1314,6 +1329,10 @@
                     new CapabilityInfo(CAPABILITY_CAN_PERFORM_GESTURES,
                             R.string.capability_title_canPerformGestures,
                             R.string.capability_desc_canPerformGestures));
+            sAvailableCapabilityInfos.put(CAPABILITY_CAN_TAKE_SCREENSHOT,
+                    new CapabilityInfo(CAPABILITY_CAN_TAKE_SCREENSHOT,
+                            R.string.capability_title_canTakeScreenshot,
+                            R.string.capability_desc_canTakeScreenshot));
             if ((context == null) || fingerprintAvailable(context)) {
                 sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES,
                         new CapabilityInfo(CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES,
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 656f87f..4ea5c62 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -18,8 +18,10 @@
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.content.pm.ParceledListSlice;
+import android.graphics.Bitmap;
 import android.graphics.Region;
 import android.os.Bundle;
+import android.os.RemoteCallback;
 import android.view.MagnificationSpec;
 import android.view.MotionEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -102,4 +104,10 @@
     boolean isFingerprintGestureDetectionAvailable();
 
     IBinder getOverlayWindowToken(int displayid);
+
+    int getWindowIdForLeashToken(IBinder token);
+
+    Bitmap takeScreenshot(int displayId);
+
+    void takeScreenshotWithCallback(int displayId, in RemoteCallback callback);
 }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 1c6a561..d39a8c4 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -133,6 +133,10 @@
     // Default flags to use with PackageManager when no flags are given.
     private final static int sDefaultFlags = PackageManager.GET_SHARED_LIBRARY_FILES;
 
+    // Name of the resource which provides background permission button string
+    public static final String APP_PERMISSION_BUTTON_ALLOW_ALWAYS =
+            "app_permission_button_allow_always";
+
     private final Object mLock = new Object();
 
     @GuardedBy("mLock")
@@ -807,6 +811,26 @@
     }
 
     @Override
+    public CharSequence getBackgroundPermissionButtonLabel() {
+        try {
+
+            String permissionController = getPermissionControllerPackageName();
+            Context context =
+                    mContext.createPackageContext(permissionController, 0);
+
+            int textId = context.getResources().getIdentifier(APP_PERMISSION_BUTTON_ALLOW_ALWAYS,
+                    "string", "com.android.permissioncontroller");
+//                    permissionController); STOPSHIP b/147434671
+            if (textId != 0) {
+                return context.getText(textId);
+            }
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "Permission controller not found.", e);
+        }
+        return "";
+    }
+
+    @Override
     public int checkSignatures(String pkg1, String pkg2) {
         try {
             return mPM.checkSignatures(pkg1, pkg2);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index bd6baec..580382e 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -355,6 +355,12 @@
             in Bundle options, int userId);
     @UnsupportedAppUsage
     int stopUser(int userid, boolean force, in IStopUserCallback callback);
+    /**
+     * Check {@link com.android.server.am.ActivityManagerService#stopUserWithDelayedLocking(int, boolean, IStopUserCallback)}
+     * for details.
+     */
+    int stopUserWithDelayedLocking(int userid, boolean force, in IStopUserCallback callback);
+
     @UnsupportedAppUsage
     void registerUserSwitchObserver(in IUserSwitchObserver observer, in String name);
     void unregisterUserSwitchObserver(in IUserSwitchObserver observer);
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
index 8c3180b..80ba464 100644
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -39,7 +39,6 @@
     boolean injectInputEvent(in InputEvent event, boolean sync);
     void syncInputTransactions();
     boolean setRotation(int rotation);
-    Bitmap takeScreenshot(in Rect crop, int rotation);
     boolean clearWindowContentFrameStats(int windowId);
     WindowContentFrameStats getWindowContentFrameStats(int windowId);
     void clearWindowAnimationFrameStats();
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 775b1d1..70262b0 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -251,7 +251,7 @@
     }
 
     private AppComponentFactory createAppFactory(ApplicationInfo appInfo, ClassLoader cl) {
-        if (appInfo.appComponentFactory != null && cl != null) {
+        if (mIncludeCode && appInfo.appComponentFactory != null && cl != null) {
             try {
                 return (AppComponentFactory)
                         cl.loadClass(appInfo.appComponentFactory).newInstance();
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 18a3e6e..2579bd1 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -27,10 +27,7 @@
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Bitmap;
-import android.graphics.Point;
-import android.graphics.Rect;
 import android.graphics.Region;
-import android.hardware.display.DisplayManagerGlobal;
 import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -831,39 +828,20 @@
     }
 
     /**
-     * Takes a screenshot.
+     * Takes a screenshot of the default display and returns it by {@link Bitmap.Config#HARDWARE}
+     * format.
      *
      * @return The screenshot bitmap on success, null otherwise.
      */
     public Bitmap takeScreenshot() {
+        final int connectionId;
         synchronized (mLock) {
             throwIfNotConnectedLocked();
+            connectionId = mConnectionId;
         }
-        Display display = DisplayManagerGlobal.getInstance()
-                .getRealDisplay(Display.DEFAULT_DISPLAY);
-        Point displaySize = new Point();
-        display.getRealSize(displaySize);
-
-        int rotation = display.getRotation();
-
-        // Take the screenshot
-        Bitmap screenShot = null;
-        try {
-            // Calling out without a lock held.
-            screenShot = mUiAutomationConnection.takeScreenshot(
-                    new Rect(0, 0, displaySize.x, displaySize.y), rotation);
-            if (screenShot == null) {
-                return null;
-            }
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error while taking screnshot!", re);
-            return null;
-        }
-
-        // Optimization
-        screenShot.setHasAlpha(false);
-
-        return screenShot;
+        // Calling out without a lock held.
+        return AccessibilityInteractionClient.getInstance()
+                .takeScreenshot(connectionId, Display.DEFAULT_DISPLAY);
     }
 
     /**
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 82e9881..4fb9743 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -21,8 +21,6 @@
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Rect;
 import android.hardware.input.InputManager;
 import android.os.Binder;
 import android.os.IBinder;
@@ -180,23 +178,6 @@
     }
 
     @Override
-    public Bitmap takeScreenshot(Rect crop, int rotation) {
-        synchronized (mLock) {
-            throwIfCalledByNotTrustedUidLocked();
-            throwIfShutdownLocked();
-            throwIfNotConnectedLocked();
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            int width = crop.width();
-            int height = crop.height();
-            return SurfaceControl.screenshot(crop, width, height, rotation);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
     public boolean clearWindowContentFrameStats(int windowId) throws RemoteException {
         synchronized (mLock) {
             throwIfCalledByNotTrustedUidLocked();
@@ -449,7 +430,8 @@
         info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
                 | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
                 | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
-                | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS);
+                | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS
+                | AccessibilityServiceInfo.CAPABILITY_CAN_TAKE_SCREENSHOT);
         try {
             // Calling out with a lock held is fine since if the system
             // process is gone the client calling in will be killed.
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 2507991..8eee73c 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -568,8 +568,10 @@
      *
      * @see Configuration#isScreenWideColorGamut()
      * @return True if wcg should be enabled for this device.
+     * @hide
      */
-    private boolean shouldEnableWideColorGamut() {
+    @TestApi
+    public boolean shouldEnableWideColorGamut() {
         return mWcgEnabled;
     }
 
@@ -877,6 +879,7 @@
      * @see #FLAG_SYSTEM
      * @hide
      */
+    @TestApi
     @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
     public boolean wallpaperSupportsWcg(int which) {
         if (!shouldEnableWideColorGamut()) {
@@ -893,6 +896,8 @@
      *
      * @hide
      */
+    @TestApi
+    @Nullable
     @UnsupportedAppUsage
     public Bitmap getBitmap() {
         return getBitmap(false);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index caaa686..c584575 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -7418,7 +7418,9 @@
      * @param userHandle The user for whom to check the caller-id permission
      * @hide
      */
-    public boolean getBluetoothContactSharingDisabled(UserHandle userHandle) {
+    @SystemApi
+    @RequiresPermission(permission.INTERACT_ACROSS_USERS)
+    public boolean getBluetoothContactSharingDisabled(@NonNull UserHandle userHandle) {
         if (mService != null) {
             try {
                 return mService.getBluetoothContactSharingDisabledForUser(userHandle
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index c8f2ff3..567eb4a 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -85,6 +85,15 @@
     public static final int FLAG_NON_INCREMENTAL = 1 << 2;
 
     /**
+     * For key value backup, indicates that the backup contains no new data since the last backup
+     * attempt completed without any errors. The transport should use this to record that
+     * a successful backup attempt has been completed but no backup data has been changed.
+     *
+     * @see #performBackup(PackageInfo, ParcelFileDescriptor, int)
+     */
+    public static final int FLAG_DATA_NOT_CHANGED = 1 << 3;
+
+    /**
      * Used as a boolean extra in the binding intent of transports. We pass {@code true} to
      * notify transports that the current connection is used for registering the transport.
      */
@@ -302,7 +311,8 @@
      *   BackupService.doBackup() method.  This may be a pipe rather than a file on
      *   persistent media, so it may not be seekable.
      * @param flags a combination of {@link BackupTransport#FLAG_USER_INITIATED}, {@link
-     *   BackupTransport#FLAG_NON_INCREMENTAL}, {@link BackupTransport#FLAG_INCREMENTAL}, or 0.
+     *   BackupTransport#FLAG_NON_INCREMENTAL}, {@link BackupTransport#FLAG_INCREMENTAL},
+     *   {@link BackupTransport#FLAG_DATA_NOT_CHANGED},or 0.
      * @return one of {@link BackupTransport#TRANSPORT_OK} (OK so far),
      *  {@link BackupTransport#TRANSPORT_PACKAGE_REJECTED} (to suppress backup of this
      *  specific package, but allow others to proceed),
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 9ef9574..3860508 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1825,7 +1825,7 @@
      * @throws ActivityNotFoundException &nbsp;
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
     @SystemApi
     public void startActivityAsUser(@RequiresPermission @NonNull Intent intent,
             @NonNull UserHandle user) {
@@ -1873,7 +1873,7 @@
      * @throws ActivityNotFoundException &nbsp;
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
     @UnsupportedAppUsage
     public void startActivityAsUser(@RequiresPermission Intent intent, @Nullable Bundle options,
             UserHandle userId) {
@@ -1979,7 +1979,7 @@
      * @see #startActivities(Intent[])
      * @see PackageManager#resolveActivity
      */
-    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
     public int startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) {
         throw new RuntimeException("Not implemented. Must override in a subclass.");
     }
diff --git a/core/java/android/content/pm/InstallSourceInfo.java b/core/java/android/content/pm/InstallSourceInfo.java
index 4d235f1..c0fdcc9 100644
--- a/core/java/android/content/pm/InstallSourceInfo.java
+++ b/core/java/android/content/pm/InstallSourceInfo.java
@@ -29,32 +29,39 @@
 
     @Nullable private final String mInitiatingPackageName;
 
+    @Nullable private final SigningInfo mInitiatingPackageSigningInfo;
+
     @Nullable private final String mOriginatingPackageName;
 
     @Nullable private final String mInstallingPackageName;
 
     /** @hide */
     public InstallSourceInfo(@Nullable String initiatingPackageName,
+            @Nullable SigningInfo initiatingPackageSigningInfo,
             @Nullable String originatingPackageName, @Nullable String installingPackageName) {
-        this.mInitiatingPackageName = initiatingPackageName;
-        this.mOriginatingPackageName = originatingPackageName;
-        this.mInstallingPackageName = installingPackageName;
+        mInitiatingPackageName = initiatingPackageName;
+        mInitiatingPackageSigningInfo = initiatingPackageSigningInfo;
+        mOriginatingPackageName = originatingPackageName;
+        mInstallingPackageName = installingPackageName;
     }
 
     @Override
     public int describeContents() {
-        return 0;
+        return mInitiatingPackageSigningInfo == null
+                ? 0 : mInitiatingPackageSigningInfo.describeContents();
     }
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(mInitiatingPackageName);
+        dest.writeParcelable(mInitiatingPackageSigningInfo, flags);
         dest.writeString(mOriginatingPackageName);
         dest.writeString(mInstallingPackageName);
     }
 
     private InstallSourceInfo(Parcel source) {
         mInitiatingPackageName = source.readString();
+        mInitiatingPackageSigningInfo = source.readParcelable(SigningInfo.class.getClassLoader());
         mOriginatingPackageName = source.readString();
         mInstallingPackageName = source.readString();
     }
@@ -66,6 +73,14 @@
     }
 
     /**
+     * Information about the signing certificates used to sign the initiating package, if available.
+     */
+    @Nullable
+    public SigningInfo getInitiatingPackageSigningInfo() {
+        return mInitiatingPackageSigningInfo;
+    }
+
+    /**
      * The name of the package on behalf of which the initiating package requested the installation,
      * or null if not available.
      * <p>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4bfc40e..5ca50c2 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4389,6 +4389,18 @@
     public abstract boolean shouldShowRequestPermissionRationale(@NonNull String permName);
 
     /**
+     * Gets the string that is displayed on the button which corresponds to granting background
+     * location in settings. The intended use for this is to help apps instruct users how to
+     * grant a background permission by providing the string that users will see.
+     *
+     * @return the string shown on the button for granting background location
+     */
+    @NonNull
+    public CharSequence getBackgroundPermissionButtonLabel() {
+        return "";
+    }
+
+    /**
      * Returns an {@link android.content.Intent} suitable for passing to
      * {@link android.app.Activity#startActivityForResult(android.content.Intent, int)}
      * which prompts the user to grant permissions to this application.
@@ -6053,6 +6065,11 @@
      * If the calling application does not hold the INSTALL_PACKAGES permission then
      * the result will always return {@code null} from
      * {@link InstallSourceInfo#getOriginatingPackageName()}.
+     * <p>
+     * If the package that requested the install has been uninstalled, then information about it
+     * will only be returned from {@link InstallSourceInfo#getInitiatingPackageName()} and
+     * {@link InstallSourceInfo#getInitiatingPackageSigningInfo()} if the calling package is
+     * requesting its own install information and is not an instant app.
      *
      * @param packageName The name of the package to query
      * @throws NameNotFoundException if the given package name is not installed
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index 698876b..11cf2d6 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -18,6 +18,7 @@
 
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
+import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType;
 import android.os.CancellationSignal;
 import android.os.Parcelable;
 
@@ -119,6 +120,7 @@
     class AuthenticationResult {
         private Identifier mIdentifier;
         private CryptoObject mCryptoObject;
+        private @AuthenticationResultType int mAuthenticationType;
         private int mUserId;
 
         /**
@@ -129,27 +131,41 @@
         /**
          * Authentication result
          * @param crypto
+         * @param authenticationType
          * @param identifier
          * @param userId
          * @hide
          */
-        public AuthenticationResult(CryptoObject crypto, Identifier identifier,
+        public AuthenticationResult(CryptoObject crypto,
+                @AuthenticationResultType int authenticationType, Identifier identifier,
                 int userId) {
             mCryptoObject = crypto;
+            mAuthenticationType = authenticationType;
             mIdentifier = identifier;
             mUserId = userId;
         }
 
         /**
-         * Obtain the crypto object associated with this transaction
-         * @return crypto object provided to {@link BiometricAuthenticator#authenticate(
-         * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)}
+         * Provides the crypto object associated with this transaction.
+         * @return The crypto object provided to {@link BiometricPrompt#authenticate(
+         * BiometricPrompt.CryptoObject, CancellationSignal, Executor,
+         * BiometricPrompt.AuthenticationCallback)}
          */
         public CryptoObject getCryptoObject() {
             return mCryptoObject;
         }
 
         /**
+         * Provides the type of authentication (e.g. device credential or biometric) that was
+         * requested from and successfully provided by the user.
+         *
+         * @return An integer value representing the authentication method used.
+         */
+        public @AuthenticationResultType int getAuthenticationType() {
+            return mAuthenticationType;
+        }
+
+        /**
          * Obtain the biometric identifier associated with this operation. Applications are strongly
          * discouraged from associating specific identifiers with specific applications or
          * operations.
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index cb8fc8b..a695ce8 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -21,6 +21,7 @@
 import static android.hardware.biometrics.BiometricManager.Authenticators;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -40,6 +41,8 @@
 
 import com.android.internal.R;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.security.Signature;
 import java.util.concurrent.Executor;
 
@@ -397,9 +400,11 @@
             new IBiometricServiceReceiver.Stub() {
 
         @Override
-        public void onAuthenticationSucceeded() throws RemoteException {
+        public void onAuthenticationSucceeded(@AuthenticationResultType int authenticationType)
+                throws RemoteException {
             mExecutor.execute(() -> {
-                final AuthenticationResult result = new AuthenticationResult(mCryptoObject);
+                final AuthenticationResult result =
+                        new AuthenticationResult(mCryptoObject, authenticationType);
                 mAuthenticationCallback.onAuthenticationSucceeded(result);
             });
         }
@@ -576,28 +581,62 @@
     }
 
     /**
-     * Container for callback data from {@link #authenticate( CancellationSignal, Executor,
+     * Authentication type reported by {@link AuthenticationResult} when the user authenticated by
+     * entering their device PIN, pattern, or password.
+     */
+    public static final int AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL = 1;
+
+    /**
+     * Authentication type reported by {@link AuthenticationResult} when the user authenticated by
+     * presenting some form of biometric (e.g. fingerprint or face).
+     */
+    public static final int AUTHENTICATION_RESULT_TYPE_BIOMETRIC = 2;
+
+    /**
+     * An {@link IntDef} representing the type of auth, as reported by {@link AuthenticationResult}.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL, AUTHENTICATION_RESULT_TYPE_BIOMETRIC})
+    public @interface AuthenticationResultType {
+    }
+
+    /**
+     * Container for callback data from {@link #authenticate(CancellationSignal, Executor,
      * AuthenticationCallback)} and {@link #authenticate(CryptoObject, CancellationSignal, Executor,
-     * AuthenticationCallback)}
+     * AuthenticationCallback)}.
      */
     public static class AuthenticationResult extends BiometricAuthenticator.AuthenticationResult {
         /**
          * Authentication result
          * @param crypto
+         * @param authenticationType
          * @hide
          */
-        public AuthenticationResult(CryptoObject crypto) {
+        public AuthenticationResult(CryptoObject crypto,
+                @AuthenticationResultType int authenticationType) {
             // Identifier and userId is not used for BiometricPrompt.
-            super(crypto, null /* identifier */, 0 /* userId */);
+            super(crypto, authenticationType, null /* identifier */, 0 /* userId */);
         }
+
         /**
-         * Obtain the crypto object associated with this transaction
-         * @return crypto object provided to {@link #authenticate( CryptoObject, CancellationSignal,
-         * Executor, AuthenticationCallback)}
+         * Provides the crypto object associated with this transaction.
+         * @return The crypto object provided to {@link #authenticate(CryptoObject,
+         * CancellationSignal, Executor, AuthenticationCallback)}
          */
         public CryptoObject getCryptoObject() {
             return (CryptoObject) super.getCryptoObject();
         }
+
+        /**
+         * Provides the type of authentication (e.g. device credential or biometric) that was
+         * requested from and successfully provided by the user.
+         *
+         * @return An integer value representing the authentication method used.
+         */
+        public @AuthenticationResultType int getAuthenticationType() {
+            return super.getAuthenticationType();
+        }
     }
 
     /**
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
index c960049..1d43aa6 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
@@ -20,8 +20,8 @@
  * @hide
  */
 oneway interface IBiometricServiceReceiver {
-    // Notify BiometricPrompt that authentication was successful
-    void onAuthenticationSucceeded();
+    // Notify BiometricPrompt that authentication was successful.
+    void onAuthenticationSucceeded(int authenticationType);
     // Noties that authentication failed.
     void onAuthenticationFailed();
     // Notify BiometricPrompt that an error has occurred.
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index a5e0f04..456ebf2 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -557,9 +557,9 @@
                 dest.writeInt(vendorUuid.toString().length());
                 dest.writeString(vendorUuid.toString());
             }
+            dest.writeInt(version);
             dest.writeBlob(data);
             dest.writeTypedArray(keyphrases, flags);
-            dest.writeInt(version);
         }
 
         @Override
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index 7291419..03d29a3 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -370,6 +370,12 @@
         }
 
         @Override
+        public synchronized void onModuleDied() {
+            Message m = mHandler.obtainMessage(EVENT_SERVICE_DIED);
+            mHandler.sendMessage(m);
+        }
+
+        @Override
         public synchronized void binderDied() {
             Message m = mHandler.obtainMessage(EVENT_SERVICE_DIED);
             mHandler.sendMessage(m);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index c523f65..8ba3131 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -662,7 +662,7 @@
      * {@hide}
      */
     @Deprecated
-    @UnsupportedAppUsage
+    @SystemApi
     public static final int TYPE_WIFI_P2P    = 13;
 
     /**
@@ -3622,14 +3622,26 @@
     /**
      * Helper function to request a network with a particular legacy type.
      *
-     * This is temporarily public @hide so it can be called by system code that uses the
-     * NetworkRequest API to request networks but relies on CONNECTIVITY_ACTION broadcasts for
-     * instead network notifications.
+     * @deprecated This is temporarily public for tethering to backwards compatibility that uses
+     * the NetworkRequest API to request networks with legacy type and relies on
+     * CONNECTIVITY_ACTION broadcasts instead of NetworkCallbacks. New caller should use
+     * {@link #requestNetwork(NetworkRequest, NetworkCallback, Handler)} instead.
      *
      * TODO: update said system code to rely on NetworkCallbacks and make this method private.
+
+     * @param request {@link NetworkRequest} describing this request.
+     * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
+     *                        the callback must not be shared - it uniquely specifies this request.
+     * @param timeoutMs The time in milliseconds to attempt looking for a suitable network
+     *                  before {@link NetworkCallback#onUnavailable()} is called. The timeout must
+     *                  be a positive value (i.e. >0).
+     * @param legacyType to specify the network type(#TYPE_*).
+     * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
      *
      * @hide
      */
+    @SystemApi
+    @Deprecated
     public void requestNetwork(@NonNull NetworkRequest request,
             @NonNull NetworkCallback networkCallback, int timeoutMs, int legacyType,
             @NonNull Handler handler) {
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 385cb1d..72a6b39 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -57,9 +57,6 @@
     @UnsupportedAppUsage
     boolean getRestrictBackground();
 
-    /** Callback used to change internal state on tethering */
-    void onTetheringChanged(String iface, boolean tethering);
-
     /** Gets the restrict background status based on the caller's UID:
         1 - disabled
         2 - whitelisted
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 2792c56..be8e561 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -21,6 +21,8 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.net.util.LinkPropertiesUtils;
+import android.net.util.LinkPropertiesUtils.CompareResult;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -84,36 +86,6 @@
     /**
      * @hide
      */
-    public static class CompareResult<T> {
-        public final List<T> removed = new ArrayList<>();
-        public final List<T> added = new ArrayList<>();
-
-        public CompareResult() {}
-
-        public CompareResult(Collection<T> oldItems, Collection<T> newItems) {
-            if (oldItems != null) {
-                removed.addAll(oldItems);
-            }
-            if (newItems != null) {
-                for (T newItem : newItems) {
-                    if (!removed.remove(newItem)) {
-                        added.add(newItem);
-                    }
-                }
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "removed=[" + TextUtils.join(",", removed)
-                    + "] added=[" + TextUtils.join(",", added)
-                    + "]";
-        }
-    }
-
-    /**
-     * @hide
-     */
     @UnsupportedAppUsage(implicitMember =
             "values()[Landroid/net/LinkProperties$ProvisioningChange;")
     public enum ProvisioningChange {
@@ -1295,7 +1267,7 @@
      */
     @UnsupportedAppUsage
     public boolean isIdenticalInterfaceName(@NonNull LinkProperties target) {
-        return TextUtils.equals(getInterfaceName(), target.getInterfaceName());
+        return LinkPropertiesUtils.isIdenticalInterfaceName(target, this);
     }
 
     /**
@@ -1318,10 +1290,7 @@
      */
     @UnsupportedAppUsage
     public boolean isIdenticalAddresses(@NonNull LinkProperties target) {
-        Collection<InetAddress> targetAddresses = target.getAddresses();
-        Collection<InetAddress> sourceAddresses = getAddresses();
-        return (sourceAddresses.size() == targetAddresses.size()) ?
-                    sourceAddresses.containsAll(targetAddresses) : false;
+        return LinkPropertiesUtils.isIdenticalAddresses(target, this);
     }
 
     /**
@@ -1333,15 +1302,7 @@
      */
     @UnsupportedAppUsage
     public boolean isIdenticalDnses(@NonNull LinkProperties target) {
-        Collection<InetAddress> targetDnses = target.getDnsServers();
-        String targetDomains = target.getDomains();
-        if (mDomains == null) {
-            if (targetDomains != null) return false;
-        } else {
-            if (!mDomains.equals(targetDomains)) return false;
-        }
-        return (mDnses.size() == targetDnses.size()) ?
-                mDnses.containsAll(targetDnses) : false;
+        return LinkPropertiesUtils.isIdenticalDnses(target, this);
     }
 
     /**
@@ -1394,9 +1355,7 @@
      */
     @UnsupportedAppUsage
     public boolean isIdenticalRoutes(@NonNull LinkProperties target) {
-        Collection<RouteInfo> targetRoutes = target.getRoutes();
-        return (mRoutes.size() == targetRoutes.size()) ?
-                mRoutes.containsAll(targetRoutes) : false;
+        return LinkPropertiesUtils.isIdenticalRoutes(target, this);
     }
 
     /**
@@ -1408,8 +1367,7 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public boolean isIdenticalHttpProxy(@NonNull LinkProperties target) {
-        return getHttpProxy() == null ? target.getHttpProxy() == null :
-                getHttpProxy().equals(target.getHttpProxy());
+        return LinkPropertiesUtils.isIdenticalHttpProxy(target, this);
     }
 
     /**
@@ -1541,26 +1499,6 @@
     }
 
     /**
-     * Compares the addresses in this LinkProperties with another
-     * LinkProperties, examining only addresses on the base link.
-     *
-     * @param target a LinkProperties with the new list of addresses
-     * @return the differences between the addresses.
-     * @hide
-     */
-    public @NonNull CompareResult<LinkAddress> compareAddresses(@Nullable LinkProperties target) {
-        /*
-         * Duplicate the LinkAddresses into removed, we will be removing
-         * address which are common between mLinkAddresses and target
-         * leaving the addresses that are different. And address which
-         * are in target but not in mLinkAddresses are placed in the
-         * addedAddresses.
-         */
-        return new CompareResult<>(mLinkAddresses,
-                target != null ? target.getLinkAddresses() : null);
-    }
-
-    /**
      * Compares the DNS addresses in this LinkProperties with another
      * LinkProperties, examining only DNS addresses on the base link.
      *
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 74c9aac..0e10c42 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -20,11 +20,11 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.net.util.MacAddressUtils;
 import android.net.wifi.WifiInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import com.android.internal.util.BitUtils;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
@@ -33,7 +33,6 @@
 import java.net.UnknownHostException;
 import java.security.SecureRandom;
 import java.util.Arrays;
-import java.util.Random;
 
 /**
  * Representation of a MAC address.
@@ -109,21 +108,13 @@
         if (equals(BROADCAST_ADDRESS)) {
             return TYPE_BROADCAST;
         }
-        if (isMulticastAddress()) {
+        if ((mAddr & MULTICAST_MASK) != 0) {
             return TYPE_MULTICAST;
         }
         return TYPE_UNICAST;
     }
 
     /**
-     * @return true if this MacAddress is a multicast address.
-     * @hide
-     */
-    public boolean isMulticastAddress() {
-        return (mAddr & MULTICAST_MASK) != 0;
-    }
-
-    /**
      * @return true if this MacAddress is a locally assigned address.
      */
     public boolean isLocallyAssigned() {
@@ -192,7 +183,7 @@
      * @hide
      */
     public static boolean isMacAddress(byte[] addr) {
-        return addr != null && addr.length == ETHER_ADDR_LEN;
+        return MacAddressUtils.isMacAddress(addr);
     }
 
     /**
@@ -261,26 +252,11 @@
     }
 
     private static byte[] byteAddrFromLongAddr(long addr) {
-        byte[] bytes = new byte[ETHER_ADDR_LEN];
-        int index = ETHER_ADDR_LEN;
-        while (index-- > 0) {
-            bytes[index] = (byte) addr;
-            addr = addr >> 8;
-        }
-        return bytes;
+        return MacAddressUtils.byteAddrFromLongAddr(addr);
     }
 
     private static long longAddrFromByteAddr(byte[] addr) {
-        Preconditions.checkNotNull(addr);
-        if (!isMacAddress(addr)) {
-            throw new IllegalArgumentException(
-                    Arrays.toString(addr) + " was not a valid MAC address");
-        }
-        long longAddr = 0;
-        for (byte b : addr) {
-            longAddr = (longAddr << 8) + BitUtils.uint8(b);
-        }
-        return longAddr;
+        return MacAddressUtils.longAddrFromByteAddr(addr);
     }
 
     // Internal conversion function equivalent to longAddrFromByteAddr(byteAddrFromStringAddr(addr))
@@ -350,50 +326,7 @@
      * @hide
      */
     public static @NonNull MacAddress createRandomUnicastAddressWithGoogleBase() {
-        return createRandomUnicastAddress(BASE_GOOGLE_MAC, new SecureRandom());
-    }
-
-    /**
-     * Returns a generated MAC address whose 46 bits, excluding the locally assigned bit and the
-     * unicast bit, are randomly selected.
-     *
-     * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
-     *
-     * @return a random locally assigned, unicast MacAddress.
-     *
-     * @hide
-     */
-    public static @NonNull MacAddress createRandomUnicastAddress() {
-        return createRandomUnicastAddress(null, new SecureRandom());
-    }
-
-    /**
-     * Returns a randomly generated MAC address using the given Random object and the same
-     * OUI values as the given MacAddress.
-     *
-     * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
-     *
-     * @param base a base MacAddress whose OUI is used for generating the random address.
-     *             If base == null then the OUI will also be randomized.
-     * @param r a standard Java Random object used for generating the random address.
-     * @return a random locally assigned MacAddress.
-     *
-     * @hide
-     */
-    public static @NonNull MacAddress createRandomUnicastAddress(MacAddress base, Random r) {
-        long addr;
-        if (base == null) {
-            addr = r.nextLong() & VALID_LONG_MASK;
-        } else {
-            addr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong());
-        }
-        addr |= LOCALLY_ASSIGNED_MASK;
-        addr &= ~MULTICAST_MASK;
-        MacAddress mac = new MacAddress(addr);
-        if (mac.equals(DEFAULT_MAC_ADDRESS)) {
-            return createRandomUnicastAddress(base, r);
-        }
-        return mac;
+        return MacAddressUtils.createRandomUnicastAddress(BASE_GOOGLE_MAC, new SecureRandom());
     }
 
     // Convenience function for working around the lack of byte literals.
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index d286204..aae9fd4 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -17,6 +17,8 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Build;
@@ -43,9 +45,11 @@
  *
  * @hide
  */
+@SystemApi
 public abstract class NetworkAgent {
-    // Guaranteed to be non-null, otherwise registerNetworkAgent() would have thrown
-    // an exception. Be careful in tests when mocking though.
+    /**
+     * The {@link Network} corresponding to this object.
+     */
     @NonNull
     public final Network network;
 
@@ -58,9 +62,14 @@
     private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>();
     private volatile long mLastBwRefreshTime = 0;
     private static final long BW_REFRESH_MIN_WIN_MS = 500;
-    private boolean mPollLceScheduled = false;
-    private AtomicBoolean mPollLcePending = new AtomicBoolean(false);
-    public final int mProviderId;
+    private boolean mBandwidthUpdateScheduled = false;
+    private AtomicBoolean mBandwidthUpdatePending = new AtomicBoolean(false);
+
+    /**
+     * The ID of the {@link NetworkProvider} that created this object, or
+     * {@link NetworkProvider#ID_NONE} if unknown.
+     */
+    public final int providerId;
 
     private static final int BASE = Protocol.BASE_NETWORK_AGENT;
 
@@ -68,6 +77,7 @@
      * Sent by ConnectivityService to the NetworkAgent to inform it of
      * suspected connectivity problems on its network.  The NetworkAgent
      * should take steps to verify and correct connectivity.
+     * @hide
      */
     public static final int CMD_SUSPECT_BAD = BASE;
 
@@ -76,6 +86,7 @@
      * ConnectivityService to pass the current NetworkInfo (connection state).
      * Sent when the NetworkInfo changes, mainly due to change of state.
      * obj = NetworkInfo
+     * @hide
      */
     public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 1;
 
@@ -83,6 +94,7 @@
      * Sent by the NetworkAgent to ConnectivityService to pass the current
      * NetworkCapabilties.
      * obj = NetworkCapabilities
+     * @hide
      */
     public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 2;
 
@@ -90,11 +102,14 @@
      * Sent by the NetworkAgent to ConnectivityService to pass the current
      * NetworkProperties.
      * obj = NetworkProperties
+     * @hide
      */
     public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 3;
 
-    /* centralize place where base network score, and network score scaling, will be
+    /**
+     * Centralize the place where base network score, and network score scaling, will be
      * stored, so as we can consistently compare apple and oranges, or wifi, ethernet and LTE
+     * @hide
      */
     public static final int WIFI_BASE_SCORE = 60;
 
@@ -102,6 +117,7 @@
      * Sent by the NetworkAgent to ConnectivityService to pass the current
      * network score.
      * obj = network score Integer
+     * @hide
      */
     public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4;
 
@@ -114,12 +130,33 @@
      * obj = Bundle containing map from {@code REDIRECT_URL_KEY} to {@code String}
      *       representing URL that Internet probe was redirect to, if it was redirected,
      *       or mapping to {@code null} otherwise.
+     * @hide
      */
     public static final int CMD_REPORT_NETWORK_STATUS = BASE + 7;
 
+
+    /**
+     * Network validation suceeded.
+     * Corresponds to {@link NetworkCapabilities.NET_CAPABILITY_VALIDATED}.
+     */
+    public static final int VALIDATION_STATUS_VALID = 1;
+
+    /**
+     * Network validation was attempted and failed. This may be received more than once as
+     * subsequent validation attempts are made.
+     */
+    public static final int VALIDATION_STATUS_NOT_VALID = 2;
+
+    // TODO: remove.
+    /** @hide */
     public static final int VALID_NETWORK = 1;
+    /** @hide */
     public static final int INVALID_NETWORK = 2;
 
+    /**
+     * The key for the redirect URL in the Bundle argument of {@code CMD_REPORT_NETWORK_STATUS}.
+     * @hide
+     */
     public static String REDIRECT_URL_KEY = "redirect URL";
 
      /**
@@ -128,6 +165,7 @@
      * CONNECTED so it can be given special treatment at that time.
      *
      * obj = boolean indicating whether to use this network even if unvalidated
+     * @hide
      */
     public static final int EVENT_SET_EXPLICITLY_SELECTED = BASE + 8;
 
@@ -138,12 +176,14 @@
      * responsibility to remember it.
      *
      * arg1 = 1 if true, 0 if false
+     * @hide
      */
     public static final int CMD_SAVE_ACCEPT_UNVALIDATED = BASE + 9;
 
     /**
      * Sent by ConnectivityService to the NetworkAgent to inform the agent to pull
      * the underlying network connection for updated bandwidth information.
+     * @hide
      */
     public static final int CMD_REQUEST_BANDWIDTH_UPDATE = BASE + 10;
 
@@ -156,6 +196,7 @@
      *   obj = KeepalivePacketData object describing the data to be sent
      *
      * Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
+     * @hide
      */
     public static final int CMD_START_SOCKET_KEEPALIVE = BASE + 11;
 
@@ -165,6 +206,7 @@
      * arg1 = slot number of the keepalive to stop.
      *
      * Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
+     * @hide
      */
     public static final int CMD_STOP_SOCKET_KEEPALIVE = BASE + 12;
 
@@ -178,6 +220,7 @@
      *
      * arg1 = slot number of the keepalive
      * arg2 = error code
+     * @hide
      */
     public static final int EVENT_SOCKET_KEEPALIVE = BASE + 13;
 
@@ -186,6 +229,7 @@
      * that when crossed should trigger a system wakeup and a NetworkCapabilities update.
      *
      *   obj = int[] describing signal strength thresholds.
+     * @hide
      */
     public static final int CMD_SET_SIGNAL_STRENGTH_THRESHOLDS = BASE + 14;
 
@@ -193,6 +237,7 @@
      * Sent by ConnectivityService to the NeworkAgent to inform the agent to avoid
      * automatically reconnecting to this network (e.g. via autojoin).  Happens
      * when user selects "No" option on the "Stay connected?" dialog box.
+     * @hide
      */
     public static final int CMD_PREVENT_AUTOMATIC_RECONNECT = BASE + 15;
 
@@ -205,6 +250,7 @@
      * This does not happen with UDP, so this message is TCP-specific.
      * arg1 = slot number of the keepalive to filter for.
      * obj = the keepalive packet to send repeatedly.
+     * @hide
      */
     public static final int CMD_ADD_KEEPALIVE_PACKET_FILTER = BASE + 16;
 
@@ -212,6 +258,7 @@
      * Sent by the KeepaliveTracker to NetworkAgent to remove a packet filter. See
      * {@link #CMD_ADD_KEEPALIVE_PACKET_FILTER}.
      * arg1 = slot number of the keepalive packet filter to remove.
+     * @hide
      */
     public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17;
 
@@ -219,27 +266,32 @@
     // of dependent changes that would conflict throughout the automerger graph. Having these
     // temporarily helps with the process of going through with all these dependent changes across
     // the entire tree.
+    /** @hide TODO: decide which of these to expose. */
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score) {
         this(looper, context, logTag, ni, nc, lp, score, null, NetworkProvider.ID_NONE);
     }
+
+    /** @hide TODO: decide which of these to expose. */
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config) {
         this(looper, context, logTag, ni, nc, lp, score, config, NetworkProvider.ID_NONE);
     }
 
+    /** @hide TODO: decide which of these to expose. */
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score, int providerId) {
         this(looper, context, logTag, ni, nc, lp, score, null, providerId);
     }
 
+    /** @hide TODO: decide which of these to expose. */
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config,
             int providerId) {
         mHandler = new NetworkAgentHandler(looper);
         LOG_TAG = logTag;
         mContext = context;
-        mProviderId = providerId;
+        this.providerId = providerId;
         if (ni == null || nc == null || lp == null) {
             throw new IllegalArgumentException();
         }
@@ -287,7 +339,7 @@
                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
                     if (DBG) log("NetworkAgent channel lost");
                     // let the client know CS is done with us.
-                    unwanted();
+                    onNetworkUnwanted();
                     synchronized (mPreConnectedQueue) {
                         mAsyncChannel = null;
                     }
@@ -303,16 +355,16 @@
                         log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
                     }
                     if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
-                        mPollLceScheduled = false;
-                        if (!mPollLcePending.getAndSet(true)) {
-                            pollLceData();
+                        mBandwidthUpdateScheduled = false;
+                        if (!mBandwidthUpdatePending.getAndSet(true)) {
+                            onBandwidthUpdateRequested();
                         }
                     } else {
                         // deliver the request at a later time rather than discard it completely.
-                        if (!mPollLceScheduled) {
+                        if (!mBandwidthUpdateScheduled) {
                             long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS
                                     - currentTimeMs + 1;
-                            mPollLceScheduled = sendEmptyMessageDelayed(
+                            mBandwidthUpdateScheduled = sendEmptyMessageDelayed(
                                     CMD_REQUEST_BANDWIDTH_UPDATE, waitTime);
                         }
                     }
@@ -325,19 +377,20 @@
                                 + (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ")
                                 + redirectUrl);
                     }
-                    networkStatus(msg.arg1, redirectUrl);
+                    onValidationStatus(msg.arg1 /* status */, redirectUrl);
                     break;
                 }
                 case CMD_SAVE_ACCEPT_UNVALIDATED: {
-                    saveAcceptUnvalidated(msg.arg1 != 0);
+                    onSaveAcceptUnvalidated(msg.arg1 != 0);
                     break;
                 }
                 case CMD_START_SOCKET_KEEPALIVE: {
-                    startSocketKeepalive(msg);
+                    onStartSocketKeepalive(msg.arg1 /* slot */, msg.arg2 /* interval */,
+                            (KeepalivePacketData) msg.obj /* packet */);
                     break;
                 }
                 case CMD_STOP_SOCKET_KEEPALIVE: {
-                    stopSocketKeepalive(msg);
+                    onStopSocketKeepalive(msg.arg1 /* slot */);
                     break;
                 }
 
@@ -350,19 +403,20 @@
                     for (int i = 0; i < intThresholds.length; i++) {
                         intThresholds[i] = thresholds.get(i);
                     }
-                    setSignalStrengthThresholds(intThresholds);
+                    onSignalStrengthThresholdsUpdated(intThresholds);
                     break;
                 }
                 case CMD_PREVENT_AUTOMATIC_RECONNECT: {
-                    preventAutomaticReconnect();
+                    onAutomaticReconnectDisabled();
                     break;
                 }
                 case CMD_ADD_KEEPALIVE_PACKET_FILTER: {
-                    addKeepalivePacketFilter(msg);
+                    onAddKeepalivePacketFilter(msg.arg1 /* slot */,
+                            (KeepalivePacketData) msg.obj /* packet */);
                     break;
                 }
                 case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: {
-                    removeKeepalivePacketFilter(msg);
+                    onRemoveKeepalivePacketFilter(msg.arg1 /* slot */);
                     break;
                 }
             }
@@ -397,14 +451,16 @@
     }
 
     /**
-     * Called by the bearer code when it has new LinkProperties data.
+     * Must be called by the agent when the network's {@link LinkProperties} change.
+     * @param linkProperties the new LinkProperties.
      */
-    public void sendLinkProperties(LinkProperties linkProperties) {
+    public void sendLinkProperties(@NonNull LinkProperties linkProperties) {
         queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties));
     }
 
     /**
-     * Called by the bearer code when it has new NetworkInfo data.
+     * Must be called by the agent when it has a new NetworkInfo object.
+     * @hide TODO: expose something better.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void sendNetworkInfo(NetworkInfo networkInfo) {
@@ -412,17 +468,19 @@
     }
 
     /**
-     * Called by the bearer code when it has new NetworkCapabilities data.
+     * Must be called by the agent when the network's {@link NetworkCapabilities} change.
+     * @param networkCapabilities the new NetworkCapabilities.
      */
-    public void sendNetworkCapabilities(NetworkCapabilities networkCapabilities) {
-        mPollLcePending.set(false);
+    public void sendNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
+        mBandwidthUpdatePending.set(false);
         mLastBwRefreshTime = System.currentTimeMillis();
         queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED,
                 new NetworkCapabilities(networkCapabilities));
     }
 
     /**
-     * Called by the bearer code when it has a new score for this network.
+     * Must be called by the agent to update the score of this network.
+     * @param score the new score.
      */
     public void sendNetworkScore(int score) {
         if (score < 0) {
@@ -434,14 +492,16 @@
     }
 
     /**
-     * Called by the bearer code when it has a new NetworkScore for this network.
+     * Must be called by the agent when it has a new {@link NetworkScore} for this network.
+     * @param ns the new score.
+     * @hide TODO: unhide the NetworkScore class, and rename to sendNetworkScore.
      */
     public void updateScore(@NonNull NetworkScore ns) {
         queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, new NetworkScore(ns));
     }
 
     /**
-     * Called by the bearer to indicate this network was manually selected by the user.
+     * Must be called by the agent to indicate this network was manually selected by the user.
      * This should be called before the NetworkInfo is marked CONNECTED so that this
      * Network can be given special treatment at that time. If {@code acceptUnvalidated} is
      * {@code true}, then the system will switch to this network. If it is {@code false} and the
@@ -450,15 +510,16 @@
      * {@link #saveAcceptUnvalidated} to persist the user's choice. Thus, if the transport ever
      * calls this method with {@code acceptUnvalidated} set to {@code false}, it must also implement
      * {@link #saveAcceptUnvalidated} to respect the user's choice.
+     * @hide should move to NetworkAgentConfig.
      */
     public void explicitlySelected(boolean acceptUnvalidated) {
         explicitlySelected(true /* explicitlySelected */, acceptUnvalidated);
     }
 
     /**
-     * Called by the bearer to indicate whether the network was manually selected by the user.
-     * This should be called before the NetworkInfo is marked CONNECTED so that this
-     * Network can be given special treatment at that time.
+     * Must be called by the agent to indicate whether the network was manually selected by the
+     * user. This should be called before the network becomes connected, so it can be given
+     * special treatment when it does.
      *
      * If {@code explicitlySelected} is {@code true}, and {@code acceptUnvalidated} is {@code true},
      * then the system will switch to this network. If {@code explicitlySelected} is {@code true}
@@ -473,6 +534,7 @@
      * {@code true}, the system will interpret this as the user having accepted partial connectivity
      * on this network. Thus, the system will switch to the network and consider it validated even
      * if it only provides partial connectivity, but the network is not otherwise treated specially.
+     * @hide should move to NetworkAgentConfig.
      */
     public void explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated) {
         queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED,
@@ -486,73 +548,126 @@
      * as well, either canceling NetworkRequests or altering their score such that this
      * network won't be immediately requested again.
      */
-    abstract protected void unwanted();
+    public void onNetworkUnwanted() {
+        unwanted();
+    }
+    /** @hide TODO delete once subclasses have moved to onNetworkUnwanted. */
+    protected void unwanted() {
+    }
 
     /**
      * Called when ConnectivityService request a bandwidth update. The parent factory
      * shall try to overwrite this method and produce a bandwidth update if capable.
      */
+    public void onBandwidthUpdateRequested() {
+        pollLceData();
+    }
+    /** @hide TODO delete once subclasses have moved to onBandwidthUpdateRequested. */
     protected void pollLceData() {
     }
 
     /**
      * Called when the system determines the usefulness of this network.
      *
-     * Networks claiming internet connectivity will have their internet
-     * connectivity verified.
+     * The system attempts to validate Internet connectivity on networks that provide the
+     * {@link NetworkCapabilities#NET_CAPABILITY_INTERNET} capability.
      *
      * Currently there are two possible values:
-     * {@code VALID_NETWORK} if the system is happy with the connection,
-     * {@code INVALID_NETWORK} if the system is not happy.
-     * TODO - add indications of captive portal-ness and related success/failure,
-     * ie, CAPTIVE_SUCCESS_NETWORK, CAPTIVE_NETWORK for successful login and detection
+     * {@code VALIDATION_STATUS_VALID} if Internet connectivity was validated,
+     * {@code VALIDATION_STATUS_NOT_VALID} if Internet connectivity was not validated.
      *
-     * This may be called multiple times as the network status changes and may
-     * generate false negatives if we lose ip connectivity before the link is torn down.
+     * This may be called multiple times as network status changes, or if there are multiple
+     * subsequent attempts to validate connectivity that fail.
      *
-     * @param status one of {@code VALID_NETWORK} or {@code INVALID_NETWORK}.
-     * @param redirectUrl If the Internet probe was redirected, this is the destination it was
-     *         redirected to, otherwise {@code null}.
+     * @param status one of {@code VALIDATION_STATUS_VALID} or {@code VALIDATION_STATUS_NOT_VALID}.
+     * @param redirectUrl If Internet connectivity is being redirected (e.g., on a captive portal),
+     *        this is the destination the probes are being redirected to, otherwise {@code null}.
      */
+    public void onValidationStatus(int status, @Nullable String redirectUrl) {
+        networkStatus(status, redirectUrl);
+    }
+    /** @hide TODO delete once subclasses have moved to onValidationStatus */
     protected void networkStatus(int status, String redirectUrl) {
     }
 
+
     /**
      * Called when the user asks to remember the choice to use this network even if unvalidated.
      * The transport is responsible for remembering the choice, and the next time the user connects
      * to the network, should explicitlySelected with {@code acceptUnvalidated} set to {@code true}.
      * This method will only be called if {@link #explicitlySelected} was called with
      * {@code acceptUnvalidated} set to {@code false}.
+     * @param accept whether the user wants to use the network even if unvalidated.
      */
+    public void onSaveAcceptUnvalidated(boolean accept) {
+        saveAcceptUnvalidated(accept);
+    }
+    /** @hide TODO delete once subclasses have moved to onSaveAcceptUnvalidated */
     protected void saveAcceptUnvalidated(boolean accept) {
     }
 
     /**
      * Requests that the network hardware send the specified packet at the specified interval.
+     *
+     * @param slot the hardware slot on which to start the keepalive.
+     * @param intervalSeconds the interval between packets
+     * @param packet the packet to send.
      */
+    public void onStartSocketKeepalive(int slot, int intervalSeconds,
+            @NonNull KeepalivePacketData packet) {
+        Message msg = mHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, slot, intervalSeconds,
+                packet);
+        startSocketKeepalive(msg);
+        msg.recycle();
+    }
+    /** @hide TODO delete once subclasses have moved to onStartSocketKeepalive */
     protected void startSocketKeepalive(Message msg) {
         onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED);
     }
 
     /**
-     * Requests that the network hardware send the specified packet at the specified interval.
+     * Requests that the network hardware stop a previously-started keepalive.
+     *
+     * @param slot the hardware slot on which to stop the keepalive.
      */
+    public void onStopSocketKeepalive(int slot) {
+        Message msg = mHandler.obtainMessage(CMD_STOP_SOCKET_KEEPALIVE, slot, 0, null);
+        stopSocketKeepalive(msg);
+        msg.recycle();
+    }
+    /** @hide TODO delete once subclasses have moved to onStopSocketKeepalive */
     protected void stopSocketKeepalive(Message msg) {
         onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED);
     }
 
     /**
-     * Called by the network when a socket keepalive event occurs.
+     * Must be called by the agent when a socket keepalive event occurs.
+     *
+     * @param slot the hardware slot on which the event occurred.
+     * @param event the event that occurred.
      */
+    public void sendSocketKeepaliveEvent(int slot, int event) {
+        queueOrSendMessage(EVENT_SOCKET_KEEPALIVE, slot, event);
+    }
+    /** @hide TODO delete once callers have moved to sendSocketKeepaliveEvent */
     public void onSocketKeepaliveEvent(int slot, int reason) {
-        queueOrSendMessage(EVENT_SOCKET_KEEPALIVE, slot, reason);
+        sendSocketKeepaliveEvent(slot, reason);
     }
 
     /**
      * Called by ConnectivityService to add specific packet filter to network hardware to block
-     * ACKs matching the sent keepalive packets. Implementations that support this feature must
-     * override this method.
+     * replies (e.g., TCP ACKs) matching the sent keepalive packets. Implementations that support
+     * this feature must override this method.
+     *
+     * @param slot the hardware slot on which the keepalive should be sent.
+     * @param packet the packet that is being sent.
      */
+    public void onAddKeepalivePacketFilter(int slot, @NonNull KeepalivePacketData packet) {
+        Message msg = mHandler.obtainMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0, packet);
+        addKeepalivePacketFilter(msg);
+        msg.recycle();
+    }
+    /** @hide TODO delete once subclasses have moved to onAddKeepalivePacketFilter */
     protected void addKeepalivePacketFilter(Message msg) {
     }
 
@@ -560,14 +675,28 @@
      * Called by ConnectivityService to remove a packet filter installed with
      * {@link #addKeepalivePacketFilter(Message)}. Implementations that support this feature
      * must override this method.
+     *
+     * @param slot the hardware slot on which the keepalive is being sent.
      */
+    public void onRemoveKeepalivePacketFilter(int slot) {
+        Message msg = mHandler.obtainMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER, slot, 0, null);
+        removeKeepalivePacketFilter(msg);
+        msg.recycle();
+    }
+    /** @hide TODO delete once subclasses have moved to onRemoveKeepalivePacketFilter */
     protected void removeKeepalivePacketFilter(Message msg) {
     }
 
     /**
      * Called by ConnectivityService to inform this network transport of signal strength thresholds
      * that when crossed should trigger a system wakeup and a NetworkCapabilities update.
+     *
+     * @param thresholds the array of thresholds that should trigger wakeups.
      */
+    public void onSignalStrengthThresholdsUpdated(@NonNull int[] thresholds) {
+        setSignalStrengthThresholds(thresholds);
+    }
+    /** @hide TODO delete once subclasses have moved to onSetSignalStrengthThresholds */
     protected void setSignalStrengthThresholds(int[] thresholds) {
     }
 
@@ -577,9 +706,14 @@
      * responsible for making sure the device does not automatically reconnect to the same network
      * after the {@code unwanted} call.
      */
+    public void onAutomaticReconnectDisabled() {
+        preventAutomaticReconnect();
+    }
+    /** @hide TODO delete once subclasses have moved to onAutomaticReconnectDisabled */
     protected void preventAutomaticReconnect() {
     }
 
+    /** @hide */
     protected void log(String s) {
         Log.d(LOG_TAG, "NetworkAgent: " + s);
     }
diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java
index 824ddb8..e271037 100644
--- a/core/java/android/net/NetworkFactory.java
+++ b/core/java/android/net/NetworkFactory.java
@@ -115,13 +115,6 @@
      */
     private static final int CMD_SET_FILTER = BASE + 3;
 
-    /**
-     * Sent by NetworkFactory to ConnectivityService to indicate that a request is
-     * unfulfillable.
-     * @see #releaseRequestAsUnfulfillableByAnyFactory(NetworkRequest).
-     */
-    public static final int EVENT_UNFULFILLABLE_REQUEST = BASE + 4;
-
     private final Context mContext;
     private final ArrayList<Message> mPreConnectedQueue = new ArrayList<Message>();
     private final String LOG_TAG;
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 9731f3c..301d203 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -300,22 +300,34 @@
          * this without a single transport set will generate an exception, as will
          * subsequently adding or removing transports after this is set.
          * </p>
-         * The interpretation of this {@code String} is bearer specific and bearers that use
-         * it should document their particulars.  For example, Bluetooth may use some sort of
-         * device id while WiFi could used ssid and/or bssid.  Cellular may use carrier spn.
+         * If the {@code networkSpecifier} is provided, it shall be interpreted as follows:
+         * <ul>
+         * <li>If the specifier can be parsed as an integer, it will be treated as a
+         * {@link android.net TelephonyNetworkSpecifier}, and the provided integer will be
+         * interpreted as a SubscriptionId.
+         * <li>If the value is an ethernet interface name, it will be treated as such.
+         * <li>For all other cases, the behavior is undefined.
+         * </ul>
          *
-         * @param networkSpecifier An {@code String} of opaque format used to specify the bearer
-         *                         specific network specifier where the bearer has a choice of
-         *                         networks.
+         * @param networkSpecifier A {@code String} of either a SubscriptionId in cellular
+         *                         network request or an ethernet interface name in ethernet
+         *                         network request.
+         *
+         * @deprecated Use {@link #setNetworkSpecifier(NetworkSpecifier)} instead.
          */
+        @Deprecated
         public Builder setNetworkSpecifier(String networkSpecifier) {
-            /*
-             * A StringNetworkSpecifier does not accept null or empty ("") strings. When network
-             * specifiers were strings a null string and an empty string were considered equivalent.
-             * Hence no meaning is attached to a null or empty ("") string.
-             */
-            return setNetworkSpecifier(TextUtils.isEmpty(networkSpecifier) ? null
-                    : new StringNetworkSpecifier(networkSpecifier));
+            try {
+                int subId = Integer.parseInt(networkSpecifier);
+                return setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
+                        .setSubscriptionId(subId).build());
+            } catch (NumberFormatException nfe) {
+                // A StringNetworkSpecifier does not accept null or empty ("") strings. When network
+                // specifiers were strings a null string and an empty string were considered
+                // equivalent. Hence no meaning is attached to a null or empty ("") string.
+                return setNetworkSpecifier(TextUtils.isEmpty(networkSpecifier) ? null
+                        : new StringNetworkSpecifier(networkSpecifier));
+            }
         }
 
         /**
@@ -455,6 +467,19 @@
     }
 
     /**
+     * Returns true iff. the capabilities requested in this NetworkRequest are satisfied by the
+     * provided {@link NetworkCapabilities}.
+     *
+     * @param nc Capabilities that should satisfy this NetworkRequest. null capabilities do not
+     *           satisfy any request.
+     * @hide
+     */
+    @SystemApi
+    public boolean satisfiedBy(@Nullable NetworkCapabilities nc) {
+        return networkCapabilities.satisfiedByNetworkCapabilities(nc);
+    }
+
+    /**
      * @see Builder#addTransportType(int)
      */
     public boolean hasTransport(@Transport int transportType) {
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 428a4ea..c233ec0 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -403,7 +403,7 @@
      * @throws SecurityException if the caller does not hold the
      *         {@link permission#REQUEST_NETWORK_SCORES} permission.
      * @throws IllegalArgumentException if a score cache is already registered for this type.
-     * @deprecated equivalent to registering for cache updates with CACHE_FILTER_NONE.
+     * @deprecated equivalent to registering for cache updates with {@link #SCORE_FILTER_NONE}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES)
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 08cc4e2..779f7bc 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -31,7 +31,6 @@
 import java.io.FileDescriptor;
 import java.math.BigInteger;
 import java.net.Inet4Address;
-import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.SocketException;
 import java.net.UnknownHostException;
@@ -313,15 +312,6 @@
     }
 
     /**
-     * Check if IP address type is consistent between two InetAddress.
-     * @return true if both are the same type.  False otherwise.
-     */
-    public static boolean addressTypeMatches(InetAddress left, InetAddress right) {
-        return (((left instanceof Inet4Address) && (right instanceof Inet4Address)) ||
-                ((left instanceof Inet6Address) && (right instanceof Inet6Address)));
-    }
-
-    /**
      * Convert a 32 char hex string into a Inet6Address.
      * throws a runtime exception if the string isn't 32 chars, isn't hex or can't be
      * made into an Inet6Address
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index ea6002c..e088094 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -22,6 +22,7 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.net.util.NetUtils;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -441,21 +442,7 @@
     @UnsupportedAppUsage
     @Nullable
     public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
-        if ((routes == null) || (dest == null)) return null;
-
-        RouteInfo bestRoute = null;
-        // pick a longest prefix match under same address type
-        for (RouteInfo route : routes) {
-            if (NetworkUtils.addressTypeMatches(route.mDestination.getAddress(), dest)) {
-                if ((bestRoute != null) &&
-                        (bestRoute.mDestination.getPrefixLength() >=
-                        route.mDestination.getPrefixLength())) {
-                    continue;
-                }
-                if (route.matches(dest)) bestRoute = route;
-            }
-        }
-        return bestRoute;
+        return NetUtils.selectBestRoute(routes, dest);
     }
 
     /**
diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java
index fb224fb..fc9a8f6 100644
--- a/core/java/android/net/SocketKeepalive.java
+++ b/core/java/android/net/SocketKeepalive.java
@@ -20,6 +20,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.os.Binder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -53,7 +54,11 @@
 public abstract class SocketKeepalive implements AutoCloseable {
     static final String TAG = "SocketKeepalive";
 
-    /** @hide */
+    /**
+     * No errors.
+     * @hide
+     */
+    @SystemApi
     public static final int SUCCESS = 0;
 
     /** @hide */
diff --git a/core/java/android/net/TelephonyNetworkSpecifier.java b/core/java/android/net/TelephonyNetworkSpecifier.java
new file mode 100644
index 0000000..726f770
--- /dev/null
+++ b/core/java/android/net/TelephonyNetworkSpecifier.java
@@ -0,0 +1,146 @@
+/*
+ * 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.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * NetworkSpecifier object for cellular network request. Apps should use the
+ * {@link TelephonyNetworkSpecifier.Builder} class to create an instance.
+ */
+public final class TelephonyNetworkSpecifier extends NetworkSpecifier implements Parcelable {
+
+    private final int mSubId;
+
+    /**
+     * Return the subscription Id of current TelephonyNetworkSpecifier object.
+     *
+     * @return The subscription id.
+     */
+    public int getSubscriptionId() {
+        return mSubId;
+    }
+
+    /**
+     * @hide
+     */
+    public TelephonyNetworkSpecifier(int subId) {
+        this.mSubId = subId;
+    }
+
+    public static final @NonNull Creator<TelephonyNetworkSpecifier> CREATOR =
+            new Creator<TelephonyNetworkSpecifier>() {
+                @Override
+                public TelephonyNetworkSpecifier createFromParcel(Parcel in) {
+                    int subId = in.readInt();
+                    return new TelephonyNetworkSpecifier(subId);
+                }
+
+                @Override
+                public TelephonyNetworkSpecifier[] newArray(int size) {
+                    return new TelephonyNetworkSpecifier[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mSubId);
+    }
+
+    @Override
+    public int hashCode() {
+        return mSubId;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof TelephonyNetworkSpecifier)) {
+            return false;
+        }
+
+        TelephonyNetworkSpecifier lhs = (TelephonyNetworkSpecifier) obj;
+        return mSubId == lhs.mSubId;
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder()
+                .append("TelephonyNetworkSpecifier [")
+                .append("mSubId = ").append(mSubId)
+                .append("]")
+                .toString();
+    }
+
+    /** @hide */
+    @Override
+    public boolean satisfiedBy(NetworkSpecifier other) {
+        // Any generic requests should be satisfied by a specific telephony network.
+        // For simplicity, we treat null same as MatchAllNetworkSpecifier
+        return equals(other) || other == null || other instanceof MatchAllNetworkSpecifier;
+    }
+
+
+    /**
+     * Builder to create {@link TelephonyNetworkSpecifier} object.
+     */
+    public static final class Builder {
+        // Integer.MIN_VALUE which is not a valid subId, services as the sentinel to check if
+        // subId was set
+        private static final int SENTINEL_SUB_ID = Integer.MIN_VALUE;
+
+        private int mSubId;
+
+        public Builder() {
+            mSubId = SENTINEL_SUB_ID;
+        }
+
+        /**
+         * Set the subscription id.
+         *
+         * @param subId The subscription Id.
+         * @return Instance of {@link Builder} to enable the chaining of the builder method.
+         */
+        public @NonNull Builder setSubscriptionId(int subId) {
+            mSubId = subId;
+            return this;
+        }
+
+        /**
+         * Create a NetworkSpecifier for the cellular network request.
+         *
+         * @return TelephonyNetworkSpecifier object.
+         * @throws IllegalArgumentException when subscription Id is not provided through
+         *         {@link #setSubscriptionId(int)}.
+         */
+        public @NonNull TelephonyNetworkSpecifier build() {
+            if (mSubId == SENTINEL_SUB_ID) {
+                throw new IllegalArgumentException("Subscription Id is not provided.");
+            }
+            return new TelephonyNetworkSpecifier(mSubId);
+        }
+    }
+}
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl
index 7b2d148..416d692 100644
--- a/core/java/android/os/IVibratorService.aidl
+++ b/core/java/android/os/IVibratorService.aidl
@@ -24,7 +24,7 @@
 {
     boolean hasVibrator();
     boolean hasAmplitudeControl();
-    boolean setAlwaysOnEffect(int id, in VibrationEffect effect,
+    boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, in VibrationEffect effect,
             in VibrationAttributes attributes);
     void vibrate(int uid, String opPkg, in VibrationEffect effect,
             in VibrationAttributes attributes, String reason, IBinder token);
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index c1542c7..8050454 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -70,14 +70,15 @@
     }
 
     @Override
-    public boolean setAlwaysOnEffect(int id, VibrationEffect effect, AudioAttributes attributes) {
+    public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, VibrationEffect effect,
+            AudioAttributes attributes) {
         if (mService == null) {
             Log.w(TAG, "Failed to set always-on effect; no vibrator service.");
             return false;
         }
         try {
             VibrationAttributes atr = new VibrationAttributes.Builder(attributes, effect).build();
-            return mService.setAlwaysOnEffect(id, effect, atr);
+            return mService.setAlwaysOnEffect(uid, opPkg, alwaysOnId, effect, atr);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to set always-on effect.", e);
         }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index d8fadfb..2eaefca 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -192,7 +192,11 @@
     /**
      * Specifies if a user is disallowed from changing Wi-Fi
      * access points. The default value is <code>false</code>.
-     * <p>This restriction has no effect in a managed profile.
+     * <p>
+     * Device owner and profile owner can set this restriction, although the restriction has no
+     * effect in a managed profile. When it is set by the profile owner of an organization-owned
+     * managed profile on the parent profile, it will disallow the personal user from changing
+     * Wi-Fi access points.
      *
      * <p>Key for user restrictions.
      * <p>Type: Boolean
@@ -242,8 +246,13 @@
     /**
      * Specifies if a user is disallowed from turning on location sharing.
      * The default value is <code>false</code>.
-     * <p>In a managed profile, location sharing always reflects the primary user's setting, but
+     * <p>
+     * In a managed profile, location sharing always reflects the primary user's setting, but
      * can be overridden and forced off by setting this restriction to true in the managed profile.
+     * <p>
+     * Device owner and profile owner can set this restriction. When it is set by the profile
+     * owner of an organization-owned managed profile on the parent profile, it will prevent the
+     * user from turning on location sharing in the personal profile.
      *
      * <p>Key for user restrictions.
      * <p>Type: Boolean
@@ -349,9 +358,14 @@
      * Specifies if a user is disallowed from configuring bluetooth.
      * This does <em>not</em> restrict the user from turning bluetooth on or off.
      * The default value is <code>false</code>.
-     * <p>This restriction doesn't prevent the user from using bluetooth. For disallowing usage of
+     * <p>
+     * This restriction doesn't prevent the user from using bluetooth. For disallowing usage of
      * bluetooth completely on the device, use {@link #DISALLOW_BLUETOOTH}.
-     * <p>This restriction has no effect in a managed profile.
+     * <p>
+     * Device owner and profile owner can set this restriction, although the restriction has no
+     * effect in a managed profile. When it is set by the profile owner of an organization-owned
+     * managed profile on the parent profile, it will disallow the personal user from configuring
+     * bluetooth.
      *
      * <p>Key for user restrictions.
      * <p>Type: Boolean
@@ -364,8 +378,10 @@
     /**
      * Specifies if bluetooth is disallowed on the device.
      *
-     * <p> This restriction can only be set by the device owner and the profile owner on the
-     * primary user and it applies globally - i.e. it disables bluetooth on the entire device.
+     * <p> This restriction can only be set by the device owner, the profile owner on the
+     * primary user or the profile owner of an organization-owned managed profile on the
+     * parent profile and it applies globally - i.e. it disables bluetooth on the entire
+     * device.
      * <p>The default value is <code>false</code>.
      * <p>Key for user restrictions.
      * <p>Type: Boolean
@@ -377,8 +393,9 @@
 
     /**
      * Specifies if outgoing bluetooth sharing is disallowed on the device. Device owner and profile
-     * owner can set this restriction. When it is set by device owner, all users on this device will
-     * be affected.
+     * owner can set this restriction. When it is set by device owner or the profile owner of an
+     * organization-owned managed profile on the parent profile, all users on this device will be
+     * affected.
      *
      * <p>Default is <code>true</code> for managed profiles and false for otherwise. When a device
      * upgrades to {@link android.os.Build.VERSION_CODES#O}, the system sets it for all existing
@@ -394,7 +411,8 @@
 
     /**
      * Specifies if a user is disallowed from transferring files over
-     * USB. This can only be set by device owners and profile owners on the primary user.
+     * USB. This can only be set by device owners, profile owners on the primary user or
+     * profile owners of organization-owned managed profiles on the parent profile.
      * The default value is <code>false</code>.
      *
      * <p>Key for user restrictions.
@@ -453,8 +471,9 @@
 
     /**
      * Specifies if a user is disallowed from enabling or accessing debugging features. When set on
-     * the primary user, disables debugging features altogether, including USB debugging. When set
-     * on a managed profile or a secondary user, blocks debugging for that user only, including
+     * the primary user or by the profile owner of an organization-owned managed profile on the
+     * parent profile, disables debugging features altogether, including USB debugging. When set on
+     * a managed profile or a secondary user, blocks debugging for that user only, including
      * starting activities, making service calls, accessing content providers, sending broadcasts,
      * installing/uninstalling packages, clearing user data, etc.
      * The default value is <code>false</code>.
@@ -485,18 +504,19 @@
 
     /**
      * Specifies if a user is disallowed from enabling or disabling location providers. As a
-     * result, user is disallowed from turning on or off location. Device owner and profile owners
-     * can set this restriction and it only applies on the managed user.
+     * result, user is disallowed from turning on or off location.
      *
-     * <p>In a managed profile, location sharing is forced off when it's off on primary user, so
-     * user can still turn off location sharing on managed profile when the restriction is set by
-     * profile owner on managed profile.
-     *
-     * <p>This user restriction is different from {@link #DISALLOW_SHARE_LOCATION},
+     * <p>
+     * In a managed profile, location sharing is forced off when it is turned off on the primary
+     * user or by the profile owner of an organization-owned managed profile on the parent profile.
+     * The user can still turn off location sharing on a managed profile when the restriction is
+     * set by the profile owner on a managed profile.
+     * <p>
+     * This user restriction is different from {@link #DISALLOW_SHARE_LOCATION},
      * as the device owner or profile owner can still enable or disable location mode via
      * {@link DevicePolicyManager#setLocationEnabled} when this restriction is on.
-     *
-     * <p>The default value is <code>false</code>.
+     * <p>
+     * The default value is <code>false</code>.
      *
      * <p>Key for user restrictions.
      * <p>Type: Boolean
@@ -510,7 +530,8 @@
     /**
      * Specifies if date, time and timezone configuring is disallowed.
      *
-     * <p>When restriction is set by device owners, it applies globally - i.e., it disables date,
+     * <p>When restriction is set by device owners or profile owners of organization-owned
+     * managed profiles on the parent profile, it applies globally - i.e., it disables date,
      * time and timezone setting on the entire device and all users will be affected. When it's set
      * by profile owners, it's only applied to the managed user.
      * <p>The default value is <code>false</code>.
@@ -526,8 +547,9 @@
 
     /**
      * Specifies if a user is disallowed from configuring Tethering
-     * & portable hotspots. This can only be set by device owners and profile owners on the
-     * primary user. The default value is <code>false</code>.
+     * & portable hotspots. This can only be set by device owners, profile owners on the
+     * primary user or profile owners of organization-owned managed profiles on the parent profile.
+     * The default value is <code>false</code>.
      * <p>In Android 9.0 or higher, if tethering is enabled when this restriction is set,
      * tethering will be automatically turned off.
      *
@@ -571,8 +593,8 @@
 
     /**
      * Specifies if a user is disallowed from adding new users. This can only be set by device
-     * owners and profile owners on the primary user.
-     * The default value is <code>false</code>.
+     * owners, profile owners on the primary user or profile owners of organization-owned managed
+     * profiles on the parent profile. The default value is <code>false</code>.
      * <p>This restriction has no effect on secondary users and managed profiles since only the
      * primary user can add other users.
      *
@@ -621,7 +643,8 @@
 
     /**
      * Specifies if a user is disallowed from configuring cell
-     * broadcasts. This can only be set by device owners and profile owners on the primary user.
+     * broadcasts. This can only be set by device owners, profile owners on the primary user or
+     * profile owners of organization-owned managed profiles on the parent profile.
      * The default value is <code>false</code>.
      * <p>This restriction has no effect on secondary users and managed profiles since only the
      * primary user can configure cell broadcasts.
@@ -636,7 +659,8 @@
 
     /**
      * Specifies if a user is disallowed from configuring mobile
-     * networks. This can only be set by device owners and profile owners on the primary user.
+     * networks. This can only be set by device owners, profile owners on the primary user or
+     * profile owners of organization-owned managed profiles on the parent profile.
      * The default value is <code>false</code>.
      * <p>This restriction has no effect on secondary users and managed profiles since only the
      * primary user can configure mobile networks.
@@ -739,6 +763,10 @@
     /**
      * Specifies that the user is not allowed to send or receive
      * SMS messages. The default value is <code>false</code>.
+     * <p>
+     * Device owner and profile owner can set this restriction. When it is set by the
+     * profile owner of an organization-owned managed profile on the parent profile,
+     * it will disable SMS in the personal profile.
      *
      * <p>Key for user restrictions.
      * <p>Type: Boolean
@@ -857,7 +885,8 @@
 
     /**
      * Specifies if the user is not allowed to reboot the device into safe boot mode.
-     * This can only be set by device owners and profile owners on the primary user.
+     * This can only be set by device owners, profile owners on the primary user or profile
+     * owners of organization-owned managed profiles on the parent profile.
      * The default value is <code>false</code>.
      *
      * <p>Key for user restrictions.
@@ -896,6 +925,12 @@
 
     /**
      * Specifies if a user is not allowed to use the camera.
+     * <p>
+     * Device owner and profile owner can set this restriction. When the restriction is set by
+     * the device owner or the profile owner of an organization-owned managed profile on the
+     * parent profile, it is applied globally.
+     * <p>
+     * The default value is <code>false</code>.
      *
      * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
      * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
@@ -916,7 +951,8 @@
 
     /**
      * Specifies if a user is not allowed to use cellular data when roaming. This can only be set by
-     * device owners. The default value is <code>false</code>.
+     * device owners or profile owners of organization-owned managed profiles on the parent profile.
+     * The default value is <code>false</code>.
      *
      * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
      * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
@@ -1011,8 +1047,9 @@
      * Specifies if the contents of a user's screen is not allowed to be captured for artificial
      * intelligence purposes.
      *
-     * <p>Device owner and profile owner can set this restriction. When it is set by device owner,
-     * only the target user will be affected.
+     * <p>Device owner and profile owner can set this restriction. When it is set by the
+     * device owner or the profile owner of an organization-owned managed profile on the parent
+     * profile, only the target user will be affected.
      *
      * <p>The default value is <code>false</code>.
      *
@@ -1026,8 +1063,9 @@
      * Specifies if the current user is able to receive content suggestions for selections based on
      * the contents of their screen.
      *
-     * <p>Device owner and profile owner can set this restriction. When it is set by device owner,
-     * only the target user will be affected.
+     * <p>Device owner and profile owner can set this restriction. When it is set by the
+     * device owner or the profile owner of an organization-owned managed profile on the parent
+     * profile, only the target user will be affected.
      *
      * <p>The default value is <code>false</code>.
      *
@@ -1093,7 +1131,9 @@
      *
      * <p>The default value is <code>false</code>.
      *
-     * <p>This user restriction can only be applied by the Device Owner.
+     * <p>This user restriction can only be applied by the device owner or the profile owner
+     * of an organization-owned managed profile on the parent profile.
+     *
      * <p>Key for user restrictions.
      * <p>Type: Boolean
      * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
@@ -2413,6 +2453,13 @@
      * by {@link #createUser(String, String, int)} or {@link #createGuest(Context, String)}), it
      * takes less time.
      *
+     * <p>This method completes the majority of work necessary for user creation: it
+     * creates user data, CE and DE encryption keys, app data directories, initializes the user and
+     * grants default permissions. When pre-created users become "real" users, only then are
+     * components notified of new user creation by firing user creation broadcasts.
+     *
+     * <p>All pre-created users are removed during system upgrade.
+     *
      * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      *
      * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
@@ -2424,6 +2471,7 @@
      *
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public @Nullable UserInfo preCreateUser(@NonNull String userType) {
         try {
             return mService.preCreateUser(userType);
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index ccbb0f1..ae75f3d 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -155,7 +155,7 @@
     /**
      * Configure an always-on haptics effect.
      *
-     * @param id The board-specific always-on ID to configure.
+     * @param alwaysOnId The board-specific always-on ID to configure.
      * @param effect Vibration effect to assign to always-on id. Passing null will disable it.
      * @param attributes {@link AudioAttributes} corresponding to the vibration. For example,
      *        specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
@@ -164,8 +164,17 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)
-    public boolean setAlwaysOnEffect(int id, @Nullable VibrationEffect effect,
-                                  @Nullable AudioAttributes attributes) {
+    public boolean setAlwaysOnEffect(int alwaysOnId, @Nullable VibrationEffect effect,
+            @Nullable AudioAttributes attributes) {
+        return setAlwaysOnEffect(Process.myUid(), mPackageName, alwaysOnId, effect, attributes);
+    }
+
+    /**
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)
+    public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
+            @Nullable VibrationEffect effect, @Nullable AudioAttributes attributes) {
         Log.w(TAG, "Always-on effects aren't supported");
         return false;
     }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 089122d..e2b33e0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6420,6 +6420,15 @@
                 "accessibility_button_target_component";
 
         /**
+         * The system class name of magnification controller which is a target to be toggled via
+         * accessibility shortcut or accessibility button.
+         *
+         * @hide
+         */
+        public static final String ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER =
+                "com.android.server.accessibility.MagnificationController";
+
+        /**
          * If touch exploration is enabled.
          */
         public static final String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java
new file mode 100644
index 0000000..ada59d6
--- /dev/null
+++ b/core/java/android/timezone/CountryTimeZones.java
@@ -0,0 +1,265 @@
+/*
+ * 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.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.icu.util.TimeZone;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Information about a country's time zones.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class CountryTimeZones {
+
+    /**
+     * A mapping to a time zone ID with some associated metadata.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final class TimeZoneMapping {
+
+        private libcore.timezone.CountryTimeZones.TimeZoneMapping mDelegate;
+
+        TimeZoneMapping(libcore.timezone.CountryTimeZones.TimeZoneMapping delegate) {
+            this.mDelegate = Objects.requireNonNull(delegate);
+        }
+
+        /**
+         * Returns the ID for this mapping. See also {@link #getTimeZone()} which handles when the
+         * ID is unrecognized.
+         */
+        @NonNull
+        public String getTimeZoneId() {
+            return mDelegate.timeZoneId;
+        }
+
+        /**
+         * Returns a {@link TimeZone} object for this mapping, or {@code null} if the ID is
+         * unrecognized.
+         */
+        @Nullable
+        public TimeZone getTimeZone() {
+            return mDelegate.getTimeZone();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            TimeZoneMapping that = (TimeZoneMapping) o;
+            return this.mDelegate.equals(that.mDelegate);
+        }
+
+        @Override
+        public int hashCode() {
+            return this.mDelegate.hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return mDelegate.toString();
+        }
+    }
+
+    /**
+     * The result of lookup up a time zone using offset information (and possibly more).
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final class OffsetResult {
+
+        private final TimeZone mTimeZone;
+        private final boolean mIsOnlyMatch;
+
+        /** Creates an instance with the supplied information. */
+        public OffsetResult(@NonNull TimeZone timeZone, boolean isOnlyMatch) {
+            mTimeZone = Objects.requireNonNull(timeZone);
+            mIsOnlyMatch = isOnlyMatch;
+        }
+
+        /**
+         * Returns a time zone that matches the supplied criteria.
+         */
+        @NonNull
+        public TimeZone getTimeZone() {
+            return mTimeZone;
+        }
+
+        /**
+         * Returns {@code true} if there is only one matching time zone for the supplied criteria.
+         */
+        public boolean isOnlyMatch() {
+            return mIsOnlyMatch;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            OffsetResult that = (OffsetResult) o;
+            return mIsOnlyMatch == that.mIsOnlyMatch
+                    && mTimeZone.getID().equals(that.mTimeZone.getID());
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mTimeZone, mIsOnlyMatch);
+        }
+
+        @Override
+        public String toString() {
+            return "OffsetResult{"
+                    + "mTimeZone=" + mTimeZone
+                    + ", mIsOnlyMatch=" + mIsOnlyMatch
+                    + '}';
+        }
+    }
+
+    @NonNull
+    private final libcore.timezone.CountryTimeZones mDelegate;
+
+    CountryTimeZones(libcore.timezone.CountryTimeZones delegate) {
+        mDelegate = delegate;
+    }
+
+    /**
+     * Returns true if the ISO code for the country is a match for the one specified.
+     */
+    public boolean isForCountryCode(@NonNull String countryIso) {
+        return mDelegate.isForCountryCode(countryIso);
+    }
+
+    /**
+     * Returns the default time zone ID for the country. Can return {@code null} in cases when no
+     * data is available or the time zone ID was not recognized.
+     */
+    @Nullable
+    public String getDefaultTimeZoneId() {
+        return mDelegate.getDefaultTimeZoneId();
+    }
+
+    /**
+     * Returns the default time zone for the country. Can return {@code null} in cases when no data
+     * is available or the time zone ID was not recognized.
+     */
+    @Nullable
+    public TimeZone getDefaultTimeZone() {
+        return mDelegate.getDefaultTimeZone();
+    }
+
+    /**
+     * Qualifier for a country's default time zone. {@code true} indicates whether the default
+     * would be a good choice <em>generally</em> when there's no other information available.
+     */
+    public boolean isDefaultTimeZoneBoosted() {
+        return mDelegate.getDefaultTimeZoneBoost();
+    }
+
+    /**
+     * Returns true if the country has at least one zone that is the same as UTC at the given time.
+     */
+    public boolean hasUtcZone(long whenMillis) {
+        return mDelegate.hasUtcZone(whenMillis);
+    }
+
+    /**
+     * Returns a time zone for the country, if there is one, that matches the desired properties. If
+     * there are multiple matches and the {@code bias} is one of them then it is returned, otherwise
+     * an arbitrary match is returned based on the {@link #getEffectiveTimeZoneMappingsAt(long)}
+     * ordering.
+     *
+     * @param totalOffsetMillis the offset from UTC at {@code whenMillis}
+     * @param isDst the Daylight Savings Time state at {@code whenMillis}. {@code true} means DST,
+     *     {@code false} means not DST, {@code null} means unknown
+     * @param dstOffsetMillis the part of {@code totalOffsetMillis} contributed by DST, only used if
+     *     {@code isDst} is {@code true}. The value can be {@code null} if the DST offset is
+     *     unknown
+     * @param whenMillis the UTC time to match against
+     * @param bias the time zone to prefer, can be {@code null}
+     */
+    @Nullable
+    public OffsetResult lookupByOffsetWithBias(int totalOffsetMillis, @Nullable Boolean isDst,
+            @SuppressLint("AutoBoxing") @Nullable Integer dstOffsetMillis, long whenMillis,
+            @Nullable TimeZone bias) {
+        libcore.timezone.CountryTimeZones.OffsetResult delegateOffsetResult =
+                mDelegate.lookupByOffsetWithBias(
+                        totalOffsetMillis, isDst, dstOffsetMillis, whenMillis, bias);
+        return delegateOffsetResult == null ? null :
+                new OffsetResult(delegateOffsetResult.mTimeZone, delegateOffsetResult.mOneMatch);
+    }
+
+    /**
+     * Returns an immutable, ordered list of time zone mappings for the country in an undefined but
+     * "priority" order, filtered so that only "effective" time zone IDs are returned. An
+     * "effective" time zone is one that differs from another time zone used in the country after
+     * {@code whenMillis}. The list can be empty if there were no zones configured or the configured
+     * zone IDs were not recognized.
+     */
+    @NonNull
+    public List<TimeZoneMapping> getEffectiveTimeZoneMappingsAt(long whenMillis) {
+        List<libcore.timezone.CountryTimeZones.TimeZoneMapping> delegateList =
+                mDelegate.getEffectiveTimeZoneMappingsAt(whenMillis);
+
+        List<TimeZoneMapping> toReturn = new ArrayList<>(delegateList.size());
+        for (libcore.timezone.CountryTimeZones.TimeZoneMapping delegateMapping : delegateList) {
+            toReturn.add(new TimeZoneMapping(delegateMapping));
+        }
+        return Collections.unmodifiableList(toReturn);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        CountryTimeZones that = (CountryTimeZones) o;
+        return mDelegate.equals(that.mDelegate);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mDelegate);
+    }
+
+    @Override
+    public String toString() {
+        return mDelegate.toString();
+    }
+}
diff --git a/core/java/android/timezone/TelephonyLookup.java b/core/java/android/timezone/TelephonyLookup.java
new file mode 100644
index 0000000..39dbe85
--- /dev/null
+++ b/core/java/android/timezone/TelephonyLookup.java
@@ -0,0 +1,71 @@
+/*
+ * 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.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Objects;
+
+/**
+ * A class that can find time zone-related information about telephony networks.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public class TelephonyLookup {
+
+    private static Object sLock = new Object();
+    @GuardedBy("sLock")
+    private static TelephonyLookup sInstance;
+
+    @NonNull
+    private final libcore.timezone.TelephonyLookup mDelegate;
+
+    /**
+     * Obtains an instance for use when resolving telephony time zone information. This method never
+     * returns {@code null}.
+     */
+    @NonNull
+    public static TelephonyLookup getInstance() {
+        synchronized (sLock) {
+            if (sInstance == null) {
+                sInstance = new TelephonyLookup(libcore.timezone.TelephonyLookup.getInstance());
+            }
+            return sInstance;
+        }
+    }
+
+    private TelephonyLookup(@NonNull libcore.timezone.TelephonyLookup delegate) {
+        mDelegate = Objects.requireNonNull(delegate);
+    }
+
+    /**
+     * Returns an object capable of querying telephony network information. This method can return
+     * {@code null} in the event of an error while reading the underlying data files.
+     */
+    @Nullable
+    public TelephonyNetworkFinder getTelephonyNetworkFinder() {
+        libcore.timezone.TelephonyNetworkFinder telephonyNetworkFinderDelegate =
+                mDelegate.getTelephonyNetworkFinder();
+        return telephonyNetworkFinderDelegate != null
+                ? new TelephonyNetworkFinder(telephonyNetworkFinderDelegate) : null;
+    }
+}
diff --git a/core/java/android/timezone/TelephonyNetwork.java b/core/java/android/timezone/TelephonyNetwork.java
new file mode 100644
index 0000000..ae39fbd
--- /dev/null
+++ b/core/java/android/timezone/TelephonyNetwork.java
@@ -0,0 +1,86 @@
+/*
+ * 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.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import java.util.Objects;
+
+/**
+ * Information about a telephony network.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public class TelephonyNetwork {
+
+    @NonNull
+    private final libcore.timezone.TelephonyNetwork mDelegate;
+
+    TelephonyNetwork(@NonNull libcore.timezone.TelephonyNetwork delegate) {
+        mDelegate = Objects.requireNonNull(delegate);
+    }
+
+    /**
+     * Returns the Mobile Country Code of the network.
+     */
+    @NonNull
+    public String getMcc() {
+        return mDelegate.getMcc();
+    }
+
+    /**
+     * Returns the Mobile Network Code of the network.
+     */
+    @NonNull
+    public String getMnc() {
+        return mDelegate.getMnc();
+    }
+
+    /**
+     * Returns the country in which the network operates as an ISO 3166 alpha-2 (lower case).
+     */
+    @NonNull
+    public String getCountryIsoCode() {
+        return mDelegate.getCountryIsoCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        TelephonyNetwork that = (TelephonyNetwork) o;
+        return mDelegate.equals(that.mDelegate);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mDelegate);
+    }
+
+    @Override
+    public String toString() {
+        return "TelephonyNetwork{"
+                + "mDelegate=" + mDelegate
+                + '}';
+    }
+}
diff --git a/core/java/android/timezone/TelephonyNetworkFinder.java b/core/java/android/timezone/TelephonyNetworkFinder.java
new file mode 100644
index 0000000..a81a516
--- /dev/null
+++ b/core/java/android/timezone/TelephonyNetworkFinder.java
@@ -0,0 +1,55 @@
+/*
+ * 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.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+import java.util.Objects;
+
+/**
+ * A class that can find telephony networks loaded via {@link TelephonyLookup}.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public class TelephonyNetworkFinder {
+
+    @NonNull
+    private final libcore.timezone.TelephonyNetworkFinder mDelegate;
+
+    TelephonyNetworkFinder(libcore.timezone.TelephonyNetworkFinder delegate) {
+        mDelegate = Objects.requireNonNull(delegate);
+    }
+
+    /**
+     * Returns information held about a specific MCC + MNC combination. It is expected for this
+     * method to return {@code null}. Only known, unusual networks will typically have information
+     * returned, e.g. if they operate in countries other than the one suggested by their MCC.
+     */
+    @Nullable
+    public TelephonyNetwork findNetworkByMccMnc(@NonNull String mcc, @NonNull String mnc) {
+        Objects.requireNonNull(mcc);
+        Objects.requireNonNull(mnc);
+
+        libcore.timezone.TelephonyNetwork telephonyNetworkDelegate =
+                mDelegate.findNetworkByMccMnc(mcc, mnc);
+        return telephonyNetworkDelegate != null
+                ? new TelephonyNetwork(telephonyNetworkDelegate) : null;
+    }
+}
diff --git a/core/java/android/timezone/TimeZoneFinder.java b/core/java/android/timezone/TimeZoneFinder.java
new file mode 100644
index 0000000..15dfe62
--- /dev/null
+++ b/core/java/android/timezone/TimeZoneFinder.java
@@ -0,0 +1,67 @@
+/*
+ * 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.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * A class that can be used to find time zones.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class TimeZoneFinder {
+
+    private static Object sLock = new Object();
+    @GuardedBy("sLock")
+    private static TimeZoneFinder sInstance;
+
+    private final libcore.timezone.TimeZoneFinder mDelegate;
+
+    private TimeZoneFinder(libcore.timezone.TimeZoneFinder delegate) {
+        mDelegate = delegate;
+    }
+
+    /**
+     * Obtains an instance for use when resolving telephony time zone information. This method never
+     * returns {@code null}.
+     */
+    @NonNull
+    public static TimeZoneFinder getInstance() {
+        synchronized (sLock) {
+            if (sInstance == null) {
+                sInstance = new TimeZoneFinder(libcore.timezone.TimeZoneFinder.getInstance());
+            }
+        }
+        return sInstance;
+    }
+
+    /**
+     * Returns a {@link CountryTimeZones} object associated with the specified country code.
+     * Caching is handled as needed. If the country code is not recognized or there is an error
+     * during lookup this method can return null.
+     */
+    @Nullable
+    public CountryTimeZones lookupCountryTimeZones(@NonNull String countryIso) {
+        libcore.timezone.CountryTimeZones delegate = mDelegate.lookupCountryTimeZones(countryIso);
+        return delegate == null ? null : new CountryTimeZones(delegate);
+    }
+}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 4368115..178b3c0 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_MODE;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
@@ -1010,6 +1011,9 @@
      * @return Supported WCG color spaces.
      * @hide
      */
+    @SuppressLint("VisiblySynchronized")
+    @NonNull
+    @TestApi
     public @ColorMode ColorSpace[] getSupportedWideColorGamut() {
         synchronized (this) {
             final ColorSpace[] defaultColorSpaces = new ColorSpace[0];
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index c67ff6e..7986ceb 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -128,6 +128,19 @@
     }
 
     /**
+     * Called when a focus event is received.
+     *
+     * @param hasFocus if true, the window associated with this input channel has just received
+     *                 focus
+     *                 if false, the window associated with this input channel has just lost focus
+     * @param inTouchMode if true, the device is in touch mode
+     *                    if false, the device is not in touch mode
+     */
+    // Called from native code.
+    public void onFocusEvent(boolean hasFocus, boolean inTouchMode) {
+    }
+
+    /**
      * Called when a batched input event is pending.
      *
      * The batched input event will continue to accumulate additional movement
@@ -213,8 +226,13 @@
         onBatchedInputEventPending();
     }
 
-    public static interface Factory {
-        public InputEventReceiver createInputEventReceiver(
-                InputChannel inputChannel, Looper looper);
+    /**
+     * Factory for InputEventReceiver
+     */
+    public interface Factory {
+        /**
+         * Create a new InputReceiver for a given inputChannel
+         */
+        InputEventReceiver createInputEventReceiver(InputChannel inputChannel, Looper looper);
     }
 }
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 1782544..0de1a4f 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -42,6 +42,7 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.SurfaceControl.Transaction;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 import com.android.internal.view.SurfaceCallbackHelper;
 
@@ -201,6 +202,9 @@
     private SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
     private int mParentSurfaceGenerationId;
 
+    // The token of embedded windowless view hierarchy.
+    private IBinder mEmbeddedViewHierarchy;
+
     public SurfaceView(Context context) {
         this(context, null);
     }
@@ -1531,4 +1535,27 @@
         if (viewRoot == null) return;
         viewRoot.setUseBLASTSyncTransaction();
     }
+
+    /**
+     * Add the token of embedded view hierarchy. Set {@code null} to clear the embedded view
+     * hierarchy.
+     *
+     * @param token IBinder token.
+     * @hide
+     */
+    public void setEmbeddedViewHierarchy(IBinder token) {
+        mEmbeddedViewHierarchy = token;
+    }
+
+    /** @hide */
+    @Override
+    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfoInternal(info);
+        if (mEmbeddedViewHierarchy == null) {
+            return;
+        }
+        // Add a leashed child when this SurfaceView embeds another view hierarchy. Getting this
+        // leashed child would return the root node in the embedded hierarchy
+        info.addChild(mEmbeddedViewHierarchy);
+    }
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e0f6e06..2ef944f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -8044,6 +8044,11 @@
         }
 
         @Override
+        public void onFocusEvent(boolean hasFocus, boolean inTouchMode) {
+            windowFocusChanged(hasFocus, inTouchMode);
+        }
+
+        @Override
         public void dispose() {
             unscheduleConsumeBatchedInput();
             super.dispose();
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 914ff18..b9f08ad 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -17,10 +17,14 @@
 package android.view.accessibility;
 
 import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.graphics.Bitmap;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
@@ -337,6 +341,48 @@
         return emptyWindows;
     }
 
+
+    /**
+     * Finds an {@link AccessibilityNodeInfo} by accessibility id and given leash token instead of
+     * window id. This method is used to find the leashed node on the embedded view hierarchy.
+     *
+     * @param connectionId The id of a connection for interacting with the system.
+     * @param leashToken The token of the embedded hierarchy.
+     * @param accessibilityNodeId A unique view id or virtual descendant id from
+     *     where to start the search. Use
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
+     *     to start from the root.
+     * @param bypassCache Whether to bypass the cache while looking for the node.
+     * @param prefetchFlags flags to guide prefetching.
+     * @param arguments Optional action arguments.
+     * @return An {@link AccessibilityNodeInfo} if found, null otherwise.
+     */
+    public @Nullable AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(
+            int connectionId, @NonNull IBinder leashToken, long accessibilityNodeId,
+            boolean bypassCache, int prefetchFlags, Bundle arguments) {
+        if (leashToken == null) {
+            return null;
+        }
+        int windowId = -1;
+        try {
+            IAccessibilityServiceConnection connection = getConnection(connectionId);
+            if (connection != null) {
+                windowId = connection.getWindowIdForLeashToken(leashToken);
+            } else {
+                if (DEBUG) {
+                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
+                }
+            }
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error while calling remote getWindowIdForLeashToken", re);
+        }
+        if (windowId == -1) {
+            return null;
+        }
+        return findAccessibilityNodeInfoByAccessibilityId(connectionId, windowId,
+                accessibilityNodeId, bypassCache, prefetchFlags, arguments);
+    }
+
     /**
      * Finds an {@link AccessibilityNodeInfo} by accessibility id.
      *
@@ -783,6 +829,31 @@
     }
 
     /**
+     * Takes the screenshot of the specified display and returns it by bitmap format.
+     *
+     * @param connectionId The id of a connection for interacting with the system.
+     * @param displayId The logic display id, use {@link Display#DEFAULT_DISPLAY} for
+     *                  default display.
+     * @return The screenshot bitmap on success, null otherwise.
+     */
+    public Bitmap takeScreenshot(int connectionId, int displayId) {
+        Bitmap screenShot = null;
+        try {
+            IAccessibilityServiceConnection connection = getConnection(connectionId);
+            if (connection != null) {
+                screenShot = connection.takeScreenshot(displayId);
+            } else {
+                if (DEBUG) {
+                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
+                }
+            }
+        } catch (RemoteException re) {
+            Log.w(LOG_TAG, "Error while calling remote takeScreenshot", re);
+        }
+        return screenShot;
+    }
+
+    /**
      * Clears the result state.
      */
     private void clearResultLocked() {
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 2e5a4b5..3dfeffb 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -1195,6 +1195,19 @@
     @TestApi
     @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
     public void performAccessibilityShortcut() {
+        performAccessibilityShortcut(null);
+    }
+
+    /**
+     * Perform the accessibility shortcut for the given target which is assigned to the shortcut.
+     *
+     * @param targetName The flattened {@link ComponentName} string or the class name of a system
+     *        class implementing a supported accessibility feature, or {@code null} if there's no
+     *        specified target.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
+    public void performAccessibilityShortcut(@Nullable String targetName) {
         final IAccessibilityManager service;
         synchronized (mLock) {
             service = getServiceLocked();
@@ -1203,7 +1216,7 @@
             }
         }
         try {
-            service.performAccessibilityShortcut();
+            service.performAccessibilityShortcut(targetName);
         } catch (RemoteException re) {
             Log.e(LOG_TAG, "Error performing accessibility shortcut. ", re);
         }
@@ -1270,7 +1283,22 @@
      * @param displayId The logical display id.
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
     public void notifyAccessibilityButtonClicked(int displayId) {
+        notifyAccessibilityButtonClicked(displayId, null);
+    }
+
+    /**
+     * Perform the accessibility button for the given target which is assigned to the button.
+     *
+     * @param displayId displayId The logical display id.
+     * @param targetName The flattened {@link ComponentName} string or the class name of a system
+     *        class implementing a supported accessibility feature, or {@code null} if there's no
+     *        specified target.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
+    public void notifyAccessibilityButtonClicked(int displayId, @Nullable String targetName) {
         final IAccessibilityManager service;
         synchronized (mLock) {
             service = getServiceLocked();
@@ -1279,7 +1307,7 @@
             }
         }
         try {
-            service.notifyAccessibilityButtonClicked(displayId);
+            service.notifyAccessibilityButtonClicked(displayId, targetName);
         } catch (RemoteException re) {
             Log.e(LOG_TAG, "Error while dispatching accessibility button click", re);
         }
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 92aa7d5..184f3302 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -31,6 +31,7 @@
 import android.graphics.Region;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.InputType;
@@ -110,6 +111,9 @@
     public static final int ROOT_ITEM_ID = Integer.MAX_VALUE - 1;
 
     /** @hide */
+    public static final int LEASHED_ITEM_ID = Integer.MAX_VALUE - 2;
+
+    /** @hide */
     public static final long UNDEFINED_NODE_ID = makeNodeId(UNDEFINED_ITEM_ID, UNDEFINED_ITEM_ID);
 
     /** @hide */
@@ -117,6 +121,10 @@
             AccessibilityNodeProvider.HOST_VIEW_ID);
 
     /** @hide */
+    public static final long LEASHED_NODE_ID = makeNodeId(LEASHED_ITEM_ID,
+            AccessibilityNodeProvider.HOST_VIEW_ID);
+
+    /** @hide */
     public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001;
 
     /** @hide */
@@ -788,6 +796,10 @@
 
     private TouchDelegateInfo mTouchDelegateInfo;
 
+    private IBinder mLeashedChild;
+    private IBinder mLeashedParent;
+    private long mLeashedParentNodeId = UNDEFINED_NODE_ID;
+
     /**
      * Creates a new {@link AccessibilityNodeInfo}.
      */
@@ -1039,7 +1051,12 @@
             return null;
         }
         final long childId = mChildNodeIds.get(index);
-        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+        final AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+        if (mLeashedChild != null && childId == LEASHED_NODE_ID) {
+            return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mLeashedChild,
+                    ROOT_NODE_ID, false, FLAG_PREFETCH_DESCENDANTS, null);
+        }
+
         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId,
                 childId, false, FLAG_PREFETCH_DESCENDANTS, null);
     }
@@ -1062,6 +1079,43 @@
     }
 
     /**
+     * Adds a view root from leashed content as a child. This method is used to embedded another
+     * view hierarchy.
+     * <p>
+     * <strong>Note:</strong> Only one leashed child is permitted.
+     * </p>
+     * <p>
+     * <strong>Note:</strong> Cannot be called from an
+     * {@link android.accessibilityservice.AccessibilityService}.
+     * This class is made immutable before being delivered to an AccessibilityService.
+     * Note that a view cannot be made its own child.
+     * </p>
+     *
+     * @param token The token to which a view root is added.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     * @hide
+     */
+    @TestApi
+    public void addChild(@NonNull IBinder token) {
+        enforceNotSealed();
+        if (token == null) {
+            return;
+        }
+        if (mChildNodeIds == null) {
+            mChildNodeIds = new LongArray();
+        }
+
+        mLeashedChild = token;
+        // Checking uniqueness.
+        // Since only one leashed child is permitted, skip adding ID if the ID already exists.
+        if (mChildNodeIds.indexOf(LEASHED_NODE_ID) >= 0) {
+            return;
+        }
+        mChildNodeIds.add(LEASHED_NODE_ID);
+    }
+
+    /**
      * Unchecked version of {@link #addChild(View)} that does not verify
      * uniqueness. For framework use only.
      *
@@ -1090,6 +1144,38 @@
     }
 
     /**
+     * Removes a leashed child. If the child was not previously added to the node,
+     * calling this method has no effect.
+     * <p>
+     * <strong>Note:</strong> Cannot be called from an
+     * {@link android.accessibilityservice.AccessibilityService}.
+     * This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     *
+     * @param token The token of the leashed child
+     * @return true if the child was present
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     * @hide
+     */
+    public boolean removeChild(IBinder token) {
+        enforceNotSealed();
+        if (mChildNodeIds == null || mLeashedChild == null) {
+            return false;
+        }
+        if (!mLeashedChild.equals(token)) {
+            return false;
+        }
+        final int index = mChildNodeIds.indexOf(LEASHED_NODE_ID);
+        mLeashedChild = null;
+        if (index < 0) {
+            return false;
+        }
+        mChildNodeIds.remove(index);
+        return true;
+    }
+
+    /**
      * Adds a virtual child which is a descendant of the given <code>root</code>.
      * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
      * is added as a child.
@@ -1668,6 +1754,9 @@
      */
     public AccessibilityNodeInfo getParent() {
         enforceSealed();
+        if (mLeashedParent != null && mLeashedParentNodeId != UNDEFINED_NODE_ID) {
+            return getNodeForAccessibilityId(mConnectionId, mLeashedParent, mLeashedParentNodeId);
+        }
         return getNodeForAccessibilityId(mConnectionId, mWindowId, mParentNodeId);
     }
 
@@ -3257,6 +3346,40 @@
     }
 
     /**
+     * Sets the token and node id of the leashed parent.
+     *
+     * @param token The token.
+     * @param viewId The accessibility view id.
+     * @hide
+     */
+    @TestApi
+    public void setLeashedParent(@Nullable IBinder token, int viewId) {
+        enforceNotSealed();
+        mLeashedParent = token;
+        mLeashedParentNodeId = makeNodeId(viewId, AccessibilityNodeProvider.HOST_VIEW_ID);
+    }
+
+    /**
+     * Gets the token of the leashed parent.
+     *
+     * @return The token.
+     * @hide
+     */
+    public @Nullable IBinder getLeashedParent() {
+        return mLeashedParent;
+    }
+
+    /**
+     * Gets the node id of the leashed parent.
+     *
+     * @return The accessibility node id.
+     * @hide
+     */
+    public long getLeashedParentNodeId() {
+        return mLeashedParentNodeId;
+    }
+
+    /**
      * Sets if this instance is sealed.
      *
      * @param sealed Whether is sealed.
@@ -3559,6 +3682,18 @@
         if (!Objects.equals(mTouchDelegateInfo, DEFAULT.mTouchDelegateInfo)) {
             nonDefaultFields |= bitAt(fieldIndex);
         }
+        fieldIndex++;
+        if (mLeashedChild != DEFAULT.mLeashedChild) {
+            nonDefaultFields |= bitAt(fieldIndex);
+        }
+        fieldIndex++;
+        if (mLeashedParent != DEFAULT.mLeashedParent) {
+            nonDefaultFields |= bitAt(fieldIndex);
+        }
+        fieldIndex++;
+        if (mLeashedParentNodeId != DEFAULT.mLeashedParentNodeId) {
+            nonDefaultFields |= bitAt(fieldIndex);
+        }
         int totalFields = fieldIndex;
         parcel.writeLong(nonDefaultFields);
 
@@ -3685,6 +3820,16 @@
             mTouchDelegateInfo.writeToParcel(parcel, flags);
         }
 
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            parcel.writeStrongBinder(mLeashedChild);
+        }
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            parcel.writeStrongBinder(mLeashedParent);
+        }
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            parcel.writeLong(mLeashedParentNodeId);
+        }
+
         if (DEBUG) {
             fieldIndex--;
             if (totalFields != fieldIndex) {
@@ -3768,6 +3913,10 @@
         final TouchDelegateInfo otherInfo = other.mTouchDelegateInfo;
         mTouchDelegateInfo = (otherInfo != null)
                 ? new TouchDelegateInfo(otherInfo.mTargetMap, true) : null;
+
+        mLeashedChild = other.mLeashedChild;
+        mLeashedParent = other.mLeashedParent;
+        mLeashedParentNodeId = other.mLeashedParentNodeId;
     }
 
     private void initPoolingInfos(AccessibilityNodeInfo other) {
@@ -3921,6 +4070,16 @@
             mTouchDelegateInfo = TouchDelegateInfo.CREATOR.createFromParcel(parcel);
         }
 
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            mLeashedChild = parcel.readStrongBinder();
+        }
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            mLeashedParent = parcel.readStrongBinder();
+        }
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            mLeashedParentNodeId = parcel.readLong();
+        }
+
         mSealed = sealed;
     }
 
@@ -4200,6 +4359,19 @@
                         | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS, null);
     }
 
+    private static AccessibilityNodeInfo getNodeForAccessibilityId(int connectionId,
+            IBinder leashToken, long accessibilityId) {
+        if (!((leashToken != null)
+                && (getAccessibilityViewId(accessibilityId) != UNDEFINED_ITEM_ID)
+                && (connectionId != UNDEFINED_CONNECTION_ID))) {
+            return null;
+        }
+        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+        return client.findAccessibilityNodeInfoByAccessibilityId(connectionId,
+                leashToken, accessibilityId, false, FLAG_PREFETCH_PREDECESSORS
+                        | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS, null);
+    }
+
     /** @hide */
     public static String idToString(long accessibilityId) {
         int accessibilityViewId = getAccessibilityViewId(accessibilityId);
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 392db57..fcaaa2e 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -66,12 +66,12 @@
     // Used by UiAutomation
     IBinder getWindowToken(int windowId, int userId);
 
-    void notifyAccessibilityButtonClicked(int displayId);
+    void notifyAccessibilityButtonClicked(int displayId, String targetName);
 
     void notifyAccessibilityButtonVisibilityChanged(boolean available);
 
     // Requires Manifest.permission.MANAGE_ACCESSIBILITY
-    void performAccessibilityShortcut();
+    void performAccessibilityShortcut(String targetName);
 
     // Requires Manifest.permission.MANAGE_ACCESSIBILITY
     List<String> getAccessibilityShortcutTargets(int shortcutType);
diff --git a/core/java/android/webkit/PacProcessor.java b/core/java/android/webkit/PacProcessor.java
new file mode 100644
index 0000000..fe8bbb8
--- /dev/null
+++ b/core/java/android/webkit/PacProcessor.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 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.webkit;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+
+/**
+ * Class to evaluate PAC scripts.
+ * @hide
+ */
+
+@SystemApi
+public interface PacProcessor {
+
+    /**
+     * Returns the default PacProcessor instance.
+     *
+     * @return the default PacProcessor instance.
+     */
+    @NonNull
+    static PacProcessor getInstance() {
+        return WebViewFactory.getProvider().getPacProcessor();
+    }
+
+    /**
+     * Set PAC script to use.
+     *
+     * @param script PAC script.
+     * @return true if PAC script is successfully set.
+     */
+    boolean setProxyScript(@NonNull String script);
+
+    /**
+     * Gets a list of proxy servers to use.
+     * @param url The URL being accessed.
+     * @return a PAC-style semicolon-separated list of valid proxy servers.
+     *         For example: "PROXY xxx.xxx.xxx.xxx:xx; SOCKS yyy.yyy.yyy:yy".
+     */
+    @Nullable
+    String makeProxyRequest(@NonNull String url);
+}
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 6a1ed39..f7c3ec0 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -175,6 +175,15 @@
     WebViewDatabase getWebViewDatabase(Context context);
 
     /**
+     * Gets the singleton PacProcessor instance.
+     * @return the PacProcessor instance
+     */
+    @NonNull
+    default PacProcessor getPacProcessor() {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    /**
      * Gets the classloader used to load internal WebView implementation classes. This interface
      * should only be used by the WebView Support Library.
      */
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index f851e10..b891af5 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -152,7 +152,7 @@
 
     // Specifies whether to allow starting a cursor drag by dragging anywhere over the text.
     @VisibleForTesting
-    public static boolean FLAG_ENABLE_CURSOR_DRAG = false;
+    public static boolean FLAG_ENABLE_CURSOR_DRAG = true;
     // Specifies whether to use the magnifier when pressing the insertion or selection handles.
     private static final boolean FLAG_USE_MAGNIFIER = true;
 
@@ -5741,10 +5741,10 @@
                 return;
             }
             switch (event.getActionMasked()) {
-                case MotionEvent.ACTION_DOWN:
-                    mIsDraggingCursor = false;
-                    break;
                 case MotionEvent.ACTION_MOVE:
+                    if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
+                        break;
+                    }
                     if (mIsDraggingCursor) {
                         performCursorDrag(event);
                     } else if (FLAG_ENABLE_CURSOR_DRAG
diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java
index 6277afe..b13ca42 100644
--- a/core/java/android/widget/EditorTouchState.java
+++ b/core/java/android/widget/EditorTouchState.java
@@ -173,6 +173,13 @@
                     mIsDragCloseToVertical = (4 * deltaXSquared) <= distanceSquared;
                 }
             }
+        } else if (action == MotionEvent.ACTION_CANCEL) {
+            mLastDownMillis = 0;
+            mLastUpMillis = 0;
+            mMultiTapStatus = MultiTapStatus.NONE;
+            mMultiTapInSameArea = false;
+            mMovedEnoughForDrag = false;
+            mIsDragCloseToVertical = false;
         }
     }
 
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 5731e50..8565493 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -422,7 +422,7 @@
     /**
      * Update the displayed time if necessary and invalidate the view.
      */
-    public void refresh() {
+    public void refreshTime() {
         onTimeChanged();
         invalidate();
     }
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 9bdb4c1..d119b2e 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -16,6 +16,8 @@
 
 package android.widget;
 
+import static com.android.internal.util.Preconditions.checkNotNull;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -42,8 +44,12 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * A toast is a view containing a quick little message for the user.  The toast class
@@ -262,6 +268,29 @@
     }
 
     /**
+     * Adds a callback to be notified when the toast is shown or hidden.
+     *
+     * Note that if the toast is blocked for some reason you won't get a call back.
+     *
+     * @see #removeCallback(Callback)
+     */
+    public void addCallback(@NonNull Callback callback) {
+        checkNotNull(callback);
+        synchronized (mTN.mCallbacks) {
+            mTN.mCallbacks.add(callback);
+        }
+    }
+
+    /**
+     * Removes a callback previously added with {@link #addCallback(Callback)}.
+     */
+    public void removeCallback(@NonNull Callback callback) {
+        synchronized (mTN.mCallbacks) {
+            mTN.mCallbacks.remove(callback);
+        }
+    }
+
+    /**
      * Gets the LayoutParams for the Toast window.
      * @hide
      */
@@ -389,6 +418,9 @@
 
         String mPackageName;
 
+        @GuardedBy("mCallbacks")
+        private final List<Callback> mCallbacks = new ArrayList<>();
+
         static final long SHORT_DURATION_TIMEOUT = 4000;
         static final long LONG_DURATION_TIMEOUT = 7000;
 
@@ -449,6 +481,12 @@
             };
         }
 
+        private List<Callback> getCallbacks() {
+            synchronized (mCallbacks) {
+                return new ArrayList<>(mCallbacks);
+            }
+        }
+
         /**
          * schedule handleShow into the right thread
          */
@@ -522,6 +560,9 @@
                 try {
                     mWM.addView(mView, mParams);
                     trySendAccessibilityEvent();
+                    for (Callback callback : getCallbacks()) {
+                        callback.onToastShown();
+                    }
                 } catch (WindowManager.BadTokenException e) {
                     /* ignore */
                 }
@@ -564,8 +605,30 @@
                 } catch (RemoteException e) {
                 }
 
+                for (Callback callback : getCallbacks()) {
+                    callback.onToastHidden();
+                }
                 mView = null;
             }
         }
     }
+
+    /**
+     * Callback object to be called when the toast is shown or hidden.
+     *
+     * Callback methods will be called on the looper thread provided on construction.
+     *
+     * @see #addCallback(Callback)
+     */
+    public abstract static class Callback {
+        /**
+         * Called when the toast is displayed on the screen.
+         */
+        public void onToastShown() {}
+
+        /**
+         * Called when the toast is hidden.
+         */
+        public void onToastHidden() {}
+    }
 }
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index de204ba..b670316 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -19,6 +19,15 @@
 import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
 import static android.view.accessibility.AccessibilityManager.ShortcutType;
 
+import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+import static com.android.internal.app.AccessibilityButtonChooserActivity.WhiteListingFeatureElementIndex.COMPONENT_ID;
+import static com.android.internal.app.AccessibilityButtonChooserActivity.WhiteListingFeatureElementIndex.FRAGMENT_TYPE;
+import static com.android.internal.app.AccessibilityButtonChooserActivity.WhiteListingFeatureElementIndex.ICON_ID;
+import static com.android.internal.app.AccessibilityButtonChooserActivity.WhiteListingFeatureElementIndex.LABEL_ID;
+import static com.android.internal.app.AccessibilityButtonChooserActivity.WhiteListingFeatureElementIndex.SETTINGS_KEY;
+
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -63,10 +72,6 @@
  * Activity used to display and persist a service or feature target for the Accessibility button.
  */
 public class AccessibilityButtonChooserActivity extends Activity {
-
-    private static final String MAGNIFICATION_COMPONENT_ID =
-            "com.android.server.accessibility.MagnificationController";
-
     private static final char SERVICES_SEPARATOR = ':';
     private static final TextUtils.SimpleStringSplitter sStringColonSplitter =
             new TextUtils.SimpleStringSplitter(SERVICES_SEPARATOR);
@@ -76,7 +81,7 @@
             ACCESSIBILITY_SHORTCUT_KEY);
 
     private int mShortcutType;
-    private List<AccessibilityButtonTarget> mTargets = new ArrayList<>();
+    private final List<AccessibilityButtonTarget> mTargets = new ArrayList<>();
     private AlertDialog mAlertDialog;
     private TargetAdapter mTargetAdapter;
 
@@ -99,7 +104,7 @@
             UserShortcutType.TRIPLETAP,
     })
     /** Denotes the user shortcut type. */
-    public @interface UserShortcutType {
+    private @interface UserShortcutType {
         int DEFAULT = 0;
         int SOFTWARE = 1; // 1 << 0
         int HARDWARE = 2; // 1 << 1
@@ -122,7 +127,7 @@
             AccessibilityServiceFragmentType.INTUITIVE,
             AccessibilityServiceFragmentType.BOUNCE,
     })
-    public @interface AccessibilityServiceFragmentType {
+    private @interface AccessibilityServiceFragmentType {
         int LEGACY = 0;
         int INVISIBLE = 1;
         int INTUITIVE = 2;
@@ -140,11 +145,61 @@
             ShortcutMenuMode.LAUNCH,
             ShortcutMenuMode.EDIT,
     })
-    public @interface ShortcutMenuMode {
+    private @interface ShortcutMenuMode {
         int LAUNCH = 0;
         int EDIT = 1;
     }
 
+    /**
+     * Annotation for align the element index of white listing feature
+     * {@code WHITE_LISTING_FEATURES}.
+     *
+     * {@code COMPONENT_ID} is to get the service component name.
+     * {@code LABEL_ID} is to get the service label text.
+     * {@code ICON_ID} is to get the service icon.
+     * {@code FRAGMENT_TYPE} is to get the service fragment type.
+     * {@code SETTINGS_KEY} is to get the service settings key.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            WhiteListingFeatureElementIndex.COMPONENT_ID,
+            WhiteListingFeatureElementIndex.LABEL_ID,
+            WhiteListingFeatureElementIndex.ICON_ID,
+            WhiteListingFeatureElementIndex.FRAGMENT_TYPE,
+            WhiteListingFeatureElementIndex.SETTINGS_KEY,
+    })
+    @interface WhiteListingFeatureElementIndex {
+        int COMPONENT_ID = 0;
+        int LABEL_ID = 1;
+        int ICON_ID = 2;
+        int FRAGMENT_TYPE = 3;
+        int SETTINGS_KEY = 4;
+    }
+
+    private static final String[][] WHITE_LISTING_FEATURES = {
+            {
+                    COLOR_INVERSION_COMPONENT_NAME.flattenToString(),
+                    String.valueOf(R.string.color_inversion_feature_name),
+                    String.valueOf(R.drawable.ic_accessibility_color_inversion),
+                    String.valueOf(AccessibilityServiceFragmentType.INTUITIVE),
+                    Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
+            },
+            {
+                    DALTONIZER_COMPONENT_NAME.flattenToString(),
+                    String.valueOf(R.string.color_correction_feature_name),
+                    String.valueOf(R.drawable.ic_accessibility_color_correction),
+                    String.valueOf(AccessibilityServiceFragmentType.INTUITIVE),
+                    Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
+            },
+            {
+                    MAGNIFICATION_CONTROLLER_NAME,
+                    String.valueOf(R.string.accessibility_magnification_chooser_text),
+                    String.valueOf(R.drawable.ic_accessibility_magnification),
+                    String.valueOf(AccessibilityServiceFragmentType.INVISIBLE),
+                    Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
+            },
+    };
+
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -199,6 +254,20 @@
 
     private static List<AccessibilityButtonTarget> getServiceTargets(@NonNull Context context,
             @ShortcutType int shortcutType) {
+        final List<AccessibilityButtonTarget> targets = new ArrayList<>();
+        targets.addAll(getAccessibilityServiceTargets(context));
+        targets.addAll(getWhiteListingServiceTargets(context));
+
+        final AccessibilityManager ams = (AccessibilityManager) context.getSystemService(
+                Context.ACCESSIBILITY_SERVICE);
+        final List<String> requiredTargets = ams.getAccessibilityShortcutTargets(shortcutType);
+        targets.removeIf(target -> !requiredTargets.contains(target.getId()));
+
+        return targets;
+    }
+
+    private static List<AccessibilityButtonTarget> getAccessibilityServiceTargets(
+            @NonNull Context context) {
         final AccessibilityManager ams = (AccessibilityManager) context.getSystemService(
                 Context.ACCESSIBILITY_SERVICE);
         final List<AccessibilityServiceInfo> installedServices =
@@ -209,29 +278,73 @@
 
         final List<AccessibilityButtonTarget> targets = new ArrayList<>(installedServices.size());
         for (AccessibilityServiceInfo info : installedServices) {
-            if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) {
-                targets.add(new AccessibilityButtonTarget(context, info));
-            }
-        }
-
-        final List<String> requiredTargets = ams.getAccessibilityShortcutTargets(shortcutType);
-        targets.removeIf(target -> !requiredTargets.contains(target.getId()));
-
-        // TODO(b/146815874): Will replace it with white list services.
-        if (Settings.Secure.getInt(context.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 1) {
-            final AccessibilityButtonTarget magnificationTarget = new AccessibilityButtonTarget(
-                    context,
-                    MAGNIFICATION_COMPONENT_ID,
-                    R.string.accessibility_magnification_chooser_text,
-                    R.drawable.ic_accessibility_magnification,
-                    AccessibilityServiceFragmentType.INTUITIVE);
-            targets.add(magnificationTarget);
+            targets.add(new AccessibilityButtonTarget(context, info));
         }
 
         return targets;
     }
 
+    private static List<AccessibilityButtonTarget> getWhiteListingServiceTargets(
+            @NonNull Context context) {
+        final List<AccessibilityButtonTarget> targets = new ArrayList<>();
+
+        for (int i = 0; i < WHITE_LISTING_FEATURES.length; i++) {
+            final AccessibilityButtonTarget target = new AccessibilityButtonTarget(
+                    context,
+                    WHITE_LISTING_FEATURES[i][COMPONENT_ID],
+                    Integer.parseInt(WHITE_LISTING_FEATURES[i][LABEL_ID]),
+                    Integer.parseInt(WHITE_LISTING_FEATURES[i][ICON_ID]),
+                    Integer.parseInt(WHITE_LISTING_FEATURES[i][FRAGMENT_TYPE]));
+            targets.add(target);
+        }
+
+        return targets;
+    }
+
+    private static boolean isWhiteListingServiceEnabled(@NonNull Context context,
+            AccessibilityButtonTarget target) {
+
+        for (int i = 0; i < WHITE_LISTING_FEATURES.length; i++) {
+            if (WHITE_LISTING_FEATURES[i][COMPONENT_ID].equals(target.getId())) {
+                return Settings.Secure.getInt(context.getContentResolver(),
+                        WHITE_LISTING_FEATURES[i][SETTINGS_KEY],
+                        /* settingsValueOff */ 0) == /* settingsValueOn */ 1;
+            }
+        }
+
+        return false;
+    }
+
+    private static boolean isWhiteListingService(String componentId) {
+        for (int i = 0; i < WHITE_LISTING_FEATURES.length; i++) {
+            if (WHITE_LISTING_FEATURES[i][COMPONENT_ID].equals(componentId)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private void disableWhiteListingService(String componentId) {
+        for (int i = 0; i < WHITE_LISTING_FEATURES.length; i++) {
+            if (WHITE_LISTING_FEATURES[i][COMPONENT_ID].equals(componentId)) {
+                Settings.Secure.putInt(getContentResolver(),
+                        WHITE_LISTING_FEATURES[i][SETTINGS_KEY], /* settingsValueOn */ 1);
+                return;
+            }
+        }
+    }
+
+    private void disableService(ComponentName componentName) {
+        final String componentId = componentName.flattenToString();
+
+        if (isWhiteListingService(componentId)) {
+            disableWhiteListingService(componentName.flattenToString());
+        } else {
+            setAccessibilityServiceState(this, componentName, /* enabled= */ false);
+        }
+    }
+
     private static class ViewHolder {
         ImageView mIconView;
         TextView mLabelView;
@@ -350,11 +463,14 @@
         private void updateIntuitiveActionItemVisibility(@NonNull Context context,
                 @NonNull ViewHolder holder, AccessibilityButtonTarget target) {
             final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT);
+            final boolean isServiceEnabled = isWhiteListingService(target.getId())
+                    ? isWhiteListingServiceEnabled(context, target)
+                    : isAccessibilityServiceEnabled(context, target);
 
             holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
             holder.mViewItem.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
             holder.mSwitchItem.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE);
-            holder.mSwitchItem.setChecked(!isEditMenuMode && isServiceEnabled(context, target));
+            holder.mSwitchItem.setChecked(!isEditMenuMode && isServiceEnabled);
             holder.mItemContainer.setVisibility(View.VISIBLE);
         }
 
@@ -411,7 +527,7 @@
         }
     }
 
-    private static boolean isServiceEnabled(@NonNull Context context,
+    private static boolean isAccessibilityServiceEnabled(@NonNull Context context,
             AccessibilityButtonTarget target) {
         final AccessibilityManager ams = (AccessibilityManager) context.getSystemService(
                 Context.ACCESSIBILITY_SERVICE);
@@ -470,14 +586,14 @@
 
             if (!hasValueInSettings(this,
                     ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE, componentName)) {
-                setAccessibilityServiceState(this, componentName, /* enabled= */ false);
+                disableService(componentName);
             }
         } else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
             optOutValueFromSettings(this, ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE, componentName);
 
             if (!hasValueInSettings(this,
                     ACCESSIBILITY_BUTTON_USER_TYPE, componentName)) {
-                setAccessibilityServiceState(this, componentName, /* enabled= */ false);
+                disableService(componentName);
             }
         } else {
             throw new IllegalArgumentException("Unsupported shortcut type:" + mShortcutType);
diff --git a/core/java/com/android/internal/app/BlockedAppActivity.java b/core/java/com/android/internal/app/BlockedAppActivity.java
new file mode 100644
index 0000000..fbdbbfb
--- /dev/null
+++ b/core/java/com/android/internal/app/BlockedAppActivity.java
@@ -0,0 +1,86 @@
+/*
+ * 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.internal.app;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.R;
+
+/**
+ * A dialog shown to the user when they try to launch an app that is not allowed in lock task
+ * mode. The intent to start this activity must be created with the static factory method provided
+ * below.
+ */
+public class BlockedAppActivity extends AlertActivity {
+
+    private static final String TAG = "BlockedAppActivity";
+    private static final String PACKAGE_NAME = "com.android.internal.app";
+    private static final String EXTRA_BLOCKED_PACKAGE = PACKAGE_NAME + ".extra.BLOCKED_PACKAGE";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent intent = getIntent();
+        int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, /* defaultValue= */ -1);
+        if (userId < 0) {
+            Slog.wtf(TAG, "Invalid user: " + userId);
+            finish();
+            return;
+        }
+
+        String packageName = intent.getStringExtra(EXTRA_BLOCKED_PACKAGE);
+        if (TextUtils.isEmpty(packageName)) {
+            Slog.wtf(TAG, "Invalid package: " + packageName);
+            finish();
+            return;
+        }
+
+        CharSequence appLabel = getAppLabel(userId, packageName);
+
+        mAlertParams.mTitle = getString(R.string.app_blocked_title);
+        mAlertParams.mMessage = getString(R.string.app_blocked_message, appLabel);
+        mAlertParams.mPositiveButtonText = getString(android.R.string.ok);
+        setupAlert();
+    }
+
+    private CharSequence getAppLabel(int userId, String packageName) {
+        PackageManager pm = getPackageManager();
+        try {
+            ApplicationInfo aInfo =
+                    pm.getApplicationInfoAsUser(packageName, /* flags= */ 0, userId);
+            return aInfo.loadLabel(pm);
+        } catch (PackageManager.NameNotFoundException ne) {
+            Slog.e(TAG, "Package " + packageName + " not found", ne);
+        }
+        return packageName;
+    }
+
+
+    /** Creates an intent that launches {@link BlockedAppActivity}. */
+    public static Intent createIntent(int userId, String packageName) {
+        return new Intent()
+                .setClassName("android", BlockedAppActivity.class.getName())
+                .putExtra(Intent.EXTRA_USER_ID, userId)
+                .putExtra(EXTRA_BLOCKED_PACKAGE, packageName);
+    }
+}
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 59dab0c8..649e5d2 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -40,10 +40,15 @@
 
 static const bool kDebugDispatchCycle = false;
 
+static const char* toString(bool value) {
+    return value ? "true" : "false";
+}
+
 static struct {
     jclass clazz;
 
     jmethodID dispatchInputEvent;
+    jmethodID onFocusEvent;
     jmethodID dispatchBatchedInputEventPending;
 } gInputEventReceiverClassInfo;
 
@@ -219,8 +224,7 @@
         bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
     if (kDebugDispatchCycle) {
         ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%" PRId64,
-                getInputChannelName().c_str(),
-                consumeBatches ? "true" : "false", frameTime);
+              getInputChannelName().c_str(), toString(consumeBatches), frameTime);
     }
 
     if (consumeBatches) {
@@ -235,6 +239,7 @@
     for (;;) {
         uint32_t seq;
         InputEvent* inputEvent;
+
         status_t status = mInputConsumer.consume(&mInputEventFactory,
                 consumeBatches, frameTime, &seq, &inputEvent);
         if (status) {
@@ -302,6 +307,19 @@
                 inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
                 break;
             }
+            case AINPUT_EVENT_TYPE_FOCUS: {
+                FocusEvent* focusEvent = static_cast<FocusEvent*>(inputEvent);
+                if (kDebugDispatchCycle) {
+                    ALOGD("channel '%s' ~ Received focus event: hasFocus=%s, inTouchMode=%s.",
+                          getInputChannelName().c_str(), toString(focusEvent->getHasFocus()),
+                          toString(focusEvent->getInTouchMode()));
+                }
+                env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.onFocusEvent,
+                                    jboolean(focusEvent->getHasFocus()),
+                                    jboolean(focusEvent->getInTouchMode()));
+                finishInputEvent(seq, true /* handled */);
+                return OK;
+            }
 
             default:
                 assert(false); // InputConsumer should prevent this from ever happening
@@ -421,6 +439,8 @@
     gInputEventReceiverClassInfo.dispatchInputEvent = GetMethodIDOrDie(env,
             gInputEventReceiverClassInfo.clazz,
             "dispatchInputEvent", "(ILandroid/view/InputEvent;)V");
+    gInputEventReceiverClassInfo.onFocusEvent =
+            GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onFocusEvent", "(ZZ)V");
     gInputEventReceiverClassInfo.dispatchBatchedInputEventPending = GetMethodIDOrDie(env,
             gInputEventReceiverClassInfo.clazz, "dispatchBatchedInputEventPending", "()V");
 
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 673772a..39ea45a 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1431,7 +1431,7 @@
   // Isolated process / webview / app zygote should be gated by SELinux and file permission
   // so they can't even traverse CE / DE directories.
   if (pkg_data_info_list != nullptr
-      && GetBoolProperty(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, false)) {
+      && GetBoolProperty(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true)) {
     isolateAppData(env, pkg_data_info_list, uid, process_name, managed_nice_name,
         fail_fn);
   }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7ecd9b7..2665e8a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4976,6 +4976,12 @@
                 android:process=":ui">
         </activity>
 
+        <activity android:name="com.android.internal.app.BlockedAppActivity"
+                android:theme="@style/Theme.Dialog.Confirmation"
+                android:excludeFromRecents="true"
+                android:process=":ui">
+        </activity>
+
         <activity android:name="com.android.settings.notification.NotificationAccessConfirmationActivity"
                   android:theme="@style/Theme.Dialog.Confirmation"
                   android:excludeFromRecents="true">
diff --git a/core/res/res/drawable/ic_accessibility_color_correction.xml b/core/res/res/drawable/ic_accessibility_color_correction.xml
new file mode 100644
index 0000000..02fa4b8
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_color_correction.xml
@@ -0,0 +1,37 @@
+<vector android:height="24dp" android:viewportHeight="192"
+    android:viewportWidth="192" android:width="24dp"
+    xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#00BCD4" android:pathData="M37.14,173.74L8.61,83.77c-1.72,-5.75 0.26,-11.97 4.97,-15.63L87.15,11c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c4.63,3.7 6.53,9.88 4.8,15.57l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84C44.53,184 38.97,179.83 37.14,173.74z"/>
+    <path android:fillAlpha="0.2" android:fillColor="#FFFFFF"
+        android:pathData="M13.58,69.14L87.15,12c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c3.36,2.68 5.27,6.67 5.41,10.82c0.15,-4.51 -1.79,-8.93 -5.41,-11.82l-73.81,-58.94C99.61,7.01 92.35,6.96 87.15,11L13.58,68.14c-3.71,2.88 -5.72,7.36 -5.56,11.94C8.17,75.85 10.14,71.81 13.58,69.14z" android:strokeAlpha="0.2"/>
+    <path android:fillAlpha="0.2" android:fillColor="#263238"
+        android:pathData="M183.35,84.63l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84c-6.31,0 -11.87,-4.17 -13.7,-10.26L8.61,82.77c-0.36,-1.22 -0.55,-2.46 -0.59,-3.69c-0.06,1.56 0.13,3.14 0.59,4.69l28.53,89.97c1.83,6.09 7.39,10.26 13.7,10.26h90.37c6.28,0 11.82,-4.13 13.67,-10.19l28.48,-88.18c0.48,-1.57 0.67,-3.17 0.61,-4.75C183.92,82.13 183.73,83.39 183.35,84.63z" android:strokeAlpha="0.2"/>
+    <path android:pathData="M60.01,135L60.01,135H60l48.04,49h33.17c6.28,0 11.82,-4.13 13.67,-10.19l18.68,-57.84L129.51,72.2l-0.01,0c0.27,0.27 0.52,0.5 0.77,0.77l0.55,0.55c1.57,1.56 1.57,4.08 -0.04,5.68l-12.56,12.49l6.36,6.3l1.38,1.37l-5.67,5.64l-5.71,-5.68L79.15,135H60.42H60.01z">
+        <aapt:attr name="android:fillColor">
+            <gradient android:endX="155.9627" android:endY="165.1493"
+                android:startX="98.4649" android:startY="107.6516" android:type="linear">
+                <item android:color="#19263238" android:offset="0"/>
+                <item android:color="#00212121" android:offset="1"/>
+            </gradient>
+        </aapt:attr>
+    </path>
+    <path android:fillColor="#0097A7" android:pathData="M68.55,120.35l32.173,-32.173l7.658,7.658l-32.173,32.173z"/>
+    <path android:fillColor="#FFFFFF" android:pathData="M130.83,73.52l-9.42,-9.36c-1.57,-1.56 -4.1,-1.56 -5.67,0l-12.56,12.48L95.42,69l-5.67,5.64l5.71,5.68L60,116.97V135h19.15l35.42,-35.68l5.71,5.68l5.67,-5.64l-7.73,-7.68l12.56,-12.48C132.4,77.6 132.4,75.08 130.83,73.52zM74.98,126.77l-6.43,-6.43l32.17,-32.17l6.43,6.43L74.98,126.77z"/>
+    <path android:fillAlpha="0.1" android:fillColor="#263238"
+        android:pathData="M120.28,105l-5.71,-5.68l-35.42,35.68l-19.15,0l1,1l19.15,0l35.42,-35.68l5.71,5.68l5.68,-5.64l-1,-1z" android:strokeAlpha="0.1"/>
+    <path android:fillAlpha="0.1" android:fillColor="#263238"
+        android:pathData="M90.75,75.64l-0.01,0l4.71,4.68l0.01,0z" android:strokeAlpha="0.1"/>
+    <path android:fillAlpha="0.1" android:fillColor="#263238"
+        android:pathData="M131.83,74.52l-0.97,-0.97c1.54,1.56 1.53,4.06 -0.07,5.65l-12.56,12.48l1,1l12.55,-12.48C133.4,78.6 133.4,76.08 131.83,74.52z" android:strokeAlpha="0.1"/>
+    <path android:fillAlpha="0.1" android:fillColor="#263238"
+        android:pathData="M101.72,89.17l6.67,6.66l0,0l-7.67,-7.66l-32.17,32.17l1,1z" android:strokeAlpha="0.1"/>
+    <path android:pathData="M37.13,173.7L8.62,83.69c-1.7,-5.8 0.3,-12 5,-15.6l73.52,-57.1c5.2,-4 12.5,-4 17.6,0.1l73.82,58.9c4.6,3.7 6.5,9.9 4.8,15.6l-28.51,88.21c-1.8,6.1 -7.4,10.2 -13.7,10.2H50.83C44.53,184 38.93,179.8 37.13,173.7z">
+        <aapt:attr name="android:fillColor">
+            <gradient android:centerX="21.977" android:centerY="23.8809"
+                android:gradientRadius="158.0384" android:type="radial">
+                <item android:color="#19FFFFFF" android:offset="0"/>
+                <item android:color="#00FFFFFF" android:offset="1"/>
+            </gradient>
+        </aapt:attr>
+    </path>
+</vector>
diff --git a/core/res/res/drawable/ic_accessibility_color_inversion.xml b/core/res/res/drawable/ic_accessibility_color_inversion.xml
new file mode 100644
index 0000000..97b30b0
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_color_inversion.xml
@@ -0,0 +1,47 @@
+<vector android:height="24dp" android:viewportHeight="192"
+    android:viewportWidth="192" android:width="24dp"
+    xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#546E7A" android:pathData="M37.14,173.74L8.61,83.77c-1.72,-5.75 0.26,-11.97 4.97,-15.63L87.15,11c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c4.63,3.7 6.53,9.88 4.8,15.57l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84C44.53,184 38.97,179.83 37.14,173.74z"/>
+    <path android:fillAlpha="0.2" android:fillColor="#263238"
+        android:pathData="M183.37,84.63l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84c-6.31,0 -11.87,-4.17 -13.7,-10.26L8.61,82.77c-0.36,-1.22 -0.55,-2.46 -0.59,-3.69c-0.06,1.56 0.13,3.14 0.59,4.69l28.53,89.97c1.83,6.09 7.39,10.26 13.7,10.26h90.38c6.28,0 11.82,-4.13 13.67,-10.19l28.48,-88.18c0.48,-1.57 0.67,-3.17 0.61,-4.75C183.94,82.13 183.74,83.39 183.37,84.63z" android:strokeAlpha="0.2"/>
+    <path android:fillAlpha="0.2" android:fillColor="#FFFFFF"
+        android:pathData="M13.58,69.14L87.15,12c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c3.36,2.68 5.27,6.67 5.41,10.82c0.15,-4.51 -1.79,-8.93 -5.41,-11.82l-73.81,-58.94C99.61,7.01 92.35,6.96 87.15,11L13.58,68.14c-3.71,2.88 -5.72,7.36 -5.56,11.94C8.17,75.85 10.14,71.81 13.58,69.14z" android:strokeAlpha="0.2"/>
+    <path android:fillAlpha="0.2" android:fillColor="#FFFFFF"
+        android:pathData="M53,130.05l5.03,-4.79L44.16,112c-0.05,0.78 -0.14,1.52 -0.15,2.34C43.62,136.61 61.27,154.56 84,156v-5.31C70.13,149.64 58.56,141.54 53,130.05z" android:strokeAlpha="0.2"/>
+    <path android:fillAlpha="0.2" android:fillColor="#FFFFFF"
+        android:pathData="M109,52v5.31c13.65,1.05 24.67,9.15 30.11,20.64l-4.92,4.79L147.81,96c0.09,-0.78 0.17,-1.53 0.19,-2.34C148.38,71.39 131.36,53.44 109,52z" android:strokeAlpha="0.2"/>
+    <path android:pathData="M154.89,173.81l13.57,-42.02l-46.53,-46.56C125.75,90.5 128,96.98 128,104c0,17.7 -14.3,32 -32,32c-8.64,0 -16.47,-3.42 -22.22,-8.97l0.9,0.91L130.73,184h10.49C147.5,184 153.04,179.87 154.89,173.81z">
+        <aapt:attr name="android:fillColor">
+            <gradient android:endX="153.3523" android:endY="161.6371"
+                android:startX="90.6075" android:startY="98.8923" android:type="linear">
+                <item android:color="#33263238" android:offset="0"/>
+                <item android:color="#05263238" android:offset="1"/>
+            </gradient>
+        </aapt:attr>
+    </path>
+    <path android:pathData="M96,129.6V78.4c-14.11,0 -25.6,11.49 -25.6,25.6S81.89,129.6 96,129.6z">
+        <aapt:attr name="android:fillColor">
+            <gradient android:endX="150.8492" android:endY="164.1401"
+                android:startX="88.1044" android:startY="101.3954" android:type="linear">
+                <item android:color="#33263238" android:offset="0"/>
+                <item android:color="#05263238" android:offset="1"/>
+            </gradient>
+        </aapt:attr>
+    </path>
+    <path android:fillAlpha="0.2" android:fillColor="#263238"
+        android:pathData="M96,136c-17.53,0 -31.72,-14.04 -31.99,-31.5c0,0.17 -0.01,0.33 -0.01,0.5c0,17.7 14.3,32 32,32s32,-14.3 32,-32c0,-0.17 -0.01,-0.33 -0.01,-0.5C127.72,121.96 113.53,136 96,136z" android:strokeAlpha="0.2"/>
+    <path android:fillAlpha="0.2" android:fillColor="#263238"
+        android:pathData="M70.4,104c0,0.17 0.01,0.33 0.01,0.5C70.68,90.62 82.06,79.4 96,79.4v-1C81.89,78.4 70.4,89.88 70.4,104z" android:strokeAlpha="0.2"/>
+    <path android:fillAlpha="0.2" android:fillColor="#FFFFFF"
+        android:pathData="M96,72c-17.7,0 -32,14.3 -32,32s14.3,32 32,32s32,-14.3 32,-32S113.7,72 96,72zM70.4,104c0,-14.11 11.49,-25.6 25.6,-25.6v51.2C81.89,129.6 70.4,118.11 70.4,104z" android:strokeAlpha="0.2"/>
+    <path android:fillColor="#FFFFFF" android:pathData="M96,72c-17.7,0 -32,14.3 -32,32s14.3,32 32,32s32,-14.3 32,-32S113.7,72 96,72zM70.4,104c0,-14.11 11.49,-25.6 25.6,-25.6v51.2C81.89,129.6 70.4,118.11 70.4,104z"/>
+    <path android:pathData="M37.14,173.74L8.61,83.77c-1.72,-5.75 0.26,-11.97 4.97,-15.63L87.15,11c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c4.63,3.7 6.53,9.88 4.8,15.57l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84C44.53,184 38.97,179.83 37.14,173.74z">
+        <aapt:attr name="android:fillColor">
+            <gradient android:endX="156.2451" android:endY="171.4516"
+                android:startX="37.0633" android:startY="52.269802" android:type="linear">
+                <item android:color="#19FFFFFF" android:offset="0"/>
+                <item android:color="#00FFFFFF" android:offset="1"/>
+            </gradient>
+        </aapt:attr>
+    </path>
+</vector>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9c08728..4475415 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3752,6 +3752,8 @@
              </p>
          -->
         <attr name="canRequestFingerprintGestures" format="boolean" />
+        <!-- Attribute whether the accessibility service wants to be able to take screenshot. -->
+        <attr name="canTakeScreenshot" format="boolean" />
 
         <!-- Animated image of the accessibility service purpose or behavior, to help users
              understand how the service can help them.-->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index bf7558c..d437aa1 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -777,4 +777,9 @@
     <!-- Assistant handles -->
     <dimen name="assist_handle_shadow_radius">2dp</dimen>
 
+    <!-- For Waterfall Display -->
+    <dimen name="waterfall_display_left_edge_size">0px</dimen>
+    <dimen name="waterfall_display_top_edge_size">0px</dimen>
+    <dimen name="waterfall_display_right_edge_size">0px</dimen>
+    <dimen name="waterfall_display_bottom_edge_size">0px</dimen>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 6cf6a68..9860830 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3007,6 +3007,7 @@
       <public name="featureId" />
       <public name="supportsInlineSuggestions" />
       <public name="crossProfile" />
+      <public name="canTakeScreenshot"/>
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a0e4064..f977ea8 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -794,6 +794,11 @@
     <string name="capability_desc_canCaptureFingerprintGestures">Can capture gestures performed on
         the device\'s fingerprint sensor.</string>
 
+    <!-- Title for the capability of an accessibility service to take screenshot. [CHAR LIMIT=32] -->
+    <string name="capability_title_canTakeScreenshot">Take screenshot</string>
+    <!-- Description for the capability of an accessibility service to take screenshot. [CHAR LIMIT=NONE] -->
+    <string name="capability_desc_canTakeScreenshot">Can take a screenshot of the display.</string>
+
     <!--  Permissions -->
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -4922,6 +4927,13 @@
     <!-- Title for button to turn on work profile. [CHAR LIMIT=NONE] -->
     <string name="work_mode_turn_on">Turn on</string>
 
+    <!-- Title of the dialog that is shown when the user tries to launch a suspended application [CHAR LIMIT=50] -->
+    <string name="app_blocked_title">App is not available</string>
+    <!-- Default message shown in the dialog that is shown when the user tries to launch a suspended application [CHAR LIMIT=NONE] -->
+    <string name="app_blocked_message">
+        <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> is not available right now.
+    </string>
+
     <!-- Message displayed in dialog when app is too old to run on this verison of android. [CHAR LIMIT=NONE] -->
     <string name="deprecated_target_sdk_message">This app was built for an older version of Android and may not work properly. Try checking for updates, or contact the developer.</string>
     <!-- Title for button to see application detail in app store which it came from - it may allow user to update to newer version. [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9e11749..30dbfc7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3036,6 +3036,9 @@
   <java-symbol type="string" name="app_suspended_more_details" />
   <java-symbol type="string" name="app_suspended_default_message" />
 
+  <java-symbol type="string" name="app_blocked_title" />
+  <java-symbol type="string" name="app_blocked_message" />
+
   <!-- Used internally for assistant to launch activity transitions -->
   <java-symbol type="id" name="cross_task_transition" />
 
@@ -3214,6 +3217,8 @@
   <java-symbol type="string" name="edit_accessibility_shortcut_menu_button" />
   <java-symbol type="string" name="cancel_accessibility_shortcut_menu_button" />
 
+  <java-symbol type="drawable" name="ic_accessibility_color_inversion" />
+  <java-symbol type="drawable" name="ic_accessibility_color_correction" />
   <java-symbol type="drawable" name="ic_accessibility_magnification" />
 
   <java-symbol type="drawable" name="ic_delete_item" />
@@ -3801,5 +3806,14 @@
   <!-- Assistant handles -->
   <java-symbol type="dimen" name="assist_handle_shadow_radius" />
 
+  <!-- For Waterfall Display -->
+  <java-symbol type="dimen" name="waterfall_display_left_edge_size" />
+  <java-symbol type="dimen" name="waterfall_display_top_edge_size" />
+  <java-symbol type="dimen" name="waterfall_display_right_edge_size" />
+  <java-symbol type="dimen" name="waterfall_display_bottom_edge_size" />
+
+  <!-- Accessibility take screenshot -->
+  <java-symbol type="string" name="capability_desc_canTakeScreenshot" />
+  <java-symbol type="string" name="capability_title_canTakeScreenshot" />
 
 </resources>
diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchSchemaTest.java b/core/tests/coretests/src/android/app/appsearch/AppSearchSchemaTest.java
new file mode 100644
index 0000000..0be52c1
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/AppSearchSchemaTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.app.appsearch;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.expectThrows;
+
+import android.app.appsearch.AppSearchSchema.IndexingConfig;
+import android.app.appsearch.AppSearchSchema.PropertyConfig;
+
+import androidx.test.filters.SmallTest;
+
+import com.google.android.icing.proto.IndexingConfig.TokenizerType;
+import com.google.android.icing.proto.PropertyConfigProto;
+import com.google.android.icing.proto.SchemaProto;
+import com.google.android.icing.proto.SchemaTypeConfigProto;
+import com.google.android.icing.proto.TermMatchType;
+
+import org.junit.Test;
+
+@SmallTest
+public class AppSearchSchemaTest {
+    @Test
+    public void testSuccess() {
+        AppSearchSchema schema = AppSearchSchema.newBuilder()
+                .addType(AppSearchSchema.newSchemaTypeBuilder("Email")
+                        .addProperty(AppSearchSchema.newPropertyBuilder("subject")
+                                .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                                .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                .setIndexingConfig(AppSearchSchema.newIndexingConfigBuilder()
+                                        .setTokenizerType(IndexingConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setTermMatchType(IndexingConfig.TERM_MATCH_TYPE_PREFIX)
+                                        .build()
+                                ).build()
+                        ).addProperty(AppSearchSchema.newPropertyBuilder("body")
+                                .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                                .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                .setIndexingConfig(AppSearchSchema.newIndexingConfigBuilder()
+                                        .setTokenizerType(IndexingConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setTermMatchType(IndexingConfig.TERM_MATCH_TYPE_PREFIX)
+                                        .build()
+                                ).build()
+                        ).build()
+
+                ).addType(AppSearchSchema.newSchemaTypeBuilder("MusicRecording")
+                        .addProperty(AppSearchSchema.newPropertyBuilder("artist")
+                                .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                                .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+                                .setIndexingConfig(AppSearchSchema.newIndexingConfigBuilder()
+                                        .setTokenizerType(IndexingConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setTermMatchType(IndexingConfig.TERM_MATCH_TYPE_PREFIX)
+                                        .build()
+                                ).build()
+                        ).addProperty(AppSearchSchema.newPropertyBuilder("pubDate")
+                                .setDataType(PropertyConfig.DATA_TYPE_INT64)
+                                .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                .setIndexingConfig(AppSearchSchema.newIndexingConfigBuilder()
+                                        .setTokenizerType(IndexingConfig.TOKENIZER_TYPE_NONE)
+                                        .setTermMatchType(IndexingConfig.TERM_MATCH_TYPE_UNKNOWN)
+                                        .build()
+                                ).build()
+                        ).build()
+                ).build();
+
+        SchemaProto expectedProto = SchemaProto.newBuilder()
+                .addTypes(SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("Email")
+                        .addProperties(PropertyConfigProto.newBuilder()
+                                .setPropertyName("subject")
+                                .setDataType(PropertyConfigProto.DataType.Code.STRING)
+                                .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
+                                .setIndexingConfig(
+                                        com.google.android.icing.proto.IndexingConfig.newBuilder()
+                                                .setTokenizerType(TokenizerType.Code.PLAIN)
+                                                .setTermMatchType(TermMatchType.Code.PREFIX)
+                                )
+                        ).addProperties(PropertyConfigProto.newBuilder()
+                                .setPropertyName("body")
+                                .setDataType(PropertyConfigProto.DataType.Code.STRING)
+                                .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
+                                .setIndexingConfig(
+                                        com.google.android.icing.proto.IndexingConfig.newBuilder()
+                                                .setTokenizerType(TokenizerType.Code.PLAIN)
+                                                .setTermMatchType(TermMatchType.Code.PREFIX)
+                                )
+                        )
+
+                ).addTypes(SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("MusicRecording")
+                        .addProperties(PropertyConfigProto.newBuilder()
+                                .setPropertyName("artist")
+                                .setDataType(PropertyConfigProto.DataType.Code.STRING)
+                                .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED)
+                                .setIndexingConfig(
+                                        com.google.android.icing.proto.IndexingConfig.newBuilder()
+                                                .setTokenizerType(TokenizerType.Code.PLAIN)
+                                                .setTermMatchType(TermMatchType.Code.PREFIX)
+                                )
+                        ).addProperties(PropertyConfigProto.newBuilder()
+                                .setPropertyName("pubDate")
+                                .setDataType(PropertyConfigProto.DataType.Code.INT64)
+                                .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
+                                .setIndexingConfig(
+                                        com.google.android.icing.proto.IndexingConfig.newBuilder()
+                                                .setTokenizerType(TokenizerType.Code.NONE)
+                                                .setTermMatchType(TermMatchType.Code.UNKNOWN)
+                                )
+                        )
+                ).build();
+
+        assertThat(schema.getProto()).isEqualTo(expectedProto);
+    }
+
+    @Test
+    public void testInvalidEnums() {
+        PropertyConfig.Builder builder = AppSearchSchema.newPropertyBuilder("test");
+        assertThrows(IllegalArgumentException.class, () -> builder.setDataType(99));
+        assertThrows(IllegalArgumentException.class, () -> builder.setCardinality(99));
+    }
+
+    @Test
+    public void testMissingFields() {
+        PropertyConfig.Builder builder = AppSearchSchema.newPropertyBuilder("test");
+        Exception e = expectThrows(IllegalSchemaException.class, builder::build);
+        assertThat(e).hasMessageThat().contains("Missing field: dataType");
+
+        builder.setDataType(PropertyConfig.DATA_TYPE_DOCUMENT);
+        e = expectThrows(IllegalSchemaException.class, builder::build);
+        assertThat(e).hasMessageThat().contains("Missing field: schemaType");
+
+        builder.setSchemaType("TestType");
+        e = expectThrows(IllegalSchemaException.class, builder::build);
+        assertThat(e).hasMessageThat().contains("Missing field: cardinality");
+
+        builder.setCardinality(PropertyConfig.CARDINALITY_REPEATED);
+        builder.build();
+    }
+}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 1bd52af..ade1e0d 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -46,7 +46,7 @@
     // The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest:
     // See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo,
     // and assertAccessibilityNodeInfoCleared in that class.
-    private static final int NUM_MARSHALLED_PROPERTIES = 35;
+    private static final int NUM_MARSHALLED_PROPERTIES = 38;
 
     /**
      * The number of properties that are purposely not marshalled
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 93f4b51..f151b81 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -19,9 +19,11 @@
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.content.pm.ParceledListSlice;
+import android.graphics.Bitmap;
 import android.graphics.Region;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.RemoteCallback;
 
 /**
  * Stub implementation of IAccessibilityServiceConnection so each test doesn't need to implement
@@ -143,4 +145,14 @@
     public IBinder getOverlayWindowToken(int displayId) {
         return null;
     }
+
+    public int getWindowIdForLeashToken(IBinder token) {
+        return -1;
+    }
+
+    public Bitmap takeScreenshot(int displayId) {
+        return null;
+    }
+
+    public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {}
 }
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index 8c9b4d0..a602fa3 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -32,6 +32,7 @@
 
 import android.app.Activity;
 import android.app.Instrumentation;
+import android.view.InputDevice;
 import android.view.MotionEvent;
 
 import androidx.test.InstrumentationRegistry;
@@ -282,6 +283,35 @@
     }
 
     @Test
+    public void testEditor_onTouchEvent_mouseDrag() throws Throwable {
+        String text = "testEditor_onTouchEvent_mouseDrag";
+        onView(withId(R.id.textview)).perform(replaceText(text));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
+
+        TextView tv = mActivity.findViewById(R.id.textview);
+        Editor editor = tv.getEditorForTesting();
+
+        // Simulate a mouse click and drag. This should NOT trigger a cursor drag.
+        long event1Time = 1001;
+        MotionEvent event1 = mouseDownEvent(event1Time, event1Time, 20f, 30f);
+        mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1));
+        assertFalse(editor.getInsertionController().isCursorBeingModified());
+        assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+        long event2Time = 1002;
+        MotionEvent event2 = mouseMoveEvent(event1Time, event2Time, 120f, 30f);
+        mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2));
+        assertFalse(editor.getInsertionController().isCursorBeingModified());
+        assertTrue(editor.getSelectionController().isCursorBeingModified());
+
+        long event3Time = 1003;
+        MotionEvent event3 = mouseUpEvent(event1Time, event3Time, 120f, 30f);
+        mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3));
+        assertFalse(editor.getInsertionController().isCursorBeingModified());
+        assertFalse(editor.getSelectionController().isCursorBeingModified());
+    }
+
+    @Test
     public void testEditor_onTouchEvent_cursorDrag() throws Throwable {
         String text = "testEditor_onTouchEvent_cursorDrag";
         onView(withId(R.id.textview)).perform(replaceText(text));
@@ -385,4 +415,25 @@
     private static MotionEvent moveEvent(long downTime, long eventTime, float x, float y) {
         return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0);
     }
+
+    private static MotionEvent mouseDownEvent(long downTime, long eventTime, float x, float y) {
+        MotionEvent event = downEvent(downTime, eventTime, x, y);
+        event.setSource(InputDevice.SOURCE_MOUSE);
+        event.setButtonState(MotionEvent.BUTTON_PRIMARY);
+        return event;
+    }
+
+    private static MotionEvent mouseUpEvent(long downTime, long eventTime, float x, float y) {
+        MotionEvent event = upEvent(downTime, eventTime, x, y);
+        event.setSource(InputDevice.SOURCE_MOUSE);
+        event.setButtonState(0);
+        return event;
+    }
+
+    private static MotionEvent mouseMoveEvent(long downTime, long eventTime, float x, float y) {
+        MotionEvent event = moveEvent(downTime, eventTime, x, y);
+        event.setSource(InputDevice.SOURCE_MOUSE);
+        event.setButtonState(MotionEvent.BUTTON_PRIMARY);
+        return event;
+    }
 }
diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
index 215d0b8..3dc001d 100644
--- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
+++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
@@ -48,24 +48,32 @@
     }
 
     @Test
+    public void testIsDistanceWithin() throws Exception {
+        assertTrue(EditorTouchState.isDistanceWithin(0, 0, 0, 0, 8));
+        assertTrue(EditorTouchState.isDistanceWithin(3, 9, 5, 11, 8));
+        assertTrue(EditorTouchState.isDistanceWithin(5, 11, 3, 9, 8));
+        assertFalse(EditorTouchState.isDistanceWithin(5, 10, 5, 20, 8));
+    }
+
+    @Test
     public void testUpdate_singleTap() throws Exception {
         // Simulate an ACTION_DOWN event.
         long event1Time = 1000;
         MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
         mTouchState.update(event1, mConfig);
-        assertSingleTap(mTouchState, 20f, 30f, 0, 0, false);
+        assertSingleTap(mTouchState, 20f, 30f, 0, 0);
 
         // Simulate an ACTION_UP event.
         long event2Time = 1001;
         MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
         mTouchState.update(event2, mConfig);
-        assertSingleTap(mTouchState, 20f, 30f, 20f, 30f, false);
+        assertSingleTap(mTouchState, 20f, 30f, 20f, 30f);
 
         // Generate an ACTION_DOWN event whose time is after the double-tap timeout.
         long event3Time = event2Time + ViewConfiguration.getDoubleTapTimeout() + 1;
         MotionEvent event3 = downEvent(event3Time, event3Time, 22f, 33f);
         mTouchState.update(event3, mConfig);
-        assertSingleTap(mTouchState, 22f, 33f, 20f, 30f, false);
+        assertSingleTap(mTouchState, 22f, 33f, 20f, 30f);
     }
 
     @Test
@@ -74,13 +82,13 @@
         long event1Time = 1000;
         MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
         mTouchState.update(event1, mConfig);
-        assertSingleTap(mTouchState, 20f, 30f, 0, 0, false);
+        assertSingleTap(mTouchState, 20f, 30f, 0, 0);
 
         // Simulate an ACTION_UP event.
         long event2Time = 1001;
         MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
         mTouchState.update(event2, mConfig);
-        assertSingleTap(mTouchState, 20f, 30f, 20f, 30f, false);
+        assertSingleTap(mTouchState, 20f, 30f, 20f, 30f);
 
         // Generate an ACTION_DOWN event whose time is within the double-tap timeout.
         long event3Time = 1002;
@@ -96,13 +104,13 @@
         long event1Time = 1000;
         MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
         mTouchState.update(event1, mConfig);
-        assertSingleTap(mTouchState, 20f, 30f, 0, 0, false);
+        assertSingleTap(mTouchState, 20f, 30f, 0, 0);
 
         // Simulate an ACTION_UP event.
         long event2Time = 1001;
         MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
         mTouchState.update(event2, mConfig);
-        assertSingleTap(mTouchState, 20f, 30f, 20f, 30f, false);
+        assertSingleTap(mTouchState, 20f, 30f, 20f, 30f);
 
         // Generate an ACTION_DOWN event whose time is within the double-tap timeout.
         long event3Time = 1002;
@@ -125,13 +133,13 @@
         long event1Time = 1000;
         MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
         mTouchState.update(event1, mConfig);
-        assertSingleTap(mTouchState, 20f, 30f, 0, 0, false);
+        assertSingleTap(mTouchState, 20f, 30f, 0, 0);
 
         // Simulate an ACTION_UP event with a delay that's longer than the double-tap timeout.
         long event2Time = 1000 + ViewConfiguration.getDoubleTapTimeout() + 1;
         MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
         mTouchState.update(event2, mConfig);
-        assertSingleTap(mTouchState, 20f, 30f, 20f, 30f, false);
+        assertSingleTap(mTouchState, 20f, 30f, 20f, 30f);
 
         // Generate an ACTION_DOWN event whose time is within the double-tap timeout when
         // calculated from the last ACTION_UP event time. Even though the time between the last up
@@ -140,7 +148,7 @@
         long event3Time = event2Time + 1;
         MotionEvent event3 = downEvent(event3Time, event3Time, 22f, 33f);
         mTouchState.update(event3, mConfig);
-        assertSingleTap(mTouchState, 22f, 33f, 20f, 30f, false);
+        assertSingleTap(mTouchState, 22f, 33f, 20f, 30f);
     }
 
     @Test
@@ -149,19 +157,19 @@
         long event1Time = 1000;
         MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
         mTouchState.update(event1, mConfig);
-        assertSingleTap(mTouchState, 20f, 30f, 0, 0, false);
+        assertSingleTap(mTouchState, 20f, 30f, 0, 0);
 
         // Simulate an ACTION_MOVE event.
         long event2Time = 1001;
         MotionEvent event2 = moveEvent(event1Time, event2Time, 200f, 31f);
         mTouchState.update(event2, mConfig);
-        assertSingleTap(mTouchState, 20f, 30f, 0, 0, true);
+        assertDrag(mTouchState, 20f, 30f, 0, 0, false);
 
         // Simulate an ACTION_UP event with a delay that's longer than the double-tap timeout.
         long event3Time = 5000;
         MotionEvent event3 = upEvent(event1Time, event3Time, 200f, 31f);
         mTouchState.update(event3, mConfig);
-        assertSingleTap(mTouchState, 20f, 30f, 200f, 31f, false);
+        assertSingleTap(mTouchState, 20f, 30f, 200f, 31f);
 
         // Generate an ACTION_DOWN event whose time is within the double-tap timeout when
         // calculated from the last ACTION_UP event time. Even though the time between the last up
@@ -170,7 +178,7 @@
         long event4Time = event3Time + 1;
         MotionEvent event4 = downEvent(event4Time, event4Time, 200f, 31f);
         mTouchState.update(event4, mConfig);
-        assertSingleTap(mTouchState, 200f, 31f, 200f, 31f, false);
+        assertSingleTap(mTouchState, 200f, 31f, 200f, 31f);
     }
 
     @Test
@@ -180,14 +188,14 @@
         MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
         event1.setSource(InputDevice.SOURCE_MOUSE);
         mTouchState.update(event1, mConfig);
-        assertSingleTap(mTouchState, 20f, 30f, 0, 0, false);
+        assertSingleTap(mTouchState, 20f, 30f, 0, 0);
 
         // Simulate an ACTION_UP event.
         long event2Time = 1001;
         MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
         event2.setSource(InputDevice.SOURCE_MOUSE);
         mTouchState.update(event2, mConfig);
-        assertSingleTap(mTouchState, 20f, 30f, 20f, 30f, false);
+        assertSingleTap(mTouchState, 20f, 30f, 20f, 30f);
 
         // Generate a second ACTION_DOWN event whose time is within the double-tap timeout.
         long event3Time = 1002;
@@ -220,13 +228,13 @@
         long event1Time = 1000;
         MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
         mTouchState.update(event1, mConfig);
-        assertSingleTap(mTouchState, 20f, 30f, 0, 0, false);
+        assertSingleTap(mTouchState, 20f, 30f, 0, 0);
 
         // Simulate an ACTION_UP event.
         long event2Time = 1001;
         MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
         mTouchState.update(event2, mConfig);
-        assertSingleTap(mTouchState, 20f, 30f, 20f, 30f, false);
+        assertSingleTap(mTouchState, 20f, 30f, 20f, 30f);
 
         // Generate a second ACTION_DOWN event whose time is within the double-tap timeout.
         long event3Time = 1002;
@@ -246,7 +254,7 @@
         long event5Time = 1004;
         MotionEvent event5 = downEvent(event5Time, event5Time, 22f, 32f);
         mTouchState.update(event5, mConfig);
-        assertSingleTap(mTouchState, 22f, 32f, 21f, 31f, false);
+        assertSingleTap(mTouchState, 22f, 32f, 21f, 31f);
     }
 
     @Test
@@ -255,13 +263,13 @@
         long event1Time = 1000;
         MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
         mTouchState.update(event1, mConfig);
-        assertSingleTap(mTouchState, 20f, 30f, 0, 0, false);
+        assertSingleTap(mTouchState, 20f, 30f, 0, 0);
 
         // Simulate an ACTION_MOVE event whose location is not far enough to start a drag.
         long event2Time = 1001;
         MotionEvent event2 = moveEvent(event1Time, event2Time, 21f, 30f);
         mTouchState.update(event2, mConfig);
-        assertSingleTap(mTouchState, 20f, 30f, 0, 0, false);
+        assertSingleTap(mTouchState, 20f, 30f, 0, 0);
 
         // Simulate another ACTION_MOVE event whose location is far enough to start a drag.
         int touchSlop = mConfig.getScaledTouchSlop();
@@ -270,21 +278,135 @@
         long event3Time = 1002;
         MotionEvent event3 = moveEvent(event3Time, event3Time, newX, newY);
         mTouchState.update(event3, mConfig);
-        assertSingleTap(mTouchState, 20f, 30f, 0, 0, true);
+        assertDrag(mTouchState, 20f, 30f, 0, 0, false);
 
         // Simulate an ACTION_UP event.
         long event4Time = 1003;
         MotionEvent event4 = upEvent(event3Time, event4Time, 200f, 300f);
         mTouchState.update(event4, mConfig);
-        assertSingleTap(mTouchState, 20f, 30f, 200f, 300f, false);
+        assertSingleTap(mTouchState, 20f, 30f, 200f, 300f);
     }
 
     @Test
-    public void testIsDistanceWithin() throws Exception {
-        assertTrue(EditorTouchState.isDistanceWithin(0, 0, 0, 0, 8));
-        assertTrue(EditorTouchState.isDistanceWithin(3, 9, 5, 11, 8));
-        assertTrue(EditorTouchState.isDistanceWithin(5, 11, 3, 9, 8));
-        assertFalse(EditorTouchState.isDistanceWithin(5, 10, 5, 20, 8));
+    public void testUpdate_drag_startsCloseToVerticalThenHorizontal() throws Exception {
+        // Simulate an ACTION_DOWN event.
+        long event1Time = 1001;
+        MotionEvent event1 = downEvent(event1Time, event1Time, 0f, 0f);
+        mTouchState.update(event1, mConfig);
+        assertSingleTap(mTouchState, 0f, 0f, 0, 0);
+
+        // Simulate an ACTION_MOVE event that is < 30 deg from vertical.
+        long event2Time = 1002;
+        MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 174f);
+        mTouchState.update(event2, mConfig);
+        assertDrag(mTouchState, 0f, 0f, 0, 0, true);
+
+        // Simulate another ACTION_MOVE event that is horizontal from the original down event.
+        // The value of `isDragCloseToVertical` should NOT change since it should only reflect the
+        // initial direction of movement.
+        long event3Time = 1003;
+        MotionEvent event3 = moveEvent(event1Time, event3Time, 200f, 0f);
+        mTouchState.update(event3, mConfig);
+        assertDrag(mTouchState, 0f, 0f, 0, 0, true);
+
+        // Simulate an ACTION_UP event.
+        long event4Time = 1004;
+        MotionEvent event4 = upEvent(event1Time, event4Time, 200f, 0f);
+        mTouchState.update(event4, mConfig);
+        assertSingleTap(mTouchState, 0f, 0f, 200f, 0f);
+    }
+
+    @Test
+    public void testUpdate_drag_startsHorizontalThenVertical() throws Exception {
+        // Simulate an ACTION_DOWN event.
+        long event1Time = 1001;
+        MotionEvent event1 = downEvent(event1Time, event1Time, 0f, 0f);
+        mTouchState.update(event1, mConfig);
+        assertSingleTap(mTouchState, 0f, 0f, 0, 0);
+
+        // Simulate an ACTION_MOVE event that is > 30 deg from vertical.
+        long event2Time = 1002;
+        MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 173f);
+        mTouchState.update(event2, mConfig);
+        assertDrag(mTouchState, 0f, 0f, 0, 0, false);
+
+        // Simulate another ACTION_MOVE event that is vertical from the original down event.
+        // The value of `isDragCloseToVertical` should NOT change since it should only reflect the
+        // initial direction of movement.
+        long event3Time = 1003;
+        MotionEvent event3 = moveEvent(event1Time, event3Time, 0f, 200f);
+        mTouchState.update(event3, mConfig);
+        assertDrag(mTouchState, 0f, 0f, 0, 0, false);
+
+        // Simulate an ACTION_UP event.
+        long event4Time = 1004;
+        MotionEvent event4 = upEvent(event1Time, event4Time, 0f, 200f);
+        mTouchState.update(event4, mConfig);
+        assertSingleTap(mTouchState, 0f, 0f, 0f, 200f);
+    }
+
+    @Test
+    public void testUpdate_cancelAfterDown() throws Exception {
+        // Simulate an ACTION_DOWN event.
+        long event1Time = 1001;
+        MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+        mTouchState.update(event1, mConfig);
+        assertSingleTap(mTouchState, 20f, 30f, 0, 0);
+
+        // Simulate an ACTION_CANCEL event.
+        long event2Time = 1002;
+        MotionEvent event2 = cancelEvent(event1Time, event2Time, 20f, 30f);
+        mTouchState.update(event2, mConfig);
+        assertSingleTap(mTouchState, 20f, 30f, 0, 0);
+    }
+
+    @Test
+    public void testUpdate_cancelAfterDrag() throws Exception {
+        // Simulate an ACTION_DOWN event.
+        long event1Time = 1001;
+        MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+        mTouchState.update(event1, mConfig);
+        assertSingleTap(mTouchState, 20f, 30f, 0, 0);
+
+        // Simulate another ACTION_MOVE event whose location is far enough to start a drag.
+        long event2Time = 1002;
+        MotionEvent event2 = moveEvent(event2Time, event2Time, 200f, 30f);
+        mTouchState.update(event2, mConfig);
+        assertDrag(mTouchState, 20f, 30f, 0, 0, false);
+
+        // Simulate an ACTION_CANCEL event.
+        long event3Time = 1003;
+        MotionEvent event3 = cancelEvent(event1Time, event3Time, 200f, 30f);
+        mTouchState.update(event3, mConfig);
+        assertSingleTap(mTouchState, 20f, 30f, 0, 0);
+    }
+
+    @Test
+    public void testUpdate_cancelAfterMultitap() throws Exception {
+        // Simulate an ACTION_DOWN event.
+        long event1Time = 1001;
+        MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+        mTouchState.update(event1, mConfig);
+        assertSingleTap(mTouchState, 20f, 30f, 0, 0);
+
+        // Simulate an ACTION_UP event.
+        long event2Time = 1002;
+        MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
+        mTouchState.update(event2, mConfig);
+        assertSingleTap(mTouchState, 20f, 30f, 20f, 30f);
+
+        // Generate an ACTION_DOWN event whose time is within the double-tap timeout.
+        long event3Time = 1003;
+        MotionEvent event3 = downEvent(event3Time, event3Time, 22f, 33f);
+        mTouchState.update(event3, mConfig);
+        assertMultiTap(mTouchState, 22f, 33f, 20f, 30f,
+                MultiTapStatus.DOUBLE_TAP, true);
+
+        // Simulate an ACTION_CANCEL event.
+        long event4Time = 1004;
+        MotionEvent event4 = cancelEvent(event3Time, event4Time, 20f, 30f);
+        mTouchState.update(event4, mConfig);
+        assertSingleTap(mTouchState, 22f, 33f, 20f, 30f);
     }
 
     private static MotionEvent downEvent(long downTime, long eventTime, float x, float y) {
@@ -299,8 +421,12 @@
         return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0);
     }
 
+    private static MotionEvent cancelEvent(long downTime, long eventTime, float x, float y) {
+        return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_CANCEL, x, y, 0);
+    }
+
     private static void assertSingleTap(EditorTouchState touchState, float lastDownX,
-            float lastDownY, float lastUpX, float lastUpY, boolean isMovedEnoughForDrag) {
+            float lastDownY, float lastUpX, float lastUpY) {
         assertThat(touchState.getLastDownX(), is(lastDownX));
         assertThat(touchState.getLastDownY(), is(lastDownY));
         assertThat(touchState.getLastUpX(), is(lastUpX));
@@ -309,7 +435,21 @@
         assertThat(touchState.isTripleClick(), is(false));
         assertThat(touchState.isMultiTap(), is(false));
         assertThat(touchState.isMultiTapInSameArea(), is(false));
-        assertThat(touchState.isMovedEnoughForDrag(), is(isMovedEnoughForDrag));
+        assertThat(touchState.isMovedEnoughForDrag(), is(false));
+    }
+
+    private static void assertDrag(EditorTouchState touchState, float lastDownX,
+            float lastDownY, float lastUpX, float lastUpY, boolean isDragCloseToVertical) {
+        assertThat(touchState.getLastDownX(), is(lastDownX));
+        assertThat(touchState.getLastDownY(), is(lastDownY));
+        assertThat(touchState.getLastUpX(), is(lastUpX));
+        assertThat(touchState.getLastUpY(), is(lastUpY));
+        assertThat(touchState.isDoubleTap(), is(false));
+        assertThat(touchState.isTripleClick(), is(false));
+        assertThat(touchState.isMultiTap(), is(false));
+        assertThat(touchState.isMultiTapInSameArea(), is(false));
+        assertThat(touchState.isMovedEnoughForDrag(), is(true));
+        assertThat(touchState.isDragCloseToVertical(), is(isDragCloseToVertical));
     }
 
     private static void assertMultiTap(EditorTouchState touchState,
@@ -325,5 +465,6 @@
                 || multiTapStatus == MultiTapStatus.TRIPLE_CLICK));
         assertThat(touchState.isMultiTapInSameArea(), is(isMultiTapInSameArea));
         assertThat(touchState.isMovedEnoughForDrag(), is(false));
+        assertThat(touchState.isDragCloseToVertical(), is(false));
     }
 }
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index aa55e08..8bfd0a0 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -47,7 +47,6 @@
 import android.view.textclassifier.TextClassifier;
 
 import androidx.test.filters.MediumTest;
-import androidx.test.filters.Suppress;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -193,7 +192,6 @@
     }
 
     @Test
-    @Suppress // Consistently failing. b/29591177
     public void testDragAndDrop_longClick() {
         final String text = "abc def ghi.";
         onView(withId(R.id.textview)).perform(mouseClick());
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 82854e5..6784ede 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -348,7 +348,7 @@
         verify(mAlertDialog).show();
         verify(mAccessibilityManagerService, atLeastOnce()).getInstalledAccessibilityServiceList(
                 anyInt());
-        verify(mAccessibilityManagerService, times(0)).performAccessibilityShortcut();
+        verify(mAccessibilityManagerService, times(0)).performAccessibilityShortcut(null);
         verify(mFrameworkObjectProvider, times(0)).getTextToSpeech(any(), any());
     }
 
@@ -365,7 +365,7 @@
         assertEquals(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
                 mLayoutParams.privateFlags
                         & WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
-        verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut();
+        verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut(null);
     }
 
     @Test
@@ -433,7 +433,7 @@
 
         verifyZeroInteractions(mAlertDialogBuilder, mAlertDialog);
         verify(mToast).show();
-        verify(mAccessibilityManagerService).performAccessibilityShortcut();
+        verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
     }
 
     @Test
@@ -459,7 +459,7 @@
         when(mServiceInfo.loadSummary(any())).thenReturn(null);
         Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1);
         getController().performAccessibilityShortcut();
-        verify(mAccessibilityManagerService).performAccessibilityShortcut();
+        verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
     }
 
     @Test
@@ -471,7 +471,7 @@
         getController().performAccessibilityShortcut();
 
         verifyZeroInteractions(mToast);
-        verify(mAccessibilityManagerService).performAccessibilityShortcut();
+        verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
     }
 
     @Test
@@ -485,7 +485,7 @@
         getController().performAccessibilityShortcut();
 
         verifyZeroInteractions(mToast);
-        verify(mAccessibilityManagerService).performAccessibilityShortcut();
+        verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
     }
 
     @Test
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index 26a40d3..dfb7a16 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -135,3 +135,10 @@
     src: "com.android.car.floatingcardslauncher.xml",
     filename_from_src: true,
 }
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.car.ui.paintbooth",
+    sub_dir: "permissions",
+    src: "com.android.car.ui.paintbooth.xml",
+    filename_from_src: true,
+}
diff --git a/data/etc/car/com.android.car.ui.paintbooth.xml b/data/etc/car/com.android.car.ui.paintbooth.xml
new file mode 100644
index 0000000..11bf304
--- /dev/null
+++ b/data/etc/car/com.android.car.ui.paintbooth.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+
+    <privapp-permissions package="com.android.car.ui.paintbooth">
+        <!-- For enabling/disabling, and getting list of RROs -->
+        <permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/>
+        <!-- For showing the current active activity -->
+        <permission name="android.permission.REAL_GET_TASKS"/>
+        <!-- For getting list of RROs for current user -->
+        <permission name="android.permission.MANAGE_USERS"/>
+        <!-- For getting list of RROs for current user-->
+        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 9930ea2..ad99ab3 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -245,6 +245,7 @@
         <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
         <permission name="android.permission.TETHER_PRIVILEGED"/>
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
+        <permission name="android.permission.UPDATE_DEVICE_STATS"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.server.telecom">
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 17aacb9..fedde42 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -308,6 +308,9 @@
         if (spec.isStrongBoxBacked()) {
             flags |= KeyStore.FLAG_STRONGBOX;
         }
+        if (spec.isCriticalToDeviceEncryption()) {
+            flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
+        }
         String keyAliasInKeystore = Credentials.USER_PRIVATE_KEY + spec.getKeystoreAlias();
         KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
         boolean success = false;
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 91aac83..c52fd48 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -18,10 +18,8 @@
 
 import android.annotation.Nullable;
 import android.security.Credentials;
-import android.security.GateKeeper;
 import android.security.KeyPairGeneratorSpec;
 import android.security.KeyStore;
-import android.security.KeyStoreException;
 import android.security.keymaster.KeyCharacteristics;
 import android.security.keymaster.KeymasterArguments;
 import android.security.keymaster.KeymasterCertificateChain;
@@ -458,6 +456,9 @@
         if (mSpec.isStrongBoxBacked()) {
             flags |= KeyStore.FLAG_STRONGBOX;
         }
+        if (mSpec.isCriticalToDeviceEncryption()) {
+            flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
+        }
 
         byte[] additionalEntropy =
                 KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 52ff9e0..450dd33 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -271,6 +271,7 @@
     private final boolean mIsStrongBoxBacked;
     private final boolean mUserConfirmationRequired;
     private final boolean mUnlockedDeviceRequired;
+    private final boolean mCriticalToDeviceEncryption;
     /*
      * ***NOTE***: All new fields MUST also be added to the following:
      * ParcelableKeyGenParameterSpec class.
@@ -307,7 +308,8 @@
             boolean invalidatedByBiometricEnrollment,
             boolean isStrongBoxBacked,
             boolean userConfirmationRequired,
-            boolean unlockedDeviceRequired) {
+            boolean unlockedDeviceRequired,
+            boolean criticalToDeviceEncryption) {
         if (TextUtils.isEmpty(keyStoreAlias)) {
             throw new IllegalArgumentException("keyStoreAlias must not be empty");
         }
@@ -357,6 +359,7 @@
         mIsStrongBoxBacked = isStrongBoxBacked;
         mUserConfirmationRequired = userConfirmationRequired;
         mUnlockedDeviceRequired = unlockedDeviceRequired;
+        mCriticalToDeviceEncryption = criticalToDeviceEncryption;
     }
 
     /**
@@ -710,6 +713,16 @@
     }
 
     /**
+     * Return whether this key is critical to the device encryption flow.
+     *
+     * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
+     * @hide
+     */
+    public boolean isCriticalToDeviceEncryption() {
+        return mCriticalToDeviceEncryption;
+    }
+
+    /**
      * Builder of {@link KeyGenParameterSpec} instances.
      */
     public final static class Builder {
@@ -741,6 +754,7 @@
         private boolean mIsStrongBoxBacked = false;
         private boolean mUserConfirmationRequired;
         private boolean mUnlockedDeviceRequired = false;
+        private boolean mCriticalToDeviceEncryption = false;
 
         /**
          * Creates a new instance of the {@code Builder}.
@@ -804,6 +818,7 @@
             mIsStrongBoxBacked = sourceSpec.isStrongBoxBacked();
             mUserConfirmationRequired = sourceSpec.isUserConfirmationRequired();
             mUnlockedDeviceRequired = sourceSpec.isUnlockedDeviceRequired();
+            mCriticalToDeviceEncryption = sourceSpec.isCriticalToDeviceEncryption();
         }
 
         /**
@@ -1339,6 +1354,20 @@
         }
 
         /**
+         * Set whether this key is critical to the device encryption flow
+         *
+         * This is a special flag only available to system servers to indicate the current key
+         * is part of the device encryption flow.
+         *
+         * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
+         * @hide
+         */
+        public Builder setCriticalToDeviceEncryption(boolean critical) {
+            mCriticalToDeviceEncryption = critical;
+            return this;
+        }
+
+        /**
          * Builds an instance of {@code KeyGenParameterSpec}.
          */
         @NonNull
@@ -1370,7 +1399,8 @@
                     mInvalidatedByBiometricEnrollment,
                     mIsStrongBoxBacked,
                     mUserConfirmationRequired,
-                    mUnlockedDeviceRequired);
+                    mUnlockedDeviceRequired,
+                    mCriticalToDeviceEncryption);
         }
     }
 }
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index d8030fb..98e4589 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -16,8 +16,8 @@
 
 package android.security.keystore;
 
-import android.os.Parcelable;
 import android.os.Parcel;
+import android.os.Parcelable;
 
 import java.math.BigInteger;
 import java.security.spec.AlgorithmParameterSpec;
@@ -105,6 +105,7 @@
         out.writeBoolean(mSpec.isStrongBoxBacked());
         out.writeBoolean(mSpec.isUserConfirmationRequired());
         out.writeBoolean(mSpec.isUnlockedDeviceRequired());
+        out.writeBoolean(mSpec.isCriticalToDeviceEncryption());
     }
 
     private static Date readDateOrNull(Parcel in) {
@@ -160,6 +161,7 @@
         final boolean isStrongBoxBacked = in.readBoolean();
         final boolean userConfirmationRequired = in.readBoolean();
         final boolean unlockedDeviceRequired = in.readBoolean();
+        final boolean criticalToDeviceEncryption = in.readBoolean();
         // The KeyGenParameterSpec is intentionally not constructed using a Builder here:
         // The intention is for this class to break if new parameters are added to the
         // KeyGenParameterSpec constructor (whereas using a builder would silently drop them).
@@ -190,7 +192,8 @@
                 invalidatedByBiometricEnrollment,
                 isStrongBoxBacked,
                 userConfirmationRequired,
-                unlockedDeviceRequired);
+                unlockedDeviceRequired,
+                criticalToDeviceEncryption);
     }
 
     public static final @android.annotation.NonNull Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() {
diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
index fca2775..b7d72fc 100644
--- a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
+++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
@@ -84,6 +84,7 @@
                 .setIsStrongBoxBacked(true)
                 .setUserConfirmationRequired(true)
                 .setUnlockedDeviceRequired(true)
+                .setCriticalToDeviceEncryption(true)
                 .build();
     }
 
@@ -115,6 +116,7 @@
         assertThat(spec.isStrongBoxBacked(), is(true));
         assertThat(spec.isUserConfirmationRequired(), is(true));
         assertThat(spec.isUnlockedDeviceRequired(), is(true));
+        assertThat(spec.isCriticalToDeviceEncryption(), is(true));
     }
 
     private Parcel parcelForReading(ParcelableKeyGenParameterSpec spec) {
diff --git a/media/OWNERS b/media/OWNERS
index 8bd037a..be60583 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -5,6 +5,7 @@
 etalvala@google.com
 gkasten@google.com
 hdmoon@google.com
+hkuang@google.com
 hunga@google.com
 insun@google.com
 jaewan@google.com
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 6d37c2d..bc4da10 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -57,7 +57,7 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private static final Object sRouterLock = new Object();
 
-    @GuardedBy("sLock")
+    @GuardedBy("sRouterLock")
     private static MediaRouter2 sInstance;
 
     private final Context mContext;
@@ -73,23 +73,23 @@
             new CopyOnWriteArrayList<>();
 
     private final String mPackageName;
-    @GuardedBy("sLock")
+    @GuardedBy("sRouterLock")
     final Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
 
-    @GuardedBy("sLock")
+    @GuardedBy("sRouterLock")
     private RouteDiscoveryPreference mDiscoveryPreference = RouteDiscoveryPreference.EMPTY;
 
     // TODO: Make MediaRouter2 is always connected to the MediaRouterService.
-    @GuardedBy("sLock")
+    @GuardedBy("sRouterLock")
     Client2 mClient;
 
-    @GuardedBy("sLock")
+    @GuardedBy("sRouterLock")
     private Map<String, RoutingController> mRoutingControllers = new ArrayMap<>();
 
     private AtomicInteger mSessionCreationRequestCnt = new AtomicInteger(1);
 
     final Handler mHandler;
-    @GuardedBy("sLock")
+    @GuardedBy("sRouterLock")
     private boolean mShouldUpdateRoutes;
     private volatile List<MediaRoute2Info> mFilteredRoutes = Collections.emptyList();
 
@@ -728,16 +728,15 @@
      * For example, selecting/deselcting/transferring routes to session can be done through this
      * class. Instances are created by {@link MediaRouter2}.
      *
-     * TODO: Need to add toString()
      * @hide
      */
     public final class RoutingController {
         private final Object mControllerLock = new Object();
 
-        @GuardedBy("mLock")
+        @GuardedBy("mControllerLock")
         private RoutingSessionInfo mSessionInfo;
 
-        @GuardedBy("mLock")
+        @GuardedBy("mControllerLock")
         private volatile boolean mIsReleased;
 
         RoutingController(@NonNull RoutingSessionInfo sessionInfo) {
@@ -998,6 +997,38 @@
             }
         }
 
+        @Override
+        public String toString() {
+            // To prevent logging spam, we only print the ID of each route.
+            List<String> selectedRoutes = getSelectedRoutes().stream()
+                    .map(MediaRoute2Info::getId).collect(Collectors.toList());
+            List<String> selectableRoutes = getSelectableRoutes().stream()
+                    .map(MediaRoute2Info::getId).collect(Collectors.toList());
+            List<String> deselectableRoutes = getDeselectableRoutes().stream()
+                    .map(MediaRoute2Info::getId).collect(Collectors.toList());
+            List<String> transferrableRoutes = getTransferrableRoutes().stream()
+                    .map(MediaRoute2Info::getId).collect(Collectors.toList());
+
+            StringBuilder result = new StringBuilder()
+                    .append("RoutingController{ ")
+                    .append("sessionId=").append(getSessionId())
+                    .append(", routeFeature=").append(getRouteFeature())
+                    .append(", selectedRoutes={")
+                    .append(selectedRoutes)
+                    .append("}")
+                    .append(", selectableRoutes={")
+                    .append(selectableRoutes)
+                    .append("}")
+                    .append(", deselectableRoutes={")
+                    .append(deselectableRoutes)
+                    .append("}")
+                    .append(", transferrableRoutes={")
+                    .append(transferrableRoutes)
+                    .append("}")
+                    .append(" }");
+            return result.toString();
+        }
+
         /**
          * TODO: Change this to package private. (Hidden for debugging purposes)
          * @hide
diff --git a/media/java/android/media/VolumeProvider.java b/media/java/android/media/VolumeProvider.java
index 8f68cbd..ed272d5 100644
--- a/media/java/android/media/VolumeProvider.java
+++ b/media/java/android/media/VolumeProvider.java
@@ -16,6 +16,7 @@
 package android.media;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.media.session.MediaSession;
 
 import java.lang.annotation.Retention;
@@ -60,6 +61,7 @@
 
     private final int mControlType;
     private final int mMaxVolume;
+    private final String mControlId;
     private int mCurrentVolume;
     private Callback mCallback;
 
@@ -73,10 +75,28 @@
      * @param maxVolume The maximum allowed volume.
      * @param currentVolume The current volume on the output.
      */
+
     public VolumeProvider(@ControlType int volumeControl, int maxVolume, int currentVolume) {
+        this(volumeControl, maxVolume, currentVolume, null);
+    }
+
+    /**
+     * Create a new volume provider for handling volume events. You must specify
+     * the type of volume control, the maximum volume that can be used, and the
+     * current volume on the output.
+     *
+     * @param volumeControl The method for controlling volume that is used by
+     *            this provider.
+     * @param maxVolume The maximum allowed volume.
+     * @param currentVolume The current volume on the output.
+     * @param volumeControlId The volume control id of this provider.
+     */
+    public VolumeProvider(@ControlType int volumeControl, int maxVolume, int currentVolume,
+            @Nullable String volumeControlId) {
         mControlType = volumeControl;
         mMaxVolume = maxVolume;
         mCurrentVolume = currentVolume;
+        mControlId = volumeControlId;
     }
 
     /**
@@ -122,6 +142,17 @@
     }
 
     /**
+     * Gets the volume control id. It can be used to identify which volume provider is
+     * used by the session.
+     *
+     * @return the volume control id or {@code null} if it isn't set.
+     */
+    @Nullable
+    public final String getVolumeControlId() {
+        return mControlId;
+    }
+
+    /**
      * Override to handle requests to set the volume of the current output.
      * After the volume has been modified {@link #setCurrentVolume} must be
      * called to notify the system.
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 4d68a6a..21378c8 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -48,6 +48,6 @@
 
     // These commands relate to volume handling
     void setPlaybackToLocal(in AudioAttributes attributes);
-    void setPlaybackToRemote(int control, int max);
+    void setPlaybackToRemote(int control, int max, @nullable String controlId);
     void setCurrentVolume(int currentVolume);
 }
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 1812d9c..c2f6206 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -964,16 +964,26 @@
         private final int mMaxVolume;
         private final int mCurrentVolume;
         private final AudioAttributes mAudioAttrs;
+        private final String mVolumeControlId;
 
         /**
          * @hide
          */
         public PlaybackInfo(int type, int control, int max, int current, AudioAttributes attrs) {
+            this(type, control, max, current, attrs, null);
+        }
+
+        /**
+         * @hide
+         */
+        public PlaybackInfo(int type, int control, int max, int current, AudioAttributes attrs,
+                String volumeControlId) {
             mVolumeType = type;
             mVolumeControl = control;
             mMaxVolume = max;
             mCurrentVolume = current;
             mAudioAttrs = attrs;
+            mVolumeControlId = volumeControlId;
         }
 
         PlaybackInfo(Parcel in) {
@@ -982,6 +992,7 @@
             mMaxVolume = in.readInt();
             mCurrentVolume = in.readInt();
             mAudioAttrs = in.readParcelable(null);
+            mVolumeControlId = in.readString();
         }
 
         /**
@@ -1042,11 +1053,24 @@
             return mAudioAttrs;
         }
 
+        /**
+         * Gets the volume control ID for this session. It can be used to identify which
+         * volume provider is used by the session.
+         *
+         * @return the volume control ID for this session or {@code null} if it's local playback
+         * or not set.
+         * @see VolumeProvider#getVolumeControlId()
+         */
+        @Nullable
+        public String getVolumeControlId() {
+            return mVolumeControlId;
+        }
+
         @Override
         public String toString() {
             return "volumeType=" + mVolumeType + ", volumeControl=" + mVolumeControl
                     + ", maxVolume=" + mMaxVolume + ", currentVolume=" + mCurrentVolume
-                    + ", audioAttrs=" + mAudioAttrs;
+                    + ", audioAttrs=" + mAudioAttrs + ", volumeControlId=" + mVolumeControlId;
         }
 
         @Override
@@ -1061,6 +1085,7 @@
             dest.writeInt(mMaxVolume);
             dest.writeInt(mCurrentVolume);
             dest.writeParcelable(mAudioAttrs, flags);
+            dest.writeString(mVolumeControlId);
         }
 
         public static final @android.annotation.NonNull Parcelable.Creator<PlaybackInfo> CREATOR =
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 3adee59..9953626 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -335,7 +335,7 @@
 
         try {
             mBinder.setPlaybackToRemote(volumeProvider.getVolumeControl(),
-                    volumeProvider.getMaxVolume());
+                    volumeProvider.getMaxVolume(), volumeProvider.getVolumeControlId());
             mBinder.setCurrentVolume(volumeProvider.getCurrentVolume());
         } catch (RemoteException e) {
             Log.wtf(TAG, "Failure in setPlaybackToRemote.", e);
diff --git a/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl b/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
index 149c1cd..726af76 100644
--- a/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
+++ b/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
@@ -44,4 +44,11 @@
      * and this event will be sent in addition to the abort event.
      */
     void onRecognitionAvailabilityChange(boolean available);
+    /**
+     * Notifies the client that the associated module has crashed and restarted. The module instance
+     * is no longer usable and will throw a ServiceSpecificException with a Status.DEAD_OBJECT code
+     * for every call. The client should detach, then re-attach to the module in order to get a new,
+     * usable instance. All state for this module has been lost.
+     */
+     void onModuleDied();
 }
diff --git a/media/java/android/media/soundtrigger_middleware/Status.aidl b/media/java/android/media/soundtrigger_middleware/Status.aidl
index 5d082e2..85ccacf 100644
--- a/media/java/android/media/soundtrigger_middleware/Status.aidl
+++ b/media/java/android/media/soundtrigger_middleware/Status.aidl
@@ -28,4 +28,6 @@
     OPERATION_NOT_SUPPORTED = 2,
     /** Temporary lack of permission. */
     TEMPORARY_PERMISSION_DENIED = 3,
+    /** The object on which this method is called is dead and all of its state is lost. */
+    DEAD_OBJECT = 4,
 }
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index c7cc9e6d..8e579bf 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -23,7 +23,6 @@
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.hardware.tv.tuner.V1_0.Constants;
-import android.media.tv.tuner.Tuner.LnbCallback;
 import android.media.tv.tuner.TunerConstants.Result;
 
 import java.lang.annotation.Retention;
diff --git a/media/java/android/media/tv/tuner/LnbCallback.java b/media/java/android/media/tv/tuner/LnbCallback.java
new file mode 100644
index 0000000..99bbf86
--- /dev/null
+++ b/media/java/android/media/tv/tuner/LnbCallback.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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.media.tv.tuner;
+
+
+/**
+ * Callback interface for receiving information from LNBs.
+ *
+ * @hide
+ */
+public interface LnbCallback {
+    /**
+     * Invoked when there is a LNB event.
+     */
+    void onEvent(int lnbEventType);
+
+    /**
+     * Invoked when there is a new DiSEqC message.
+     *
+     * @param diseqcMessage a byte array of data for DiSEqC (Digital Satellite
+     * Equipment Control) message which is specified by EUTELSAT Bus Functional
+     * Specification Version 4.2.
+     */
+    void onDiseqcMessage(byte[] diseqcMessage);
+}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index e3dfaeb..6f5ba84 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -137,29 +137,10 @@
 
     private static native DemuxCapabilities nativeGetDemuxCapabilities();
 
-    /**
-     * LNB Callback.
-     *
-     * @hide
-     */
-    public interface LnbCallback {
-        /**
-         * Invoked when there is a LNB event.
-         */
-        void onEvent(int lnbEventType);
-
-        /**
-         * Invoked when there is a new DiSEqC message.
-         *
-         * @param diseqcMessage a byte array of data for DiSEqC (Digital Satellite
-         * Equipment Control) message which is specified by EUTELSAT Bus Functional
-         * Specification Version 4.2.
-         */
-        void onDiseqcMessage(byte[] diseqcMessage);
-    }
 
     /**
      * Callback interface for receiving information from the corresponding filters.
+     * TODO: remove
      */
     public interface FilterCallback {
         /**
@@ -178,22 +159,6 @@
         void onFilterStatusChanged(@NonNull Filter filter, @FilterStatus int status);
     }
 
-    /**
-     * DVR Callback.
-     *
-     * @hide
-     */
-    public interface DvrCallback {
-        /**
-         * Invoked when record status changed.
-         */
-        void onRecordStatus(int status);
-        /**
-         * Invoked when playback status changed.
-         */
-        void onPlaybackStatus(int status);
-    }
-
     @Nullable
     private EventHandler createEventHandler() {
         Looper looper;
diff --git a/media/java/android/media/tv/tuner/dvr/Dvr.java b/media/java/android/media/tv/tuner/dvr/Dvr.java
index f90042b..95508d3 100644
--- a/media/java/android/media/tv/tuner/dvr/Dvr.java
+++ b/media/java/android/media/tv/tuner/dvr/Dvr.java
@@ -18,7 +18,6 @@
 
 import android.annotation.BytesLong;
 import android.annotation.NonNull;
-import android.media.tv.tuner.Tuner.DvrCallback;
 import android.media.tv.tuner.Tuner.Filter;
 import android.media.tv.tuner.TunerConstants.Result;
 import android.os.ParcelFileDescriptor;
diff --git a/media/java/android/media/tv/tuner/dvr/DvrCallback.java b/media/java/android/media/tv/tuner/dvr/DvrCallback.java
new file mode 100644
index 0000000..3d140f0
--- /dev/null
+++ b/media/java/android/media/tv/tuner/dvr/DvrCallback.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 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.media.tv.tuner.dvr;
+
+/**
+ * Callback interface for receiving information from DVR interfaces.
+ *
+ * @hide
+ */
+public interface DvrCallback {
+    /**
+     * Invoked when record status changed.
+     */
+    void onRecordStatusChanged(int status);
+    /**
+     * Invoked when playback status changed.
+     */
+    void onPlaybackStatusChanged(int status);
+}
diff --git a/media/java/android/media/tv/tuner/filter/FilterCallback.java b/media/java/android/media/tv/tuner/filter/FilterCallback.java
new file mode 100644
index 0000000..888adc5
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/FilterCallback.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 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.media.tv.tuner.filter;
+
+import android.annotation.NonNull;
+import android.media.tv.tuner.TunerConstants.FilterStatus;
+
+/**
+ * Callback interface for receiving information from the corresponding filters.
+ *
+ * @hide
+ */
+public interface FilterCallback {
+    /**
+     * Invoked when there are filter events.
+     *
+     * @param filter the corresponding filter which sent the events.
+     * @param events the filter events sent from the filter.
+     */
+    void onFilterEvent(@NonNull Filter filter, @NonNull FilterEvent[] events);
+    /**
+     * Invoked when filter status changed.
+     *
+     * @param filter the corresponding filter whose status is changed.
+     * @param status the new status of the filter.
+     */
+    void onFilterStatusChanged(@NonNull Filter filter, @FilterStatus int status);
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 747ceb1..50d3a5d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -262,13 +262,28 @@
         }
     }
 
+    /**
+     * Connect this device.
+     *
+     * @param connectAllProfiles {@code true} to connect all profile, {@code false} otherwise.
+     *
+     * @deprecated use {@link #connect()} instead.
+     */
+    @Deprecated
     public void connect(boolean connectAllProfiles) {
+        connect();
+    }
+
+    /**
+     * Connect this device.
+     */
+    public void connect() {
         if (!ensurePaired()) {
             return;
         }
 
         mConnectAttempted = SystemClock.elapsedRealtime();
-        connectWithoutResettingTimer(connectAllProfiles);
+        connectAllEnabledProfiles();
     }
 
     public long getHiSyncId() {
@@ -289,10 +304,10 @@
     void onBondingDockConnect() {
         // Attempt to connect if UUIDs are available. Otherwise,
         // we will connect when the ACTION_UUID intent arrives.
-        connect(false);
+        connect();
     }
 
-    private void connectWithoutResettingTimer(boolean connectAllProfiles) {
+    private void connectAllEnabledProfiles() {
         synchronized (mProfileLock) {
             // Try to initialize the profiles if they were not.
             if (mProfiles.isEmpty()) {
@@ -307,36 +322,7 @@
                 return;
             }
 
-            int preferredProfiles = 0;
-            for (LocalBluetoothProfile profile : mProfiles) {
-                if (connectAllProfiles ? profile.accessProfileEnabled()
-                        : profile.isAutoConnectable()) {
-                    if (profile.isPreferred(mDevice)) {
-                        ++preferredProfiles;
-                        connectInt(profile);
-                    }
-                }
-            }
-            if (BluetoothUtils.D) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
-
-            if (preferredProfiles == 0) {
-                connectAutoConnectableProfiles();
-            }
-        }
-    }
-
-    private void connectAutoConnectableProfiles() {
-        if (!ensurePaired()) {
-            return;
-        }
-
-        synchronized (mProfileLock) {
-            for (LocalBluetoothProfile profile : mProfiles) {
-                if (profile.isAutoConnectable()) {
-                    profile.setPreferred(mDevice, true);
-                    connectInt(profile);
-                }
-            }
+            mLocalAdapter.connectAllEnabledProfiles(mDevice);
         }
     }
 
@@ -703,7 +689,7 @@
          */
         if (!mProfiles.isEmpty()
                 && ((mConnectAttempted + timeout) > SystemClock.elapsedRealtime())) {
-            connectWithoutResettingTimer(false);
+            connectAllEnabledProfiles();
         }
 
         dispatchAttributesChanged();
@@ -722,7 +708,7 @@
         refresh();
 
         if (bondState == BluetoothDevice.BOND_BONDED && mDevice.isBondingInitiatedLocally()) {
-            connect(false);
+            connect();
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
index 61e47f8..6e7a429 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
@@ -87,8 +87,10 @@
         if (mMetricsFeature == null || mMetricsCategory == METRICS_CATEGORY_UNKNOWN) {
             return;
         }
-        final int elapse = (int) (SystemClock.elapsedRealtime() - mCreationTimestamp);
-        mMetricsFeature.action(METRICS_CATEGORY_UNKNOWN, action, mMetricsCategory, key, elapse);
+        if (mCreationTimestamp != 0L) {
+            final int elapse = (int) (SystemClock.elapsedRealtime() - mCreationTimestamp);
+            mMetricsFeature.action(METRICS_CATEGORY_UNKNOWN, action, mMetricsCategory, key, elapse);
+        }
     }
 
     /**
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
index 4ab9a9a..b07fc2b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
@@ -61,6 +61,8 @@
             "com.android.settings.category.ia.my_device_info";
     public static final String CATEGORY_BATTERY_SAVER_SETTINGS =
             "com.android.settings.category.ia.battery_saver_settings";
+    public static final String CATEGORY_SMART_BATTERY_SETTINGS =
+            "com.android.settings.category.ia.smart_battery_settings";
 
     public static final Map<String, String> KEY_COMPAT_MAP;
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 70b56ed..e85a102 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -122,7 +122,7 @@
             final CachedBluetoothDevice cachedDevice =
                     ((BluetoothMediaDevice) device).getCachedDevice();
             if (!cachedDevice.isConnected() && !cachedDevice.isBusy()) {
-                cachedDevice.connect(true);
+                cachedDevice.connect();
                 return;
             }
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
index 853c77e..05a6ce4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
@@ -40,7 +40,7 @@
         final NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll(
                 telephonyManager.createForSubscriptionId(subId).getSubscriberId());
 
-        if (!subscriptionManager.isActiveSubId(subId)) {
+        if (!subscriptionManager.isActiveSubscriptionId(subId)) {
             Log.i(TAG, "Subscription is not active: " + subId);
             return mobileAll;
         }
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/OWNER b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/OWNER
new file mode 100644
index 0000000..5c2a7b8
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/OWNER
@@ -0,0 +1,4 @@
+# People who can approve changes for submission
+arcwang@google.com
+govenliu@google.com
+qal@google.com
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
index 605c861..340a6c7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
@@ -59,8 +59,9 @@
         allKeys.add(CategoryKey.CATEGORY_SYSTEM_DEVELOPMENT);
         allKeys.add(CategoryKey.CATEGORY_GESTURES);
         allKeys.add(CategoryKey.CATEGORY_NIGHT_DISPLAY);
+        allKeys.add(CategoryKey.CATEGORY_SMART_BATTERY_SETTINGS);
         // DO NOT REMOVE ANYTHING ABOVE
 
-        assertThat(allKeys.size()).isEqualTo(18);
+        assertThat(allKeys.size()).isEqualTo(19);
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 894aa78..c780a64 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -127,7 +127,7 @@
         mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.connectDevice(device);
 
-        verify(cachedDevice).connect(true);
+        verify(cachedDevice).connect();
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
index d98f50b..b0a647e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
@@ -31,6 +31,7 @@
 import android.telephony.TelephonyManager;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -71,12 +72,13 @@
         when(mTelephonyManager.getSubscriberId(SUB_ID)).thenReturn(SUBSCRIBER_ID);
         when(mTelephonyManager.getSubscriberId(SUB_ID_2)).thenReturn(SUBSCRIBER_ID_2);
         when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
-        when(mSubscriptionManager.isActiveSubId(anyInt())).thenReturn(true);
+        when(mSubscriptionManager.isActiveSubscriptionId(anyInt())).thenReturn(true);
     }
 
     @Test
+    @Ignore
     public void getMobileTemplate_infoNull_returnMobileAll() {
-        when(mSubscriptionManager.isActiveSubId(SUB_ID)).thenReturn(false);
+        when(mSubscriptionManager.isActiveSubscriptionId(SUB_ID)).thenReturn(false);
 
         final NetworkTemplate networkTemplate = DataUsageUtils.getMobileTemplate(mContext, SUB_ID);
         assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue();
@@ -84,6 +86,7 @@
     }
 
     @Test
+    @Ignore
     public void getMobileTemplate_groupUuidNull_returnMobileAll() {
         when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1);
         when(mInfo1.getGroupUuid()).thenReturn(null);
@@ -96,6 +99,7 @@
     }
 
     @Test
+    @Ignore
     public void getMobileTemplate_groupUuidExist_returnMobileMerged() {
         when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1);
         when(mInfo1.getGroupUuid()).thenReturn(mParcelUuid);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
index 21aa526..2bd20a9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
@@ -72,7 +72,7 @@
 
         assertThat(AccessPointPreference.buildContentDescription(
                 RuntimeEnvironment.application, pref, ap))
-                .isEqualTo("ssid,connected,Wifi signal full.,Secure network");
+                .isEqualTo("ssid,connected,Wifi disconnected.,Secure network");
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/OWNER b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/OWNER
new file mode 100644
index 0000000..5c2a7b8
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/OWNER
@@ -0,0 +1,4 @@
+# People who can approve changes for submission
+arcwang@google.com
+govenliu@google.com
+qal@google.com
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1e0c1d8..4443524 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -20,6 +20,7 @@
 import static android.os.Process.ROOT_UID;
 import static android.os.Process.SHELL_UID;
 import static android.os.Process.SYSTEM_UID;
+import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
 
 import android.Manifest;
@@ -3304,7 +3305,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 185;
+            private static final int SETTINGS_VERSION = 186;
 
             private final int mUserId;
 
@@ -4547,6 +4548,32 @@
                     currentVersion = 185;
                 }
 
+                if (currentVersion == 185) {
+                    // Deprecate ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, and migrate it
+                    // to ACCESSIBILITY_BUTTON_TARGET_COMPONENT.
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                    final Setting magnifyNavbarEnabled = secureSettings.getSettingLocked(
+                            Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
+                    if ("1".equals(magnifyNavbarEnabled.getValue())) {
+                        secureSettings.insertSettingLocked(
+                                Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+                                ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER,
+                                null /* tag */, false /* makeDefault */,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                    } else {
+                        // Clear a11y button targets list setting. A11yManagerService will end up
+                        // adding all legacy enabled services that want the button to the list, so
+                        // there's no need to keep tracking them.
+                        secureSettings.insertSettingLocked(
+                                Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+                                null, null /* tag */, false /* makeDefault */,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+                    secureSettings.deleteSettingLocked(
+                            Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
+                    currentVersion = 186;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 7278225..1a1a480 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -734,7 +734,8 @@
                  Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
                  Settings.Secure.FACE_UNLOCK_RE_ENROLL,
                  Settings.Secure.TAP_GESTURE,
-                 Settings.Secure.WINDOW_MAGNIFICATION);
+                 Settings.Secure.WINDOW_MAGNIFICATION,
+                 Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER);
 
     @Test
     public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 2a1e74e..df77b5b 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -211,6 +211,7 @@
 
     <!-- accessibility -->
     <uses-permission android:name="android.permission.MODIFY_ACCESSIBILITY_DATA" />
+    <uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY" />
 
     <!-- to control accessibility volume -->
     <uses-permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME" />
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index df0dc46..e475ef1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -387,8 +387,8 @@
      * Refresh the time of the clock, due to either time tick broadcast or doze time tick alarm.
      */
     public void refresh() {
-        mClockView.refresh();
-        mClockViewBold.refresh();
+        mClockView.refreshTime();
+        mClockViewBold.refreshTime();
         if (mClockPlugin != null) {
             mClockPlugin.onTimeTick();
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java
index eba2400..99e122e 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java
@@ -188,7 +188,7 @@
     public void onTimeTick() {
         mAnalogClock.onTimeChanged();
         mBigClockView.onTimeChanged();
-        mLockClock.refresh();
+        mLockClock.refreshTime();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
index 3a2fbe5..fac923c01 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
@@ -195,7 +195,7 @@
     public void onTimeTick() {
         mAnalogClock.onTimeChanged();
         mView.onTimeChanged();
-        mLockClock.refresh();
+        mLockClock.refreshTime();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index 3bbd722..820c042 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -176,12 +176,14 @@
                     }
                     entry.ranking = newRanking
 
-                    val oldSbn = entry.sbn.cloneLight()
                     val newOverrideGroupKey = newRanking.overrideGroupKey
-                    if (!Objects.equals(oldSbn.overrideGroupKey, newOverrideGroupKey)) {
+                    if (!Objects.equals(entry.sbn.overrideGroupKey, newOverrideGroupKey)) {
+                        val oldGroupKey = entry.sbn.groupKey
+                        val oldIsGroup = entry.sbn.isGroup
+                        val oldIsGroupSummary = entry.sbn.notification.isGroupSummary
                         entry.sbn.overrideGroupKey = newOverrideGroupKey
-                        // TODO: notify group manager here?
-                        groupManager.onEntryUpdated(entry, oldSbn)
+                        groupManager.onEntryUpdated(entry, oldGroupKey, oldIsGroup,
+                                oldIsGroupSummary)
                     }
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index a6842ba..4f56f56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -157,7 +157,6 @@
     private int mNavigationIconHints = 0;
     private @TransitionMode int mNavigationBarMode;
     private AccessibilityManager mAccessibilityManager;
-    private MagnificationContentObserver mMagnificationObserver;
     private ContentResolver mContentResolver;
     private boolean mAssistantAvailable;
 
@@ -303,11 +302,6 @@
         mWindowManager = getContext().getSystemService(WindowManager.class);
         mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class);
         mContentResolver = getContext().getContentResolver();
-        mMagnificationObserver = new MagnificationContentObserver(
-                getContext().getMainThreadHandler());
-        mContentResolver.registerContentObserver(Settings.Secure.getUriFor(
-                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED), false,
-                mMagnificationObserver, UserHandle.USER_ALL);
         mContentResolver.registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
                 false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
@@ -329,7 +323,6 @@
         super.onDestroy();
         mNavigationModeController.removeListener(this);
         mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener);
-        mContentResolver.unregisterContentObserver(mMagnificationObserver);
         mContentResolver.unregisterContentObserver(mAssistContentObserver);
     }
 
@@ -969,28 +962,18 @@
      * @param outFeedbackEnabled if non-null, sets it to true if accessibility feedback is enabled.
      */
     public int getA11yButtonState(@Nullable boolean[] outFeedbackEnabled) {
-        int requestingServices = 0;
-        try {
-            if (Settings.Secure.getIntForUser(mContentResolver,
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
-                    UserHandle.USER_CURRENT) == 1) {
-                requestingServices++;
-            }
-        } catch (Settings.SettingNotFoundException e) {
-        }
-
         boolean feedbackEnabled = false;
         // AccessibilityManagerService resolves services for the current user since the local
         // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
         final List<AccessibilityServiceInfo> services =
                 mAccessibilityManager.getEnabledAccessibilityServiceList(
                         AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
+        final List<String> a11yButtonTargets =
+                mAccessibilityManager.getAccessibilityShortcutTargets(
+                        AccessibilityManager.ACCESSIBILITY_BUTTON);
+        final int requestingServices = a11yButtonTargets.size();
         for (int i = services.size() - 1; i >= 0; --i) {
             AccessibilityServiceInfo info = services.get(i);
-            if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) {
-                requestingServices++;
-            }
-
             if (info.feedbackType != 0 && info.feedbackType !=
                     AccessibilityServiceInfo.FEEDBACK_GENERIC) {
                 feedbackEnabled = true;
@@ -1114,18 +1097,6 @@
     private final AccessibilityServicesStateChangeListener mAccessibilityListener =
             this::updateAccessibilityServicesState;
 
-    private class MagnificationContentObserver extends ContentObserver {
-
-        public MagnificationContentObserver(Handler handler) {
-            super(handler);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            NavigationBarFragment.this.updateAccessibilityServicesState(mAccessibilityManager);
-        }
-    }
-
     private final Consumer<Integer> mRotationWatcher = rotation -> {
         if (mNavigationBarView != null
                 && mNavigationBarView.needsReorient(rotation)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index e11fc1b..8c947ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -116,7 +116,13 @@
      */
     private void onEntryRemovedInternal(NotificationEntry removed,
             final StatusBarNotification sbn) {
-        String groupKey = getGroupKey(sbn);
+        onEntryRemovedInternal(removed, sbn.getGroupKey(), sbn.isGroup(),
+                sbn.getNotification().isGroupSummary());
+    }
+
+    private void onEntryRemovedInternal(NotificationEntry removed, String notifGroupKey, boolean
+            isGroup, boolean isGroupSummary) {
+        String groupKey = getGroupKey(removed.getKey(), notifGroupKey);
         final NotificationGroup group = mGroupMap.get(groupKey);
         if (group == null) {
             // When an app posts 2 different notifications as summary of the same group, then a
@@ -125,7 +131,7 @@
             // the close future. See b/23676310 for reference.
             return;
         }
-        if (isGroupChild(sbn)) {
+        if (isGroupChild(removed.getKey(), isGroup, isGroupSummary)) {
             group.children.remove(removed.getKey());
         } else {
             group.summary = null;
@@ -229,7 +235,7 @@
     private int getNumberOfIsolatedChildren(String groupKey) {
         int count = 0;
         for (StatusBarNotification sbn : mIsolatedEntries.values()) {
-            if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn)) {
+            if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn.getKey())) {
                 count++;
             }
         }
@@ -238,31 +244,47 @@
 
     private NotificationEntry getIsolatedChild(String groupKey) {
         for (StatusBarNotification sbn : mIsolatedEntries.values()) {
-            if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn)) {
+            if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn.getKey())) {
                 return mGroupMap.get(sbn.getKey()).summary;
             }
         }
         return null;
     }
 
-    public void onEntryUpdated(NotificationEntry entry,
-            StatusBarNotification oldNotification) {
-        String oldKey = oldNotification.getGroupKey();
-        String newKey = entry.getSbn().getGroupKey();
-        boolean groupKeysChanged = !oldKey.equals(newKey);
-        boolean wasGroupChild = isGroupChild(oldNotification);
+    /**
+     * Update an entry's group information
+     * @param entry notification entry to update
+     * @param oldNotification previous notification info before this update
+     */
+    public void onEntryUpdated(NotificationEntry entry, StatusBarNotification oldNotification) {
+        onEntryUpdated(entry, oldNotification.getGroupKey(), oldNotification.isGroup(),
+                oldNotification.getNotification().isGroupSummary());
+    }
+
+    /**
+     * Updates an entry's group information
+     * @param entry notification entry to update
+     * @param oldGroupKey the notification's previous group key before this update
+     * @param oldIsGroup whether this notification was a group before this update
+     * @param oldIsGroupSummary whether this notification was a group summary before this update
+     */
+    public void onEntryUpdated(NotificationEntry entry, String oldGroupKey, boolean oldIsGroup,
+            boolean oldIsGroupSummary) {
+        String newGroupKey = entry.getSbn().getGroupKey();
+        boolean groupKeysChanged = !oldGroupKey.equals(newGroupKey);
+        boolean wasGroupChild = isGroupChild(entry.getKey(), oldIsGroup, oldIsGroupSummary);
         boolean isGroupChild = isGroupChild(entry.getSbn());
         mIsUpdatingUnchangedGroup = !groupKeysChanged && wasGroupChild == isGroupChild;
-        if (mGroupMap.get(getGroupKey(oldNotification)) != null) {
-            onEntryRemovedInternal(entry, oldNotification);
+        if (mGroupMap.get(getGroupKey(entry.getKey(), oldGroupKey)) != null) {
+            onEntryRemovedInternal(entry, oldGroupKey, oldIsGroup, oldIsGroupSummary);
         }
         onEntryAdded(entry);
         mIsUpdatingUnchangedGroup = false;
-        if (isIsolated(entry.getSbn())) {
+        if (isIsolated(entry.getSbn().getKey())) {
             mIsolatedEntries.put(entry.getKey(), entry.getSbn());
             if (groupKeysChanged) {
-                updateSuppression(mGroupMap.get(oldKey));
-                updateSuppression(mGroupMap.get(newKey));
+                updateSuppression(mGroupMap.get(oldGroupKey));
+                updateSuppression(mGroupMap.get(newGroupKey));
             }
         } else if (!wasGroupChild && isGroupChild) {
             onEntryBecomingChild(entry);
@@ -418,10 +440,14 @@
      * @return the key of the notification
      */
     public String getGroupKey(StatusBarNotification sbn) {
-        if (isIsolated(sbn)) {
-            return sbn.getKey();
+        return getGroupKey(sbn.getKey(), sbn.getGroupKey());
+    }
+
+    private String getGroupKey(String key, String groupKey) {
+        if (isIsolated(key)) {
+            return key;
         }
-        return sbn.getGroupKey();
+        return groupKey;
     }
 
     /** @return group expansion state after toggling. */
@@ -434,8 +460,8 @@
         return group.expanded;
     }
 
-    private boolean isIsolated(StatusBarNotification sbn) {
-        return mIsolatedEntries.containsKey(sbn.getKey());
+    private boolean isIsolated(String sbnKey) {
+        return mIsolatedEntries.containsKey(sbnKey);
     }
 
     /**
@@ -445,7 +471,7 @@
      * @return true if it is visually a group summary
      */
     public boolean isGroupSummary(StatusBarNotification sbn) {
-        if (isIsolated(sbn)) {
+        if (isIsolated(sbn.getKey())) {
             return true;
         }
         return sbn.getNotification().isGroupSummary();
@@ -458,10 +484,14 @@
      * @return true if it is visually a group child
      */
     public boolean isGroupChild(StatusBarNotification sbn) {
-        if (isIsolated(sbn)) {
+        return isGroupChild(sbn.getKey(), sbn.isGroup(), sbn.getNotification().isGroupSummary());
+    }
+
+    private boolean isGroupChild(String key, boolean isGroup, boolean isGroupSummary) {
+        if (isIsolated(key)) {
             return false;
         }
-        return sbn.isGroup() && !sbn.getNotification().isGroupSummary();
+        return isGroup && !isGroupSummary;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 5916180..f05b585 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -15,6 +15,9 @@
  */
 package com.android.systemui.statusbar.policy;
 
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+
 import android.content.Context;
 import android.content.Intent;
 import android.database.ContentObserver;
@@ -704,7 +707,11 @@
             }
             mServiceState = state;
             if (mServiceState != null) {
-                updateDataNetType(mServiceState.getDataNetworkType());
+                NetworkRegistrationInfo regInfo = mServiceState.getNetworkRegistrationInfo(
+                        DOMAIN_PS, TRANSPORT_TYPE_WWAN);
+                if (regInfo != null) {
+                    updateDataNetType(regInfo.getAccessNetworkTechnology());
+                }
             }
             updateTelephony();
         }
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 9cb06a5..13f3a5f 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
@@ -16,6 +16,9 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
 
@@ -39,6 +42,7 @@
 import android.os.Handler;
 import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
@@ -358,7 +362,13 @@
     }
 
     public void updateDataConnectionState(int dataState, int dataNetType) {
-        when(mServiceState.getDataNetworkType()).thenReturn(dataNetType);
+        NetworkRegistrationInfo fakeRegInfo = new NetworkRegistrationInfo.Builder()
+                .setTransportType(TRANSPORT_TYPE_WWAN)
+                .setDomain(DOMAIN_PS)
+                .setAccessNetworkTechnology(dataNetType)
+                .build();
+        when(mServiceState.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN))
+                .thenReturn(fakeRegInfo);
         mPhoneStateListener.onDataConnectionStateChanged(dataState, dataNetType);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 5a5ef8b..f6c750d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -1,5 +1,8 @@
 package com.android.systemui.statusbar.policy;
 
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.anyInt;
@@ -418,7 +421,13 @@
 
         // State from NR_5G to NONE NR_STATE_RESTRICTED, showing corresponding icon
         doReturn(NetworkRegistrationInfo.NR_STATE_RESTRICTED).when(mServiceState).getNrState();
-        doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+        NetworkRegistrationInfo fakeRegInfo = new NetworkRegistrationInfo.Builder()
+                .setTransportType(TRANSPORT_TYPE_WWAN)
+                .setDomain(DOMAIN_PS)
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+                .build();
+        doReturn(fakeRegInfo).when(mServiceState)
+                .getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN);
         mPhoneStateListener.onDataConnectionStateChanged(TelephonyManager.DATA_CONNECTED,
                 TelephonyManager.NETWORK_TYPE_LTE);
 
@@ -480,8 +489,13 @@
 
         verifyDataIndicators(TelephonyIcons.ICON_LTE);
 
-        when(mServiceState.getDataNetworkType())
-                .thenReturn(TelephonyManager.NETWORK_TYPE_HSPA);
+        NetworkRegistrationInfo fakeRegInfo = new NetworkRegistrationInfo.Builder()
+                .setTransportType(TRANSPORT_TYPE_WWAN)
+                .setDomain(DOMAIN_PS)
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_HSPA)
+                .build();
+        when(mServiceState.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN))
+                .thenReturn(fakeRegInfo);
         updateServiceState();
         verifyDataIndicators(TelephonyIcons.ICON_H);
     }
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index d297f3f..e441fb5 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -16,7 +16,7 @@
 
 java_defaults {
     name: "TetheringAndroidLibraryDefaults",
-    platform_apis: true,
+    sdk_version: "system_current",
     srcs: [
         "src/**/*.java",
         ":framework-tethering-shared-srcs",
@@ -29,6 +29,7 @@
         "netlink-client",
         "networkstack-aidl-interfaces-unstable-java",
         "android.hardware.tetheroffload.control-V1.0-java",
+        "net-utils-framework-common",
     ],
     libs: [
         "framework-tethering",
@@ -80,7 +81,7 @@
 // Common defaults for compiling the actual APK.
 java_defaults {
     name: "TetheringAppDefaults",
-    platform_apis: true,
+    sdk_version: "system_current",
     privileged: true,
     // Build system doesn't track transitive dependeicies for jni_libs, list all the dependencies
     // explicitly.
diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml
index 5a71eb2..c71d0d7 100644
--- a/packages/Tethering/AndroidManifest.xml
+++ b/packages/Tethering/AndroidManifest.xml
@@ -36,6 +36,7 @@
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
     <uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
     <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
+    <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
 
     <application
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
index ca290c6..19e8d69 100644
--- a/packages/Tethering/res/values/config.xml
+++ b/packages/Tethering/res/values/config.xml
@@ -38,8 +38,9 @@
 
     <!-- List of regexpressions describing the interface (if any) that represent tetherable
          Wifi P2P interfaces.  If the device doesn't want to support tethering over Wifi P2p this
-         should be empty.  An example would be "p2p-p2p.*" -->
+         should be empty.  An example would be "p2p-p2p\\d-.*" -->
     <string-array translatable="false" name="config_tether_wifi_p2p_regexs">
+        <item>"p2p-p2p\\d-.*"</item>
     </string-array>
 
     <!-- List of regexpressions describing the interface (if any) that represent tetherable
@@ -100,7 +101,7 @@
 
     <!-- If the mobile hotspot feature requires provisioning, a package name and class name
          can be provided to launch a supported application that provisions the devices.
-         EntitlementManager will send an inent to Settings with the specified package name and
+         EntitlementManager will send an intent to Settings with the specified package name and
          class name in extras to launch provision app.
          TODO: note what extras here.
 
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 6ac467e..4306cf0b 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -26,7 +26,6 @@
 
 import android.net.INetd;
 import android.net.INetworkStackStatusCallback;
-import android.net.INetworkStatsService;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -176,7 +175,6 @@
 
     private final SharedLog mLog;
     private final INetd mNetd;
-    private final INetworkStatsService mStatsService;
     private final Callback mCallback;
     private final InterfaceController mInterfaceCtrl;
 
@@ -208,12 +206,10 @@
 
     public IpServer(
             String ifaceName, Looper looper, int interfaceType, SharedLog log,
-            INetd netd, INetworkStatsService statsService, Callback callback,
-            boolean usingLegacyDhcp, Dependencies deps) {
+            INetd netd, Callback callback, boolean usingLegacyDhcp, Dependencies deps) {
         super(ifaceName, looper);
         mLog = log.forSubComponent(ifaceName);
         mNetd = netd;
-        mStatsService = statsService;
         mCallback = callback;
         mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog);
         mIfaceName = ifaceName;
@@ -882,12 +878,6 @@
             // to remove their rules, which generates errors.
             // Just do the best we can.
             try {
-                // About to tear down NAT; gather remaining statistics.
-                mStatsService.forceUpdate();
-            } catch (Exception e) {
-                mLog.e("Exception in forceUpdate: " + e.toString());
-            }
-            try {
                 mNetd.ipfwdRemoveInterfaceForward(mIfaceName, upstreamIface);
             } catch (RemoteException | ServiceSpecificException e) {
                 mLog.e("Exception in ipfwdRemoveInterfaceForward: " + e.toString());
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
index 4e2a2c1..1cabc8d 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -27,8 +27,6 @@
 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
 import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED;
 
-import static com.android.internal.R.string.config_wifi_tether_enable;
-
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -36,7 +34,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.res.Resources;
 import android.net.util.SharedLog;
 import android.os.Bundle;
 import android.os.Handler;
@@ -54,6 +51,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.StateMachine;
+import com.android.networkstack.tethering.R;
 
 import java.io.PrintWriter;
 
@@ -75,9 +73,7 @@
             "com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM";
     private static final String EXTRA_SUBID = "subId";
 
-    // {@link ComponentName} of the Service used to run tether provisioning.
-    private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(
-            Resources.getSystem().getString(config_wifi_tether_enable));
+    private final ComponentName mSilentProvisioningService;
     private static final int MS_PER_HOUR = 60 * 60 * 1000;
     private static final int EVENT_START_PROVISIONING = 0;
     private static final int EVENT_STOP_PROVISIONING = 1;
@@ -122,6 +118,8 @@
         mHandler = new EntitlementHandler(masterHandler.getLooper());
         mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM),
                 null, mHandler);
+        mSilentProvisioningService = ComponentName.unflattenFromString(
+                mContext.getResources().getString(R.string.config_wifi_tether_enable));
     }
 
     public void setOnUiEntitlementFailedListener(final OnUiEntitlementFailedListener listener) {
@@ -377,7 +375,7 @@
         intent.putExtra(EXTRA_RUN_PROVISION, true);
         intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
         intent.putExtra(EXTRA_SUBID, subId);
-        intent.setComponent(TETHER_SERVICE);
+        intent.setComponent(mSilentProvisioningService);
         // Only admin user can change tethering and SilentTetherProvisioning don't need to
         // show UI, it is fine to always start setting's background service as system user.
         mContext.startService(intent);
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
index ce7c2a6..cc36f4a 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
@@ -16,35 +16,40 @@
 
 package com.android.server.connectivity.tethering;
 
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
 import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.STATS_PER_UID;
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
-import static android.net.TrafficStats.UID_TETHERING;
+import static android.net.NetworkStats.UID_TETHERING;
 import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.usage.NetworkStatsManager;
 import android.content.ContentResolver;
-import android.net.ITetheringStatsProvider;
 import android.net.InetAddresses;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkStats;
+import android.net.NetworkStats.Entry;
 import android.net.RouteInfo;
 import android.net.netlink.ConntrackMessage;
 import android.net.netlink.NetlinkConstants;
 import android.net.netlink.NetlinkSocket;
+import android.net.netstats.provider.AbstractNetworkStatsProvider;
+import android.net.netstats.provider.NetworkStatsProviderCallback;
 import android.net.util.SharedLog;
 import android.os.Handler;
-import android.os.INetworkManagementService;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.SystemClock;
 import android.provider.Settings;
 import android.system.ErrnoException;
 import android.system.OsConstants;
 import android.text.TextUtils;
+import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats;
 
@@ -73,13 +78,19 @@
     private static final String ANYIP = "0.0.0.0";
     private static final ForwardedStats EMPTY_STATS = new ForwardedStats();
 
+    @VisibleForTesting
+    enum StatsType {
+        STATS_PER_IFACE,
+        STATS_PER_UID,
+    }
+
     private enum UpdateType { IF_NEEDED, FORCE };
 
     private final Handler mHandler;
     private final OffloadHardwareInterface mHwInterface;
     private final ContentResolver mContentResolver;
-    private final INetworkManagementService mNms;
-    private final ITetheringStatsProvider mStatsProvider;
+    private final @NonNull OffloadTetheringStatsProvider mStatsProvider;
+    private final @Nullable NetworkStatsProviderCallback mStatsProviderCb;
     private final SharedLog mLog;
     private final HashMap<String, LinkProperties> mDownstreams;
     private boolean mConfigInitialized;
@@ -109,22 +120,23 @@
     private int mNatUpdateNetlinkErrors;
 
     public OffloadController(Handler h, OffloadHardwareInterface hwi,
-            ContentResolver contentResolver, INetworkManagementService nms, SharedLog log) {
+            ContentResolver contentResolver, NetworkStatsManager nsm, SharedLog log) {
         mHandler = h;
         mHwInterface = hwi;
         mContentResolver = contentResolver;
-        mNms = nms;
         mStatsProvider = new OffloadTetheringStatsProvider();
         mLog = log.forSubComponent(TAG);
         mDownstreams = new HashMap<>();
         mExemptPrefixes = new HashSet<>();
         mLastLocalPrefixStrs = new HashSet<>();
-
+        NetworkStatsProviderCallback providerCallback = null;
         try {
-            mNms.registerTetheringStatsProvider(mStatsProvider, getClass().getSimpleName());
-        } catch (RemoteException e) {
-            mLog.e("Cannot register offload stats provider: " + e);
+            providerCallback = nsm.registerNetworkStatsProvider(
+                    getClass().getSimpleName(), mStatsProvider);
+        } catch (RuntimeException e) {
+            Log.wtf(TAG, "Cannot register offload stats provider: " + e);
         }
+        mStatsProviderCb = providerCallback;
     }
 
     /** Start hardware offload. */
@@ -173,7 +185,7 @@
                         // and we need to synchronize stats and limits between
                         // software and hardware forwarding.
                         updateStatsForAllUpstreams();
-                        forceTetherStatsPoll();
+                        mStatsProvider.pushTetherStats();
                     }
 
                     @Override
@@ -186,7 +198,7 @@
                         // limits set take into account any software tethering
                         // traffic that has been happening in the meantime.
                         updateStatsForAllUpstreams();
-                        forceTetherStatsPoll();
+                        mStatsProvider.pushTetherStats();
                         // [2] (Re)Push all state.
                         computeAndPushLocalPrefixes(UpdateType.FORCE);
                         pushAllDownstreamState();
@@ -204,14 +216,11 @@
                         // the HAL queued the callback.
                         // TODO: rev the HAL so that it provides an interface name.
 
-                        // Fetch current stats, so that when our notification reaches
-                        // NetworkStatsService and triggers a poll, we will respond with
-                        // current data (which will be above the limit that was reached).
-                        // Note that if we just changed upstream, this is unnecessary but harmless.
-                        // The stats for the previous upstream were already updated on this thread
-                        // just after the upstream was changed, so they are also up-to-date.
                         updateStatsForCurrentUpstream();
-                        forceTetherStatsPoll();
+                        mStatsProvider.pushTetherStats();
+                        // Push stats to service does not cause the service react to it immediately.
+                        // Inform the service about limit reached.
+                        if (mStatsProviderCb != null) mStatsProviderCb.onLimitReached();
                     }
 
                     @Override
@@ -253,42 +262,37 @@
         return mConfigInitialized && mControlInitialized;
     }
 
-    private class OffloadTetheringStatsProvider extends ITetheringStatsProvider.Stub {
-        @Override
-        public NetworkStats getTetherStats(int how) {
-            // getTetherStats() is the only function in OffloadController that can be called from
-            // a different thread. Do not attempt to update stats by querying the offload HAL
-            // synchronously from a different thread than our Handler thread. http://b/64771555.
-            Runnable updateStats = () -> {
-                updateStatsForCurrentUpstream();
-            };
-            if (Looper.myLooper() == mHandler.getLooper()) {
-                updateStats.run();
-            } else {
-                mHandler.post(updateStats);
-            }
+    @VisibleForTesting
+    class OffloadTetheringStatsProvider extends AbstractNetworkStatsProvider {
+        // These stats must only ever be touched on the handler thread.
+        @NonNull
+        private NetworkStats mIfaceStats = new NetworkStats(0L, 0);
+        @NonNull
+        private NetworkStats mUidStats = new NetworkStats(0L, 0);
 
-            NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
-            NetworkStats.Entry entry = new NetworkStats.Entry();
-            entry.set = SET_DEFAULT;
-            entry.tag = TAG_NONE;
-            entry.uid = (how == STATS_PER_UID) ? UID_TETHERING : UID_ALL;
+        @VisibleForTesting
+        @NonNull
+        NetworkStats getTetherStats(@NonNull StatsType how) {
+            NetworkStats stats = new NetworkStats(0L, 0);
+            final int uid = (how == StatsType.STATS_PER_UID) ? UID_TETHERING : UID_ALL;
 
-            for (Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) {
-                ForwardedStats value = kv.getValue();
-                entry.iface = kv.getKey();
-                entry.rxBytes = value.rxBytes;
-                entry.txBytes = value.txBytes;
-                stats.addEntry(entry);
+            for (final Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) {
+                final ForwardedStats value = kv.getValue();
+                final Entry entry = new Entry(kv.getKey(), uid, SET_DEFAULT, TAG_NONE, METERED_NO,
+                        ROAMING_NO, DEFAULT_NETWORK_NO, value.rxBytes, 0L, value.txBytes, 0L, 0L);
+                stats = stats.addValues(entry);
             }
 
             return stats;
         }
 
         @Override
-        public void setInterfaceQuota(String iface, long quotaBytes) {
+        public void setLimit(String iface, long quotaBytes) {
+            mLog.i("setLimit: " + iface + "," + quotaBytes);
+            // Listen for all iface is necessary since upstream might be changed after limit
+            // is set.
             mHandler.post(() -> {
-                if (quotaBytes == ITetheringStatsProvider.QUOTA_UNLIMITED) {
+                if (quotaBytes == QUOTA_UNLIMITED) {
                     mInterfaceQuotas.remove(iface);
                 } else {
                     mInterfaceQuotas.put(iface, quotaBytes);
@@ -296,6 +300,42 @@
                 maybeUpdateDataLimit(iface);
             });
         }
+
+        /**
+         * Push stats to service, but does not cause a force polling. Note that this can only be
+         * called on the handler thread.
+         */
+        public void pushTetherStats() {
+            // TODO: remove the accumulated stats and report the diff from HAL directly.
+            if (null == mStatsProviderCb) return;
+            final NetworkStats ifaceDiff =
+                    getTetherStats(StatsType.STATS_PER_IFACE).subtract(mIfaceStats);
+            final NetworkStats uidDiff =
+                    getTetherStats(StatsType.STATS_PER_UID).subtract(mUidStats);
+            try {
+                mStatsProviderCb.onStatsUpdated(0 /* token */, ifaceDiff, uidDiff);
+                mIfaceStats = mIfaceStats.add(ifaceDiff);
+                mUidStats = mUidStats.add(uidDiff);
+            } catch (RuntimeException e) {
+                mLog.e("Cannot report network stats: ", e);
+            }
+        }
+
+        @Override
+        public void requestStatsUpdate(int token) {
+            mLog.i("requestStatsUpdate: " + token);
+            // Do not attempt to update stats by querying the offload HAL
+            // synchronously from a different thread than the Handler thread. http://b/64771555.
+            mHandler.post(() -> {
+                updateStatsForCurrentUpstream();
+                pushTetherStats();
+            });
+        }
+
+        @Override
+        public void setAlert(long quotaBytes) {
+            // TODO: Ask offload HAL to notify alert without stopping traffic.
+        }
     }
 
     private String currentUpstreamInterface() {
@@ -353,14 +393,6 @@
         }
     }
 
-    private void forceTetherStatsPoll() {
-        try {
-            mNms.tetherLimitReached(mStatsProvider);
-        } catch (RemoteException e) {
-            mLog.e("Cannot report data limit reached: " + e);
-        }
-    }
-
     /** Set current tethering upstream LinkProperties. */
     public void setUpstreamLinkProperties(LinkProperties lp) {
         if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return;
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
index 4a8ef1f..90b9d3f 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -29,6 +29,8 @@
 import android.os.RemoteException;
 import android.system.OsConstants;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
 
 
@@ -91,6 +93,12 @@
             txBytes = 0;
         }
 
+        @VisibleForTesting
+        public ForwardedStats(long rxBytes, long txBytes) {
+            this.rxBytes = rxBytes;
+            this.txBytes = txBytes;
+        }
+
         /** Add Tx/Rx bytes. */
         public void add(ForwardedStats other) {
             rxBytes += other.rxBytes;
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index 038d7ae..37e0cc1 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -20,6 +20,7 @@
 import static android.hardware.usb.UsbManager.USB_CONFIGURED;
 import static android.hardware.usb.UsbManager.USB_CONNECTED;
 import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
+import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
 import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
@@ -53,6 +54,7 @@
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.usage.NetworkStatsManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothPan;
 import android.bluetooth.BluetoothProfile;
@@ -63,9 +65,8 @@
 import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.hardware.usb.UsbManager;
+import android.net.ConnectivityManager;
 import android.net.INetd;
-import android.net.INetworkPolicyManager;
-import android.net.INetworkStatsService;
 import android.net.ITetheringEventCallback;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
@@ -176,8 +177,6 @@
     private final Context mContext;
     private final ArrayMap<String, TetherState> mTetherStates;
     private final BroadcastReceiver mStateReceiver;
-    private final INetworkStatsService mStatsService;
-    private final INetworkPolicyManager mPolicyManager;
     private final Looper mLooper;
     private final StateMachine mTetherMasterSM;
     private final OffloadController mOffloadController;
@@ -207,13 +206,12 @@
     private boolean mWifiTetherRequested;
     private Network mTetherUpstream;
     private TetherStatesParcel mTetherStatesParcel;
+    private boolean mDataSaverEnabled = false;
 
     public Tethering(TetheringDependencies deps) {
         mLog.mark("Tethering.constructed");
         mDeps = deps;
         mContext = mDeps.getContext();
-        mStatsService = mDeps.getINetworkStatsService();
-        mPolicyManager = mDeps.getINetworkPolicyManager();
         mNetd = mDeps.getINetd(mContext);
         mLooper = mDeps.getTetheringLooper();
 
@@ -224,10 +222,12 @@
         mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps);
         mTetherMasterSM.start();
 
+        final NetworkStatsManager statsManager =
+                (NetworkStatsManager) mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
         mHandler = mTetherMasterSM.getHandler();
         mOffloadController = new OffloadController(mHandler,
                 mDeps.getOffloadHardwareInterface(mHandler, mLog), mContext.getContentResolver(),
-                mDeps.getINetworkManagementService(), mLog);
+                statsManager, mLog);
         mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
                 TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
         mForwardedDownstreams = new HashSet<>();
@@ -264,7 +264,7 @@
         }
 
         final UserManager userManager = (UserManager) mContext.getSystemService(
-                    Context.USER_SERVICE);
+                Context.USER_SERVICE);
         mTetheringRestriction = new UserRestrictionActionListener(userManager, this);
         final TetheringThreadExecutor executor = new TetheringThreadExecutor(mHandler);
         mActiveDataSubIdListener = new ActiveDataSubIdListener(executor);
@@ -288,6 +288,7 @@
         filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
         filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
         filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED);
+        filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED);
         mContext.registerReceiver(mStateReceiver, filter, null, handler);
     }
 
@@ -484,7 +485,7 @@
     }
 
     private void setBluetoothTethering(final boolean enable, final ResultReceiver receiver) {
-        final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        final BluetoothAdapter adapter = mDeps.getBluetoothAdapter();
         if (adapter == null || !adapter.isEnabled()) {
             Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: "
                     + (adapter == null));
@@ -740,8 +741,7 @@
                 .setContentIntent(pi);
         mLastNotificationId = id;
 
-        notificationManager.notify(null, mLastNotificationId,
-                mTetheredNotificationBuilder.buildInto(new Notification()));
+        notificationManager.notify(null, mLastNotificationId, mTetheredNotificationBuilder.build());
     }
 
     @VisibleForTesting
@@ -775,6 +775,9 @@
             } else if (action.equals(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)) {
                 mLog.log("OBSERVED user restrictions changed");
                 handleUserRestrictionAction();
+            } else if (action.equals(ACTION_RESTRICT_BACKGROUND_CHANGED)) {
+                mLog.log("OBSERVED data saver changed");
+                handleDataSaverChanged();
             }
         }
 
@@ -885,6 +888,20 @@
         private void handleUserRestrictionAction() {
             mTetheringRestriction.onUserRestrictionsChanged();
         }
+
+        private void handleDataSaverChanged() {
+            final ConnectivityManager connMgr = (ConnectivityManager) mContext.getSystemService(
+                    Context.CONNECTIVITY_SERVICE);
+            final boolean isDataSaverEnabled = connMgr.getRestrictBackgroundStatus()
+                    != ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+
+            if (mDataSaverEnabled == isDataSaverEnabled) return;
+
+            mDataSaverEnabled = isDataSaverEnabled;
+            if (mDataSaverEnabled) {
+                untetherAll();
+            }
+        }
     }
 
     @VisibleForTesting
@@ -1982,15 +1999,6 @@
 
         mLog.log(String.format("OBSERVED iface=%s state=%s error=%s", iface, state, error));
 
-        try {
-            // Notify that we're tethering (or not) this interface.
-            // This is how data saver for instance knows if the user explicitly
-            // turned on tethering (thus keeping us from being in data saver mode).
-            mPolicyManager.onTetheringChanged(iface, state == IpServer.STATE_TETHERED);
-        } catch (RemoteException e) {
-            // Not really very much we can do here.
-        }
-
         // If TetherMasterSM is in ErrorState, TetherMasterSM stays there.
         // Thus we give a chance for TetherMasterSM to recover to InitialState
         // by sending CMD_CLEAR_ERROR
@@ -2054,7 +2062,7 @@
 
         mLog.log("adding TetheringInterfaceStateMachine for: " + iface);
         final TetherState tetherState = new TetherState(
-                new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mStatsService,
+                new IpServer(iface, mLooper, interfaceType, mLog, mNetd,
                              makeControlCallback(), mConfig.enableLegacyDhcpServer,
                              mDeps.getIpServerDependencies()));
         mTetherStates.put(iface, tetherState);
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
index dbe7892..068c346 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -23,18 +23,6 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
 
-import static com.android.internal.R.array.config_mobile_hotspot_provision_app;
-import static com.android.internal.R.array.config_tether_bluetooth_regexs;
-import static com.android.internal.R.array.config_tether_dhcp_range;
-import static com.android.internal.R.array.config_tether_upstream_types;
-import static com.android.internal.R.array.config_tether_usb_regexs;
-import static com.android.internal.R.array.config_tether_wifi_p2p_regexs;
-import static com.android.internal.R.array.config_tether_wifi_regexs;
-import static com.android.internal.R.bool.config_tether_upstream_automatic;
-import static com.android.internal.R.integer.config_mobile_hotspot_provision_check_period;
-import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui;
-import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
-
 import android.content.Context;
 import android.content.res.Resources;
 import android.net.TetheringConfigurationParcel;
@@ -45,6 +33,7 @@
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.networkstack.tethering.R;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -113,27 +102,30 @@
         activeDataSubId = id;
         Resources res = getResources(ctx, activeDataSubId);
 
-        tetherableUsbRegexs = getResourceStringArray(res, config_tether_usb_regexs);
+        tetherableUsbRegexs = getResourceStringArray(res, R.array.config_tether_usb_regexs);
         // TODO: Evaluate deleting this altogether now that Wi-Fi always passes
         // us an interface name. Careful consideration needs to be given to
         // implications for Settings and for provisioning checks.
-        tetherableWifiRegexs = getResourceStringArray(res, config_tether_wifi_regexs);
-        tetherableWifiP2pRegexs = getResourceStringArray(res, config_tether_wifi_p2p_regexs);
-        tetherableBluetoothRegexs = getResourceStringArray(res, config_tether_bluetooth_regexs);
+        tetherableWifiRegexs = getResourceStringArray(res, R.array.config_tether_wifi_regexs);
+        tetherableWifiP2pRegexs = getResourceStringArray(
+                res, R.array.config_tether_wifi_p2p_regexs);
+        tetherableBluetoothRegexs = getResourceStringArray(
+                res, R.array.config_tether_bluetooth_regexs);
 
         isDunRequired = checkDunRequired(ctx);
 
-        chooseUpstreamAutomatically = getResourceBoolean(res, config_tether_upstream_automatic);
+        chooseUpstreamAutomatically = getResourceBoolean(
+                res, R.bool.config_tether_upstream_automatic);
         preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired);
 
         legacyDhcpRanges = getLegacyDhcpRanges(res);
         defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
         enableLegacyDhcpServer = getEnableLegacyDhcpServer(res);
 
-        provisioningApp = getResourceStringArray(res, config_mobile_hotspot_provision_app);
+        provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app);
         provisioningAppNoUi = getProvisioningAppNoUi(res);
         provisioningCheckPeriod = getResourceInteger(res,
-                config_mobile_hotspot_provision_check_period,
+                R.integer.config_mobile_hotspot_provision_check_period,
                 0 /* No periodic re-check */);
 
         configLog.log(toString());
@@ -248,7 +240,7 @@
     }
 
     private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) {
-        final int[] ifaceTypes = res.getIntArray(config_tether_upstream_types);
+        final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types);
         final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
         for (int i : ifaceTypes) {
             switch (i) {
@@ -298,7 +290,7 @@
     }
 
     private static String[] getLegacyDhcpRanges(Resources res) {
-        final String[] fromResource = getResourceStringArray(res, config_tether_dhcp_range);
+        final String[] fromResource = getResourceStringArray(res, R.array.config_tether_dhcp_range);
         if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) {
             return fromResource;
         }
@@ -307,7 +299,7 @@
 
     private static String getProvisioningAppNoUi(Resources res) {
         try {
-            return res.getString(config_mobile_hotspot_provision_app_no_ui);
+            return res.getString(R.string.config_mobile_hotspot_provision_app_no_ui);
         } catch (Resources.NotFoundException e) {
             return "";
         }
@@ -339,7 +331,7 @@
     }
 
     private boolean getEnableLegacyDhcpServer(final Resources res) {
-        return getResourceBoolean(res, config_tether_enable_legacy_dhcp_server)
+        return getResourceBoolean(res, R.bool.config_tether_enable_legacy_dhcp_server)
                 || getDeviceConfigBoolean(TETHER_ENABLE_LEGACY_DHCP_SERVER);
     }
 
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
index b16b329..e019c3a 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -16,18 +16,15 @@
 
 package com.android.server.connectivity.tethering;
 
+import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
 import android.net.INetd;
-import android.net.INetworkPolicyManager;
-import android.net.INetworkStatsService;
 import android.net.NetworkRequest;
 import android.net.ip.IpServer;
 import android.net.util.SharedLog;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.INetworkManagementService;
 import android.os.Looper;
-import android.os.ServiceManager;
 
 import com.android.internal.util.StateMachine;
 
@@ -97,33 +94,6 @@
     }
 
     /**
-     * Get a reference to INetworkManagementService to registerTetheringStatsProvider from
-     * OffloadController. Note: This should be removed soon by Usage refactor work in R
-     * development cycle.
-     */
-    public INetworkManagementService getINetworkManagementService() {
-        return INetworkManagementService.Stub.asInterface(
-                ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
-    }
-
-    /**
-     *  Get a reference to INetworkStatsService to force update tethering usage.
-     *  Note: This should be removed in R development cycle.
-     */
-    public INetworkStatsService getINetworkStatsService() {
-        return INetworkStatsService.Stub.asInterface(
-                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
-    }
-
-    /**
-     * Get a reference to INetworkPolicyManager to be used by tethering.
-     */
-    public INetworkPolicyManager getINetworkPolicyManager() {
-        return INetworkPolicyManager.Stub.asInterface(
-                ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
-    }
-
-    /**
      * Get a reference to INetd to be used by tethering.
      */
     public INetd getINetd(Context context) {
@@ -140,4 +110,9 @@
      *  Get Context of TetheringSerice.
      */
     public abstract Context getContext();
+
+    /**
+     * Get a reference to BluetoothAdapter to be used by tethering.
+     */
+    public abstract BluetoothAdapter getBluetoothAdapter();
 }
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
index d5cdd8a..4dd6830 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
@@ -22,6 +22,8 @@
 import android.net.RouteInfo;
 import android.net.util.InterfaceSet;
 
+import com.android.net.module.util.NetUtils;
+
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 
@@ -85,7 +87,7 @@
 
     private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) {
         final RouteInfo ri = (lp != null)
-                ? RouteInfo.selectBestRoute(lp.getAllRoutes(), dst)
+                ? NetUtils.selectBestRoute(lp.getAllRoutes(), dst)
                 : null;
         return (ri != null) ? ri.getInterface() : null;
     }
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
index e4e4a09..cb7d392 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -24,6 +24,7 @@
 import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR;
 
 import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
 import android.content.Intent;
 import android.net.IIntResultListener;
@@ -42,7 +43,6 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -363,7 +363,7 @@
                     IBinder connector;
                     try {
                         final long before = System.currentTimeMillis();
-                        while ((connector = ServiceManager.getService(
+                        while ((connector = (IBinder) mContext.getSystemService(
                                 Context.NETWORK_STACK_SERVICE)) == null) {
                             if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
                                 Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector");
@@ -377,6 +377,11 @@
                     }
                     return INetworkStackConnector.Stub.asInterface(connector);
                 }
+
+                @Override
+                public BluetoothAdapter getBluetoothAdapter() {
+                    return BluetoothAdapter.getDefaultAdapter();
+                }
             };
         }
         return mDeps;
diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 65a0ac1..1f50b6b 100644
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -52,7 +52,6 @@
 import static org.mockito.Mockito.when;
 
 import android.net.INetd;
-import android.net.INetworkStatsService;
 import android.net.InterfaceConfigurationParcel;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
@@ -99,7 +98,6 @@
     private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
 
     @Mock private INetd mNetd;
-    @Mock private INetworkStatsService mStatsService;
     @Mock private IpServer.Callback mCallback;
     @Mock private SharedLog mSharedLog;
     @Mock private IDhcpServer mDhcpServer;
@@ -139,13 +137,13 @@
             mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH;
         }
         mIpServer = new IpServer(
-                IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mStatsService,
+                IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd,
                 mCallback, usingLegacyDhcp, mDependencies);
         mIpServer.start();
         // Starting the state machine always puts us in a consistent state and notifies
         // the rest of the world that we've changed from an unknown to available state.
         mLooper.dispatchAll();
-        reset(mNetd, mStatsService, mCallback);
+        reset(mNetd, mCallback);
 
         when(mRaDaemon.start()).thenReturn(true);
     }
@@ -162,7 +160,7 @@
         if (upstreamIface != null) {
             dispatchTetherConnectionChanged(upstreamIface);
         }
-        reset(mNetd, mStatsService, mCallback);
+        reset(mNetd, mCallback);
     }
 
     @Before public void setUp() throws Exception {
@@ -173,13 +171,13 @@
     @Test
     public void startsOutAvailable() {
         mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
-                mNetd, mStatsService, mCallback, false /* usingLegacyDhcp */, mDependencies);
+                mNetd, mCallback, false /* usingLegacyDhcp */, mDependencies);
         mIpServer.start();
         mLooper.dispatchAll();
         verify(mCallback).updateInterfaceState(
                 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
         verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
-        verifyNoMoreInteractions(mCallback, mNetd, mStatsService);
+        verifyNoMoreInteractions(mCallback, mNetd);
     }
 
     @Test
@@ -198,7 +196,7 @@
             // None of these commands should trigger us to request action from
             // the rest of the system.
             dispatchCommand(command);
-            verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+            verifyNoMoreInteractions(mNetd, mCallback);
         }
     }
 
@@ -210,7 +208,7 @@
         verify(mCallback).updateInterfaceState(
                 mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
         verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
-        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback);
     }
 
     @Test
@@ -228,7 +226,7 @@
                 mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
         inOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), any(LinkProperties.class));
-        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback);
     }
 
     @Test
@@ -236,7 +234,7 @@
         initTetheredStateMachine(TETHERING_BLUETOOTH, null);
 
         dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
-        InOrder inOrder = inOrder(mNetd, mStatsService, mCallback);
+        InOrder inOrder = inOrder(mNetd, mCallback);
         inOrder.verify(mNetd).tetherApplyDnsInterfaces();
         inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
         inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
@@ -245,7 +243,7 @@
                 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
         inOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), any(LinkProperties.class));
-        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback);
     }
 
     @Test
@@ -265,7 +263,7 @@
         inOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), mLinkPropertiesCaptor.capture());
         assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
-        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback);
     }
 
     @Test
@@ -285,7 +283,7 @@
         inOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), mLinkPropertiesCaptor.capture());
         assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
-        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback);
     }
 
     @Test
@@ -298,7 +296,7 @@
         InOrder inOrder = inOrder(mNetd);
         inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
-        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback);
     }
 
     @Test
@@ -306,13 +304,12 @@
         initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
 
         dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
-        InOrder inOrder = inOrder(mNetd, mStatsService);
-        inOrder.verify(mStatsService).forceUpdate();
+        InOrder inOrder = inOrder(mNetd);
         inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
         inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
-        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback);
     }
 
     @Test
@@ -322,12 +319,10 @@
         doThrow(RemoteException.class).when(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
 
         dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
-        InOrder inOrder = inOrder(mNetd, mStatsService);
-        inOrder.verify(mStatsService).forceUpdate();
+        InOrder inOrder = inOrder(mNetd);
         inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
-        inOrder.verify(mStatsService).forceUpdate();
         inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
         inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2);
     }
@@ -340,13 +335,11 @@
                 IFACE_NAME, UPSTREAM_IFACE2);
 
         dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
-        InOrder inOrder = inOrder(mNetd, mStatsService);
-        inOrder.verify(mStatsService).forceUpdate();
+        InOrder inOrder = inOrder(mNetd);
         inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
         inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
-        inOrder.verify(mStatsService).forceUpdate();
         inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
         inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2);
     }
@@ -356,8 +349,7 @@
         initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
 
         dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
-        InOrder inOrder = inOrder(mNetd, mStatsService, mCallback);
-        inOrder.verify(mStatsService).forceUpdate();
+        InOrder inOrder = inOrder(mNetd, mCallback);
         inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).tetherApplyDnsInterfaces();
@@ -368,7 +360,7 @@
                 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
         inOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), any(LinkProperties.class));
-        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback);
     }
 
     @Test
@@ -435,11 +427,11 @@
     public void ignoresDuplicateUpstreamNotifications() throws Exception {
         initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
 
-        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback);
 
         for (int i = 0; i < 5; i++) {
             dispatchTetherConnectionChanged(UPSTREAM_IFACE);
-            verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+            verifyNoMoreInteractions(mNetd, mCallback);
         }
     }
 
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
index 79bba7f..4f07461 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -27,7 +27,6 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -56,10 +55,10 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.R;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.internal.util.test.BroadcastInterceptingContext;
+import com.android.networkstack.tethering.R;
 
 import org.junit.After;
 import org.junit.Before;
@@ -157,16 +156,18 @@
                 eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
 
         when(mResources.getStringArray(R.array.config_tether_dhcp_range))
-            .thenReturn(new String[0]);
+                .thenReturn(new String[0]);
         when(mResources.getStringArray(R.array.config_tether_usb_regexs))
-            .thenReturn(new String[0]);
+                .thenReturn(new String[0]);
         when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
-            .thenReturn(new String[0]);
+                .thenReturn(new String[0]);
         when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
-            .thenReturn(new String[0]);
+                .thenReturn(new String[0]);
         when(mResources.getIntArray(R.array.config_tether_upstream_types))
-            .thenReturn(new int[0]);
-        when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
+                .thenReturn(new int[0]);
+        when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+                false);
+        when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn("");
         when(mLog.forSubComponent(anyString())).thenReturn(mLog);
 
         mMockContext = new MockContext(mContext);
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
index 7886ca6..7e62e5a 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
@@ -16,21 +16,26 @@
 
 package com.android.server.connectivity.tethering;
 
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
 import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.STATS_PER_IFACE;
-import static android.net.NetworkStats.STATS_PER_UID;
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
+import static android.net.NetworkStats.UID_TETHERING;
 import static android.net.RouteInfo.RTN_UNICAST;
-import static android.net.TrafficStats.UID_TETHERING;
 import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
 
+import static com.android.server.connectivity.tethering.OffloadController.StatsType.STATS_PER_IFACE;
+import static com.android.server.connectivity.tethering.OffloadController.StatsType.STATS_PER_UID;
 import static com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats;
 import static com.android.testutils.MiscAssertsKt.assertContainsAll;
 import static com.android.testutils.MiscAssertsKt.assertThrows;
+import static com.android.testutils.NetworkStatsUtilsKt.orderInsensitiveEquals;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.anyObject;
@@ -39,11 +44,14 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
+import android.app.usage.NetworkStatsManager;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.net.ITetheringStatsProvider;
@@ -51,10 +59,12 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkStats;
+import android.net.NetworkStats.Entry;
 import android.net.RouteInfo;
+import android.net.netstats.provider.AbstractNetworkStatsProvider;
+import android.net.netstats.provider.NetworkStatsProviderCallback;
 import android.net.util.SharedLog;
 import android.os.Handler;
-import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
@@ -97,11 +107,13 @@
     @Mock private OffloadHardwareInterface mHardware;
     @Mock private ApplicationInfo mApplicationInfo;
     @Mock private Context mContext;
-    @Mock private INetworkManagementService mNMService;
+    @Mock private NetworkStatsManager mStatsManager;
+    @Mock private NetworkStatsProviderCallback mTetherStatsProviderCb;
     private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
             ArgumentCaptor.forClass(ArrayList.class);
-    private final ArgumentCaptor<ITetheringStatsProvider.Stub> mTetherStatsProviderCaptor =
-            ArgumentCaptor.forClass(ITetheringStatsProvider.Stub.class);
+    private final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider>
+            mTetherStatsProviderCaptor =
+            ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class);
     private final ArgumentCaptor<OffloadHardwareInterface.ControlCallback> mControlCallbackCaptor =
             ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class);
     private MockContentResolver mContentResolver;
@@ -114,6 +126,8 @@
         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
         when(mContext.getContentResolver()).thenReturn(mContentResolver);
         FakeSettingsProvider.clearSettingsProvider();
+        when(mStatsManager.registerNetworkStatsProvider(anyString(), any()))
+                .thenReturn(mTetherStatsProviderCb);
     }
 
     @After public void tearDown() throws Exception {
@@ -139,9 +153,9 @@
 
     private OffloadController makeOffloadController() throws Exception {
         OffloadController offload = new OffloadController(new Handler(Looper.getMainLooper()),
-                mHardware, mContentResolver, mNMService, new SharedLog("test"));
-        verify(mNMService).registerTetheringStatsProvider(
-                mTetherStatsProviderCaptor.capture(), anyString());
+                mHardware, mContentResolver, mStatsManager, new SharedLog("test"));
+        verify(mStatsManager).registerNetworkStatsProvider(anyString(),
+                mTetherStatsProviderCaptor.capture());
         return offload;
     }
 
@@ -384,12 +398,11 @@
         inOrder.verifyNoMoreInteractions();
     }
 
-    private void assertNetworkStats(String iface, ForwardedStats stats, NetworkStats.Entry entry) {
-        assertEquals(iface, entry.iface);
-        assertEquals(stats.rxBytes, entry.rxBytes);
-        assertEquals(stats.txBytes, entry.txBytes);
-        assertEquals(SET_DEFAULT, entry.set);
-        assertEquals(TAG_NONE, entry.tag);
+    private static @NonNull Entry buildTestEntry(@NonNull OffloadController.StatsType how,
+            @NonNull String iface, long rxBytes, long txBytes) {
+        return new Entry(iface, how == STATS_PER_IFACE ? UID_ALL : UID_TETHERING, SET_DEFAULT,
+                TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes, 0L,
+                txBytes, 0L, 0L);
     }
 
     @Test
@@ -400,19 +413,16 @@
         final OffloadController offload = makeOffloadController();
         offload.start();
 
+        final OffloadController.OffloadTetheringStatsProvider provider =
+                mTetherStatsProviderCaptor.getValue();
+
         final String ethernetIface = "eth1";
         final String mobileIface = "rmnet_data0";
 
-        ForwardedStats ethernetStats = new ForwardedStats();
-        ethernetStats.rxBytes = 12345;
-        ethernetStats.txBytes = 54321;
-
-        ForwardedStats mobileStats = new ForwardedStats();
-        mobileStats.rxBytes = 999;
-        mobileStats.txBytes = 99999;
-
-        when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats);
-        when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn(mobileStats);
+        when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
+                new ForwardedStats(12345, 54321));
+        when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn(
+                new ForwardedStats(999, 99999));
 
         InOrder inOrder = inOrder(mHardware);
 
@@ -432,10 +442,35 @@
         // Expect that we fetch stats from the previous upstream.
         inOrder.verify(mHardware, times(1)).getForwardedStats(eq(mobileIface));
 
-        ethernetStats = new ForwardedStats();
-        ethernetStats.rxBytes = 100000;
-        ethernetStats.txBytes = 100000;
-        when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats);
+        // Verify that the fetched stats are stored.
+        final NetworkStats ifaceStats = provider.getTetherStats(STATS_PER_IFACE);
+        final NetworkStats uidStats = provider.getTetherStats(STATS_PER_UID);
+        final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2)
+                .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
+                .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321));
+
+        final NetworkStats expectedUidStats = new NetworkStats(0L, 2)
+                .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
+                .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321));
+
+        assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStats));
+        assertTrue(orderInsensitiveEquals(expectedUidStats, uidStats));
+
+        final ArgumentCaptor<NetworkStats> ifaceStatsCaptor = ArgumentCaptor.forClass(
+                NetworkStats.class);
+        final ArgumentCaptor<NetworkStats> uidStatsCaptor = ArgumentCaptor.forClass(
+                NetworkStats.class);
+
+        // Force pushing stats update to verify the stats reported.
+        provider.pushTetherStats();
+        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(),
+                ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
+        assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStatsCaptor.getValue()));
+        assertTrue(orderInsensitiveEquals(expectedUidStats, uidStatsCaptor.getValue()));
+
+
+        when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
+                new ForwardedStats(100000, 100000));
         offload.setUpstreamLinkProperties(null);
         // Expect that we first clear the HAL's upstream parameters.
         inOrder.verify(mHardware, times(1)).setUpstreamParameters(
@@ -443,37 +478,38 @@
         // Expect that we fetch stats from the previous upstream.
         inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface));
 
-        ITetheringStatsProvider provider = mTetherStatsProviderCaptor.getValue();
-        NetworkStats stats = provider.getTetherStats(STATS_PER_IFACE);
-        NetworkStats perUidStats = provider.getTetherStats(STATS_PER_UID);
-        waitForIdle();
         // There is no current upstream, so no stats are fetched.
         inOrder.verify(mHardware, never()).getForwardedStats(any());
         inOrder.verifyNoMoreInteractions();
 
-        assertEquals(2, stats.size());
-        assertEquals(2, perUidStats.size());
+        // Verify that the stored stats is accumulated.
+        final NetworkStats ifaceStatsAccu = provider.getTetherStats(STATS_PER_IFACE);
+        final NetworkStats uidStatsAccu = provider.getTetherStats(STATS_PER_UID);
+        final NetworkStats expectedIfaceStatsAccu = new NetworkStats(0L, 2)
+                .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
+                .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321));
 
-        NetworkStats.Entry entry = null;
-        for (int i = 0; i < stats.size(); i++) {
-            assertEquals(UID_ALL, stats.getValues(i, entry).uid);
-            assertEquals(UID_TETHERING, perUidStats.getValues(i, entry).uid);
-        }
+        final NetworkStats expectedUidStatsAccu = new NetworkStats(0L, 2)
+                .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
+                .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321));
 
-        int ethernetPosition = ethernetIface.equals(stats.getValues(0, entry).iface) ? 0 : 1;
-        int mobilePosition = 1 - ethernetPosition;
+        assertTrue(orderInsensitiveEquals(expectedIfaceStatsAccu, ifaceStatsAccu));
+        assertTrue(orderInsensitiveEquals(expectedUidStatsAccu, uidStatsAccu));
 
-        entry = stats.getValues(mobilePosition, entry);
-        assertNetworkStats(mobileIface, mobileStats, entry);
-        entry = perUidStats.getValues(mobilePosition, entry);
-        assertNetworkStats(mobileIface, mobileStats, entry);
+        // Verify that only diff of stats is reported.
+        reset(mTetherStatsProviderCb);
+        provider.pushTetherStats();
+        final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2)
+                .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0))
+                .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000));
 
-        ethernetStats.rxBytes = 12345 + 100000;
-        ethernetStats.txBytes = 54321 + 100000;
-        entry = stats.getValues(ethernetPosition, entry);
-        assertNetworkStats(ethernetIface, ethernetStats, entry);
-        entry = perUidStats.getValues(ethernetPosition, entry);
-        assertNetworkStats(ethernetIface, ethernetStats, entry);
+        final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2)
+                .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0))
+                .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000));
+        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(),
+                ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
+        assertTrue(orderInsensitiveEquals(expectedIfaceStatsDiff, ifaceStatsCaptor.getValue()));
+        assertTrue(orderInsensitiveEquals(expectedUidStatsDiff, uidStatsCaptor.getValue()));
     }
 
     @Test
@@ -493,19 +529,19 @@
         lp.setInterfaceName(ethernetIface);
         offload.setUpstreamLinkProperties(lp);
 
-        ITetheringStatsProvider provider = mTetherStatsProviderCaptor.getValue();
+        AbstractNetworkStatsProvider provider = mTetherStatsProviderCaptor.getValue();
         final InOrder inOrder = inOrder(mHardware);
         when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true);
         when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
 
         // Applying an interface quota to the current upstream immediately sends it to the hardware.
-        provider.setInterfaceQuota(ethernetIface, ethernetLimit);
+        provider.setLimit(ethernetIface, ethernetLimit);
         waitForIdle();
         inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit);
         inOrder.verifyNoMoreInteractions();
 
         // Applying an interface quota to another upstream does not take any immediate action.
-        provider.setInterfaceQuota(mobileIface, mobileLimit);
+        provider.setLimit(mobileIface, mobileLimit);
         waitForIdle();
         inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
 
@@ -518,7 +554,7 @@
 
         // Setting a limit of ITetheringStatsProvider.QUOTA_UNLIMITED causes the limit to be set
         // to Long.MAX_VALUE.
-        provider.setInterfaceQuota(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
+        provider.setLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
         waitForIdle();
         inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE);
 
@@ -526,7 +562,7 @@
         when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false);
         lp.setInterfaceName(ethernetIface);
         offload.setUpstreamLinkProperties(lp);
-        provider.setInterfaceQuota(mobileIface, mobileLimit);
+        provider.setLimit(mobileIface, mobileLimit);
         waitForIdle();
         inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
 
@@ -535,7 +571,7 @@
         when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false);
         lp.setInterfaceName(mobileIface);
         offload.setUpstreamLinkProperties(lp);
-        provider.setInterfaceQuota(mobileIface, mobileLimit);
+        provider.setLimit(mobileIface, mobileLimit);
         waitForIdle();
         inOrder.verify(mHardware).getForwardedStats(ethernetIface);
         inOrder.verify(mHardware).stopOffloadControl();
@@ -551,7 +587,7 @@
 
         OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
         callback.onStoppedLimitReached();
-        verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue());
+        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
     }
 
     @Test
@@ -654,9 +690,10 @@
         // Verify forwarded stats behaviour.
         verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
         verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
+        // TODO: verify the exact stats reported.
+        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
+        verifyNoMoreInteractions(mTetherStatsProviderCb);
         verifyNoMoreInteractions(mHardware);
-        verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue());
-        verifyNoMoreInteractions(mNMService);
     }
 
     @Test
@@ -719,8 +756,8 @@
         // Verify forwarded stats behaviour.
         verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
         verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
-        verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue());
-        verifyNoMoreInteractions(mNMService);
+        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
+        verifyNoMoreInteractions(mTetherStatsProviderCb);
 
         // TODO: verify local prefixes and downstreams are also pushed to the HAL.
         verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture());
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
index ef97ad4..3635964 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -26,13 +26,6 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.internal.R.array.config_mobile_hotspot_provision_app;
-import static com.android.internal.R.array.config_tether_bluetooth_regexs;
-import static com.android.internal.R.array.config_tether_dhcp_range;
-import static com.android.internal.R.array.config_tether_upstream_types;
-import static com.android.internal.R.array.config_tether_usb_regexs;
-import static com.android.internal.R.array.config_tether_wifi_regexs;
-import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -51,6 +44,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.util.test.BroadcastInterceptingContext;
+import com.android.networkstack.tethering.R;
 
 import org.junit.After;
 import org.junit.Before;
@@ -120,15 +114,18 @@
                 () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
                 eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
 
-        when(mResources.getStringArray(config_tether_dhcp_range)).thenReturn(new String[0]);
-        when(mResources.getStringArray(config_tether_usb_regexs)).thenReturn(new String[0]);
-        when(mResources.getStringArray(config_tether_wifi_regexs))
+        when(mResources.getStringArray(R.array.config_tether_dhcp_range)).thenReturn(
+                new String[0]);
+        when(mResources.getStringArray(R.array.config_tether_usb_regexs)).thenReturn(new String[0]);
+        when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
                 .thenReturn(new String[]{ "test_wlan\\d" });
-        when(mResources.getStringArray(config_tether_bluetooth_regexs)).thenReturn(new String[0]);
-        when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[0]);
-        when(mResources.getStringArray(config_mobile_hotspot_provision_app))
+        when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)).thenReturn(
+                new String[0]);
+        when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]);
+        when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
                 .thenReturn(new String[0]);
-        when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
+        when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+                false);
         mHasTelephonyManager = true;
         mMockContext = new MockContext(mContext);
         mEnableLegacyDhcpServer = false;
@@ -140,7 +137,7 @@
     }
 
     private TetheringConfiguration getTetheringConfiguration(int... legacyTetherUpstreamTypes) {
-        when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(
+        when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(
                 legacyTetherUpstreamTypes);
         return new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
     }
@@ -224,7 +221,7 @@
 
     @Test
     public void testNoDefinedUpstreamTypesAddsEthernet() {
-        when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[]{});
+        when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[]{});
         when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
 
         final TetheringConfiguration cfg = new TetheringConfiguration(
@@ -246,7 +243,7 @@
 
     @Test
     public void testDefinedUpstreamTypesSansEthernetAddsEthernet() {
-        when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(
+        when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(
                 new int[]{TYPE_WIFI, TYPE_MOBILE_HIPRI});
         when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
 
@@ -264,7 +261,7 @@
 
     @Test
     public void testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet() {
-        when(mResources.getIntArray(config_tether_upstream_types))
+        when(mResources.getIntArray(R.array.config_tether_upstream_types))
                 .thenReturn(new int[]{TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_HIPRI});
         when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
 
@@ -282,7 +279,8 @@
 
     @Test
     public void testNewDhcpServerDisabled() {
-        when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(true);
+        when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+                true);
         doReturn(false).when(
                 () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
                 eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
@@ -291,7 +289,8 @@
                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
         assertTrue(enableByRes.enableLegacyDhcpServer);
 
-        when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
+        when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+                false);
         doReturn(true).when(
                 () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
                 eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
@@ -303,7 +302,8 @@
 
     @Test
     public void testNewDhcpServerEnabled() {
-        when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
+        when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+                false);
         doReturn(false).when(
                 () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
                 eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
@@ -329,16 +329,17 @@
 
     private void setUpResourceForSubId() {
         when(mResourcesForSubId.getStringArray(
-                config_tether_dhcp_range)).thenReturn(new String[0]);
+                R.array.config_tether_dhcp_range)).thenReturn(new String[0]);
         when(mResourcesForSubId.getStringArray(
-                config_tether_usb_regexs)).thenReturn(new String[0]);
+                R.array.config_tether_usb_regexs)).thenReturn(new String[0]);
         when(mResourcesForSubId.getStringArray(
-                config_tether_wifi_regexs)).thenReturn(new String[]{ "test_wlan\\d" });
+                R.array.config_tether_wifi_regexs)).thenReturn(new String[]{ "test_wlan\\d" });
         when(mResourcesForSubId.getStringArray(
-                config_tether_bluetooth_regexs)).thenReturn(new String[0]);
-        when(mResourcesForSubId.getIntArray(config_tether_upstream_types)).thenReturn(new int[0]);
+                R.array.config_tether_bluetooth_regexs)).thenReturn(new String[0]);
+        when(mResourcesForSubId.getIntArray(R.array.config_tether_upstream_types)).thenReturn(
+                new int[0]);
         when(mResourcesForSubId.getStringArray(
-                config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME);
+                R.array.config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME);
     }
 
 }
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index 900c671..5910624 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -19,6 +19,9 @@
 import static android.hardware.usb.UsbManager.USB_CONFIGURED;
 import static android.hardware.usb.UsbManager.USB_CONNECTED;
 import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
+import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
 import static android.net.RouteInfo.RTN_UNICAST;
 import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
 import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
@@ -37,8 +40,6 @@
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
-import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
-
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -53,6 +54,7 @@
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
@@ -60,6 +62,8 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.app.usage.NetworkStatsManager;
+import android.bluetooth.BluetoothAdapter;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -68,9 +72,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.res.Resources;
 import android.hardware.usb.UsbManager;
+import android.net.ConnectivityManager;
 import android.net.INetd;
-import android.net.INetworkPolicyManager;
-import android.net.INetworkStatsService;
 import android.net.ITetheringEventCallback;
 import android.net.InetAddresses;
 import android.net.InterfaceConfigurationParcel;
@@ -99,7 +102,6 @@
 import android.net.wifi.p2p.WifiP2pManager;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
@@ -120,6 +122,7 @@
 import com.android.internal.util.StateMachine;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.networkstack.tethering.R;
 
 import org.junit.After;
 import org.junit.Before;
@@ -133,6 +136,7 @@
 import java.net.Inet6Address;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Vector;
 
 @RunWith(AndroidJUnit4.class)
@@ -151,9 +155,7 @@
 
     @Mock private ApplicationInfo mApplicationInfo;
     @Mock private Context mContext;
-    @Mock private INetworkManagementService mNMService;
-    @Mock private INetworkStatsService mStatsService;
-    @Mock private INetworkPolicyManager mPolicyManager;
+    @Mock private NetworkStatsManager mStatsManager;
     @Mock private OffloadHardwareInterface mOffloadHardwareInterface;
     @Mock private Resources mResources;
     @Mock private TelephonyManager mTelephonyManager;
@@ -167,6 +169,7 @@
     @Mock private INetd mNetd;
     @Mock private UserManager mUserManager;
     @Mock private NetworkRequest mNetworkRequest;
+    @Mock private ConnectivityManager mCm;
 
     private final MockIpServerDependencies mIpServerDependencies =
             spy(new MockIpServerDependencies());
@@ -217,6 +220,8 @@
             if (Context.USB_SERVICE.equals(name)) return mUsbManager;
             if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
             if (Context.USER_SERVICE.equals(name)) return mUserManager;
+            if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager;
+            if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
             return super.getSystemService(name);
         }
 
@@ -334,21 +339,6 @@
         }
 
         @Override
-        public INetworkManagementService getINetworkManagementService() {
-            return mNMService;
-        }
-
-        @Override
-        public INetworkStatsService getINetworkStatsService() {
-            return mStatsService;
-        }
-
-        @Override
-        public INetworkPolicyManager getINetworkPolicyManager() {
-            return mPolicyManager;
-        }
-
-        @Override
         public INetd getINetd(Context context) {
             return mNetd;
         }
@@ -362,6 +352,12 @@
         public Context getContext() {
             return mServiceContext;
         }
+
+        @Override
+        public BluetoothAdapter getBluetoothAdapter() {
+            // TODO: add test for bluetooth tethering.
+            return null;
+        }
     }
 
     private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4,
@@ -420,24 +416,24 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range))
+        when(mResources.getStringArray(R.array.config_tether_dhcp_range))
                 .thenReturn(new String[0]);
-        when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs))
+        when(mResources.getStringArray(R.array.config_tether_usb_regexs))
                 .thenReturn(new String[] { "test_rndis\\d" });
-        when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs))
+        when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
                 .thenReturn(new String[]{ "test_wlan\\d" });
-        when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_p2p_regexs))
+        when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
                 .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" });
-        when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs))
+        when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
                 .thenReturn(new String[0]);
-        when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
-                .thenReturn(new int[0]);
-        when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic))
-                .thenReturn(false);
-        when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
+        when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]);
+        when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(false);
+        when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+                false);
         when(mNetd.interfaceGetList())
                 .thenReturn(new String[] {
                         TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME});
+        when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn("");
         mInterfaceConfiguration = new InterfaceConfigurationParcel();
         mInterfaceConfiguration.flags = new String[0];
         when(mRouterAdvertisementDaemon.start())
@@ -457,7 +453,7 @@
         mServiceContext.registerReceiver(mBroadcastReceiver,
                 new IntentFilter(ACTION_TETHER_STATE_CHANGED));
         mTethering = makeTethering();
-        verify(mNMService).registerTetheringStatsProvider(any(), anyString());
+        verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any());
         verify(mNetd).registerUnsolicitedEventListener(any());
         final ArgumentCaptor<PhoneStateListener> phoneListenerCaptor =
                 ArgumentCaptor.forClass(PhoneStateListener.class);
@@ -700,7 +696,8 @@
 
     @Test
     public void workingMobileUsbTethering_IPv4LegacyDhcp() {
-        when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(true);
+        when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+                true);
         sendConfigurationChanged();
         final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
         runUsbTethering(upstreamState);
@@ -788,8 +785,7 @@
 
     @Test
     public void configTetherUpstreamAutomaticIgnoresConfigTetherUpstreamTypes() throws Exception {
-        when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic))
-                .thenReturn(true);
+        when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true);
         sendConfigurationChanged();
 
         // Setup IPv6
@@ -1317,7 +1313,7 @@
     private void workingWifiP2pGroupOwnerLegacyMode(
             boolean emulateInterfaceStatusChanged) throws Exception {
         // change to legacy mode and update tethering information by chaning SIM
-        when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_p2p_regexs))
+        when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
                 .thenReturn(new String[]{});
         final int fakeSubId = 1234;
         mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId);
@@ -1355,6 +1351,50 @@
         workingWifiP2pGroupClient(false);
     }
 
+    private void setDataSaverEnabled(boolean enabled) {
+        final Intent intent = new Intent(ACTION_RESTRICT_BACKGROUND_CHANGED);
+        mServiceContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+
+        final int status = enabled ? RESTRICT_BACKGROUND_STATUS_ENABLED
+                : RESTRICT_BACKGROUND_STATUS_DISABLED;
+        when(mCm.getRestrictBackgroundStatus()).thenReturn(status);
+        mLooper.dispatchAll();
+    }
+
+    @Test
+    public void testDataSaverChanged() {
+        // Start Tethering.
+        final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
+        runUsbTethering(upstreamState);
+        assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
+        // Data saver is ON.
+        setDataSaverEnabled(true);
+        // Verify that tethering should be disabled.
+        verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+        mTethering.interfaceRemoved(TEST_USB_IFNAME);
+        mLooper.dispatchAll();
+        assertEquals(mTethering.getTetheredIfaces(), new String[0]);
+        reset(mUsbManager);
+
+        runUsbTethering(upstreamState);
+        // Verify that user can start tethering again without turning OFF data saver.
+        assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
+
+        // If data saver is keep ON with change event, tethering should not be OFF this time.
+        setDataSaverEnabled(true);
+        verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+        assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
+
+        // If data saver is turned OFF, it should not change tethering.
+        setDataSaverEnabled(false);
+        verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+        assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
+    }
+
+    private static <T> void assertContains(Collection<T> collection, T element) {
+        assertTrue(element + " not found in " + collection, collection.contains(element));
+    }
+
     // TODO: Test that a request for hotspot mode doesn't interfere with an
     // already operating tethering mode interface.
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 5eaa80a..c3965c4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -36,8 +36,12 @@
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -54,6 +58,7 @@
 import android.view.Display;
 import android.view.KeyEvent;
 import android.view.MagnificationSpec;
+import android.view.SurfaceControl;
 import android.view.View;
 import android.view.WindowInfo;
 import android.view.accessibility.AccessibilityCache;
@@ -90,6 +95,7 @@
     private static final String LOG_TAG = "AbstractAccessibilityServiceConnection";
     private static final int WAIT_WINDOWS_TIMEOUT_MILLIS = 5000;
 
+    protected static final String TAKE_SCREENSHOT = "takeScreenshot";
     protected final Context mContext;
     protected final SystemSupport mSystemSupport;
     protected final WindowManagerInternal mWindowManagerService;
@@ -934,6 +940,54 @@
         mInvocationHandler.setSoftKeyboardCallbackEnabled(enabled);
     }
 
+    @Nullable
+    @Override
+    public Bitmap takeScreenshot(int displayId) {
+        synchronized (mLock) {
+            if (!hasRightsToCurrentUserLocked()) {
+                return null;
+            }
+
+            if (!mSecurityPolicy.canTakeScreenshotLocked(this)) {
+                return null;
+            }
+        }
+
+        if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
+            return null;
+        }
+
+        final Display display = DisplayManagerGlobal.getInstance()
+                .getRealDisplay(displayId);
+        if (display == null) {
+            return null;
+        }
+        final Point displaySize = new Point();
+        display.getRealSize(displaySize);
+
+        final int rotation = display.getRotation();
+        Bitmap screenShot = null;
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            final Rect crop = new Rect(0, 0, displaySize.x, displaySize.y);
+            // TODO (b/145893483): calling new API with the display as a parameter
+            // when surface control supported.
+            screenShot = SurfaceControl.screenshot(crop, displaySize.x, displaySize.y,
+                    rotation);
+            if (screenShot != null) {
+                // Optimization for telling the bitmap that all of the pixels are known to be
+                // opaque (false). This is meant as a drawing hint, as in some cases a bitmap
+                // that is known to be opaque can take a faster drawing case than one that may
+                // have non-opaque per-pixel alpha values.
+                screenShot.setHasAlpha(false);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+        return screenShot;
+    }
+
     @Override
     public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
@@ -1018,6 +1072,20 @@
         }
     }
 
+    /**
+     * Gets windowId of given token.
+     *
+     * @param token The token
+     * @return window id
+     */
+    @Override
+    public int getWindowIdForLeashToken(@NonNull IBinder token) {
+        synchronized (mLock) {
+            // TODO: Add a method to lookup window ID by given leash token.
+            return -1;
+        }
+    }
+
     public void resetLocked() {
         mSystemSupport.getKeyEventDispatcher().flush(this);
         try {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 58d3489..36a9e0eb 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -411,6 +411,7 @@
                     if (reboundAService) {
                         onUserStateChangedLocked(userState);
                     }
+                    migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, packageName);
                 }
             }
 
@@ -811,9 +812,7 @@
 
             userState.setTouchExplorationEnabledLocked(touchExplorationEnabled);
             userState.setDisplayMagnificationEnabledLocked(false);
-            userState.setNavBarMagnificationEnabledLocked(false);
             userState.disableShortcutMagnificationLocked();
-
             userState.setAutoclickEnabledLocked(false);
             userState.mEnabledServices.clear();
             userState.mEnabledServices.add(service);
@@ -853,17 +852,20 @@
      * navigation area has been clicked.
      *
      * @param displayId The logical display id.
+     * @param targetName The flattened {@link ComponentName} string or the class name of a system
+     *        class implementing a supported accessibility feature, or {@code null} if there's no
+     *        specified target.
      */
     @Override
-    public void notifyAccessibilityButtonClicked(int displayId) {
+    public void notifyAccessibilityButtonClicked(int displayId, String targetName) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Caller does not hold permission "
                     + android.Manifest.permission.STATUS_BAR_SERVICE);
         }
-        synchronized (mLock) {
-            notifyAccessibilityButtonClickedLocked(displayId);
-        }
+        mMainHandler.sendMessage(obtainMessage(
+                AccessibilityManagerService::performAccessibilityShortcutInternal, this,
+                displayId, ACCESSIBILITY_BUTTON, targetName));
     }
 
     /**
@@ -1023,6 +1025,7 @@
             // the state since the context in which the current user
             // state was used has changed since it was inactive.
             onUserStateChangedLocked(userState);
+            migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null);
 
             if (announceNewUser) {
                 // Schedule announcement of the current user if needed.
@@ -1132,66 +1135,6 @@
         }
     }
 
-    // TODO(a11y shortcut): Remove this function and Use #performAccessibilityShortcutInternal(
-    //  ACCESSIBILITY_BUTTON) instead, after the new Settings shortcut Ui merged.
-    private void notifyAccessibilityButtonClickedLocked(int displayId) {
-        final AccessibilityUserState state = getCurrentUserStateLocked();
-
-        int potentialTargets = state.isNavBarMagnificationEnabledLocked() ? 1 : 0;
-        for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
-            final AccessibilityServiceConnection service = state.mBoundServices.get(i);
-            if (service.mRequestAccessibilityButton) {
-                potentialTargets++;
-            }
-        }
-
-        if (potentialTargets == 0) {
-            return;
-        }
-        if (potentialTargets == 1) {
-            if (state.isNavBarMagnificationEnabledLocked()) {
-                mMainHandler.sendMessage(obtainMessage(
-                        AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this,
-                        displayId));
-                return;
-            } else {
-                for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
-                    final AccessibilityServiceConnection service = state.mBoundServices.get(i);
-                    if (service.mRequestAccessibilityButton) {
-                        service.notifyAccessibilityButtonClickedLocked(displayId);
-                        return;
-                    }
-                }
-            }
-        } else {
-            if (state.getServiceAssignedToAccessibilityButtonLocked() == null
-                    && !state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
-                mMainHandler.sendMessage(obtainMessage(
-                        AccessibilityManagerService::showAccessibilityTargetsSelection, this,
-                        displayId, ACCESSIBILITY_BUTTON));
-            } else if (state.isNavBarMagnificationEnabledLocked()
-                    && state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
-                mMainHandler.sendMessage(obtainMessage(
-                        AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this,
-                        displayId));
-                return;
-            } else {
-                for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
-                    final AccessibilityServiceConnection service = state.mBoundServices.get(i);
-                    if (service.mRequestAccessibilityButton && (service.mComponentName.equals(
-                            state.getServiceAssignedToAccessibilityButtonLocked()))) {
-                        service.notifyAccessibilityButtonClickedLocked(displayId);
-                        return;
-                    }
-                }
-            }
-            // The user may have turned off the assigned service or feature
-            mMainHandler.sendMessage(obtainMessage(
-                    AccessibilityManagerService::showAccessibilityTargetsSelection, this,
-                    displayId, ACCESSIBILITY_BUTTON));
-        }
-    }
-
     private void sendAccessibilityButtonToInputFilter(int displayId) {
         synchronized (mLock) {
             if (mHasInputFilter && mInputFilter != null) {
@@ -1635,8 +1578,7 @@
             if (userState.isDisplayMagnificationEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
             }
-            if (userState.isNavBarMagnificationEnabledLocked()
-                    || userState.isShortcutKeyMagnificationEnabledLocked()) {
+            if (userState.isShortcutMagnificationEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
             }
             if (userHasMagnificationServicesLocked(userState)) {
@@ -1886,14 +1828,8 @@
                 mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
                 0, userState.mUserId) == 1;
-        final boolean navBarMagnificationEnabled = Settings.Secure.getIntForUser(
-                mContext.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
-                0, userState.mUserId) == 1;
-        if ((displayMagnificationEnabled != userState.isDisplayMagnificationEnabledLocked())
-                || (navBarMagnificationEnabled != userState.isNavBarMagnificationEnabledLocked())) {
+        if ((displayMagnificationEnabled != userState.isDisplayMagnificationEnabledLocked())) {
             userState.setDisplayMagnificationEnabledLocked(displayMagnificationEnabled);
-            userState.setNavBarMagnificationEnabledLocked(navBarMagnificationEnabled);
             return true;
         }
         return false;
@@ -2088,8 +2024,7 @@
         // displays in one display. It's not a real display and there's no input events for it.
         final ArrayList<Display> displays = getValidDisplayList();
         if (userState.isDisplayMagnificationEnabledLocked()
-                || userState.isNavBarMagnificationEnabledLocked()
-                || userState.isShortcutKeyMagnificationEnabledLocked()) {
+                || userState.isShortcutMagnificationEnabledLocked()) {
             for (int i = 0; i < displays.size(); i++) {
                 final Display display = displays.get(i);
                 getMagnificationController().register(display.getDisplayId());
@@ -2208,6 +2143,85 @@
         scheduleNotifyClientsOfServicesStateChangeLocked(userState);
     }
 
+    /**
+     * 1) Check if the service assigned to accessibility button target sdk version > Q.
+     *    If it isn't, remove it from the list and associated setting.
+     *    (It happens when an accessibility service package is downgraded.)
+     * 2) Check if an enabled service targeting sdk version > Q and requesting a11y button is
+     *    assigned to a shortcut. If it isn't, assigns it to the accessibility button.
+     *    (It happens when an enabled accessibility service package is upgraded.)
+     *
+     * @param packageName The package name to check, or {@code null} to check all services.
+     */
+    private void migrateAccessibilityButtonSettingsIfNecessaryLocked(
+            AccessibilityUserState userState, @Nullable String packageName) {
+        final Set<String> buttonTargets =
+                userState.getShortcutTargetsLocked(ACCESSIBILITY_BUTTON);
+        int lastSize = buttonTargets.size();
+        buttonTargets.removeIf(name -> {
+            if (packageName != null && name != null && !name.contains(packageName)) {
+                return false;
+            }
+            final ComponentName componentName = ComponentName.unflattenFromString(name);
+            if (componentName == null) {
+                return false;
+            }
+            final AccessibilityServiceInfo serviceInfo =
+                    userState.getInstalledServiceInfoLocked(componentName);
+            if (serviceInfo == null) {
+                return false;
+            }
+            if (serviceInfo.getResolveInfo().serviceInfo.applicationInfo
+                    .targetSdkVersion > Build.VERSION_CODES.Q) {
+                return false;
+            }
+            // A11y services targeting sdk version <= Q should not be in the list.
+            return true;
+        });
+        boolean changed = (lastSize != buttonTargets.size());
+        lastSize = buttonTargets.size();
+
+        final Set<String> shortcutKeyTargets =
+                userState.getShortcutTargetsLocked(ACCESSIBILITY_SHORTCUT_KEY);
+        userState.mEnabledServices.forEach(componentName -> {
+            if (packageName != null && componentName != null
+                    && !packageName.equals(componentName.getPackageName())) {
+                return;
+            }
+            final AccessibilityServiceInfo serviceInfo =
+                    userState.getInstalledServiceInfoLocked(componentName);
+            if (serviceInfo == null) {
+                return;
+            }
+            final boolean requestA11yButton = (serviceInfo.flags
+                    & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+            if (!(serviceInfo.getResolveInfo().serviceInfo.applicationInfo
+                    .targetSdkVersion > Build.VERSION_CODES.Q && requestA11yButton)) {
+                return;
+            }
+            final String serviceName = serviceInfo.getComponentName().flattenToString();
+            if (TextUtils.isEmpty(serviceName)) {
+                return;
+            }
+            if (shortcutKeyTargets.contains(serviceName) || buttonTargets.contains(serviceName)) {
+                return;
+            }
+            // For enabled a11y services targeting sdk version > Q and requesting a11y button should
+            // be assigned to a shortcut.
+            buttonTargets.add(serviceName);
+        });
+        changed |= (lastSize != buttonTargets.size());
+        if (!changed) {
+            return;
+        }
+
+        // Update setting key with new value.
+        persistColonDelimitedSetToSettingLocked(
+                Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+                userState.mUserId, buttonTargets, str -> str);
+        scheduleNotifyClientsOfServicesStateChangeLocked(userState);
+    }
+
     private void updateRecommendedUiTimeoutLocked(AccessibilityUserState userState) {
         int newNonInteractiveUiTimeout = userState.getUserNonInteractiveUiTimeoutLocked();
         int newInteractiveUiTimeout = userState.getUserInteractiveUiTimeoutLocked();
@@ -2269,9 +2283,13 @@
      * AIDL-exposed method to be called when the accessibility shortcut key is enabled. Requires
      * permission to write secure settings, since someone with that permission can enable
      * accessibility services themselves.
+     *
+     * @param targetName The flattened {@link ComponentName} string or the class name of a system
+     *        class implementing a supported accessibility feature, or {@code null} if there's no
+     *        specified target.
      */
     @Override
-    public void performAccessibilityShortcut() {
+    public void performAccessibilityShortcut(String targetName) {
         if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)
                 && (mContext.checkCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
                 != PackageManager.PERMISSION_GRANTED)) {
@@ -2280,7 +2298,7 @@
         }
         mMainHandler.sendMessage(obtainMessage(
                 AccessibilityManagerService::performAccessibilityShortcutInternal, this,
-                Display.DEFAULT_DISPLAY, ACCESSIBILITY_SHORTCUT_KEY));
+                Display.DEFAULT_DISPLAY, ACCESSIBILITY_SHORTCUT_KEY, targetName));
     }
 
     /**
@@ -2288,20 +2306,31 @@
      *
      * @param shortcutType The shortcut type.
      * @param displayId The display id of the accessibility button.
+     * @param targetName The flattened {@link ComponentName} string or the class name of a system
+     *        class implementing a supported accessibility feature, or {@code null} if there's no
+     *        specified target.
      */
     private void performAccessibilityShortcutInternal(int displayId,
-            @ShortcutType int shortcutType) {
+            @ShortcutType int shortcutType, @Nullable String targetName) {
         final List<String> shortcutTargets = getAccessibilityShortcutTargetsInternal(shortcutType);
         if (shortcutTargets.isEmpty()) {
             Slog.d(LOG_TAG, "No target to perform shortcut, shortcutType=" + shortcutType);
             return;
         }
-        // In case there are many targets assigned to the given shortcut.
-        if (shortcutTargets.size() > 1) {
-            showAccessibilityTargetsSelection(displayId, shortcutType);
-            return;
+        // In case the caller specified a target name
+        if (targetName != null) {
+            if (!shortcutTargets.contains(targetName)) {
+                Slog.d(LOG_TAG, "Perform shortcut failed, invalid target name:" + targetName);
+                return;
+            }
+        } else {
+            // In case there are many targets assigned to the given shortcut.
+            if (shortcutTargets.size() > 1) {
+                showAccessibilityTargetsSelection(displayId, shortcutType);
+                return;
+            }
+            targetName = shortcutTargets.get(0);
         }
-        final String targetName = shortcutTargets.get(0);
         // In case user assigned magnification to the given shortcut.
         if (targetName.equals(MAGNIFICATION_CONTROLLER_NAME)) {
             sendAccessibilityButtonToInputFilter(displayId);
@@ -2844,11 +2873,6 @@
         private final Uri mDisplayMagnificationEnabledUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
 
-        // TODO(a11y shortcut): Remove this setting key, and have a migrate function in
-        //  Setting provider after new shortcut UI merged.
-        private final Uri mNavBarMagnificationEnabledUri = Settings.Secure.getUriFor(
-                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
-
         private final Uri mAutoclickEnabledUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED);
 
@@ -2888,8 +2912,6 @@
                     false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(mDisplayMagnificationEnabledUri,
                     false, this, UserHandle.USER_ALL);
-            contentResolver.registerContentObserver(mNavBarMagnificationEnabledUri,
-                    false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(mAutoclickEnabledUri,
                     false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(mEnabledAccessibilityServicesUri,
@@ -2924,8 +2946,7 @@
                     if (readTouchExplorationEnabledSettingLocked(userState)) {
                         onUserStateChangedLocked(userState);
                     }
-                } else if (mDisplayMagnificationEnabledUri.equals(uri)
-                        || mNavBarMagnificationEnabledUri.equals(uri)) {
+                } else if (mDisplayMagnificationEnabledUri.equals(uri)) {
                     if (readMagnificationEnabledSettingsLocked(userState)) {
                         onUserStateChangedLocked(userState);
                     }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index 2032109..7dbec7c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -326,6 +326,19 @@
     }
 
     /**
+     * Checks if a service can take screenshot.
+     *
+     * @param service The service requesting access
+     *
+     * @return Whether ot not the service may take screenshot
+     */
+    public boolean canTakeScreenshotLocked(
+            @NonNull AbstractAccessibilityServiceConnection service) {
+        return (service.getCapabilities()
+                & AccessibilityServiceInfo.CAPABILITY_CAN_TAKE_SCREENSHOT) != 0;
+    }
+
+    /**
      * Returns the parent userId of the profile according to the specified userId.
      *
      * @param userId The userId to check
@@ -426,6 +439,7 @@
                 return false;
             }
         }
+        // TODO: Check parent windowId if the giving windowId is from embedded view hierarchy.
         if (windowId == mAccessibilityWindowManager.getActiveWindowId(userId)) {
             return true;
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 37ac3ec..25911a7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -16,6 +16,8 @@
 
 package com.android.server.accessibility;
 
+import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT;
+
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
 import android.Manifest;
@@ -25,16 +27,20 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
+import android.graphics.Bitmap;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Process;
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Slog;
 import android.view.Display;
 
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
@@ -313,48 +319,16 @@
         }
     }
 
-    // TODO(a11y shortcut): Refactoring the logic here, after the new Settings shortcut Ui merged.
     public boolean isAccessibilityButtonAvailableLocked(AccessibilityUserState userState) {
         // If the service does not request the accessibility button, it isn't available
         if (!mRequestAccessibilityButton) {
             return false;
         }
-
         // If the accessibility button isn't currently shown, it cannot be available to services
         if (!mSystemSupport.isAccessibilityButtonShown()) {
             return false;
         }
-
-        // If magnification is on and assigned to the accessibility button, services cannot be
-        if (userState.isNavBarMagnificationEnabledLocked()
-                && userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
-            return false;
-        }
-
-        int requestingServices = 0;
-        for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
-            final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
-            if (service.mRequestAccessibilityButton) {
-                requestingServices++;
-            }
-        }
-
-        if (requestingServices == 1) {
-            // If only a single service is requesting, it must be this service, and the
-            // accessibility button is available to it
-            return true;
-        } else {
-            // With more than one active service, we derive the target from the user's settings
-            if (userState.getServiceAssignedToAccessibilityButtonLocked() == null) {
-                // If the user has not made an assignment, we treat the button as available to
-                // all services until the user interacts with the button to make an assignment
-                return true;
-            } else {
-                // If an assignment was made, it defines availability
-                return mComponentName.equals(
-                        userState.getServiceAssignedToAccessibilityButtonLocked());
-            }
-        }
+        return true;
     }
 
     @Override
@@ -419,4 +393,15 @@
             }
         }
     }
+
+    @Override
+    public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {
+        mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
+            final Bitmap screenshot = super.takeScreenshot(displayId);
+            // Send back the result.
+            final Bundle payload = new Bundle();
+            payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT, screenshot);
+            callback.sendResult(payload);
+        }, null).recycleOnUse());
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index a163f74..ebe2af6 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -100,7 +100,6 @@
     private boolean mIsAutoclickEnabled;
     private boolean mIsDisplayMagnificationEnabled;
     private boolean mIsFilterKeyEventsEnabled;
-    private boolean mIsNavBarMagnificationEnabled;
     private boolean mIsPerformGesturesEnabled;
     private boolean mIsTextHighContrastEnabled;
     private boolean mIsTouchExplorationEnabled;
@@ -153,7 +152,6 @@
         mAccessibilityButtonTargets.clear();
         mIsTouchExplorationEnabled = false;
         mIsDisplayMagnificationEnabled = false;
-        mIsNavBarMagnificationEnabled = false;
         mIsAutoclickEnabled = false;
         mUserNonInteractiveUiTimeout = 0;
         mUserInteractiveUiTimeout = 0;
@@ -435,8 +433,6 @@
         pw.append(", touchExplorationEnabled=").append(String.valueOf(mIsTouchExplorationEnabled));
         pw.append(", displayMagnificationEnabled=").append(String.valueOf(
                 mIsDisplayMagnificationEnabled));
-        pw.append(", navBarMagnificationEnabled=").append(String.valueOf(
-                mIsNavBarMagnificationEnabled));
         pw.append(", autoclickEnabled=").append(String.valueOf(mIsAutoclickEnabled));
         pw.append(", nonInteractiveUiTimeout=").append(String.valueOf(mNonInteractiveUiTimeout));
         pw.append(", interactiveUiTimeout=").append(String.valueOf(mInteractiveUiTimeout));
@@ -553,8 +549,12 @@
         mLastSentClientState = state;
     }
 
-    public boolean isShortcutKeyMagnificationEnabledLocked() {
-        return mAccessibilityShortcutKeyTargets.contains(MAGNIFICATION_CONTROLLER_NAME);
+    /**
+     * Returns true if navibar magnification or shortcut key magnification is enabled.
+     */
+    public boolean isShortcutMagnificationEnabledLocked() {
+        return mAccessibilityShortcutKeyTargets.contains(MAGNIFICATION_CONTROLLER_NAME)
+                || mAccessibilityButtonTargets.contains(MAGNIFICATION_CONTROLLER_NAME);
     }
 
     /**
@@ -690,28 +690,4 @@
     public void setUserNonInteractiveUiTimeoutLocked(int timeout) {
         mUserNonInteractiveUiTimeout = timeout;
     }
-
-    // TODO(a11y shortcut): These functions aren't necessary, after the new Settings shortcut Ui
-    //  is merged.
-    boolean isNavBarMagnificationEnabledLocked() {
-        return mIsNavBarMagnificationEnabled;
-    }
-
-    void setNavBarMagnificationEnabledLocked(boolean enabled) {
-        mIsNavBarMagnificationEnabled = enabled;
-    }
-
-    boolean isNavBarMagnificationAssignedToAccessibilityButtonLocked() {
-        return mAccessibilityButtonTargets.contains(MAGNIFICATION_CONTROLLER_NAME);
-    }
-
-    ComponentName getServiceAssignedToAccessibilityButtonLocked() {
-        final String targetName = mAccessibilityButtonTargets.isEmpty() ? null
-                : mAccessibilityButtonTargets.valueAt(0);
-        if (targetName == null) {
-            return null;
-        }
-        return ComponentName.unflattenFromString(targetName);
-    }
-    // TODO(a11y shortcut): End
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 7a8a112..5d9af26 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -25,6 +25,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.util.Slog;
 import android.view.Display;
@@ -325,5 +326,8 @@
 
         @Override
         public void onFingerprintGesture(int gesture) {}
+
+        @Override
+        public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {}
     }
 }
diff --git a/services/art-profile b/services/art-profile
index a0338d5..4e113c8 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -3066,18 +3066,18 @@
 HSPLcom/android/server/am/AppBindRecord;->dumpInIntentBind(Ljava/io/PrintWriter;Ljava/lang/String;)V
 PLcom/android/server/am/AppBindRecord;->toString()Ljava/lang/String;
 PLcom/android/server/am/AppBindRecord;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
-PLcom/android/server/am/AppCompactor$1;->onPropertyChanged(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-HSPLcom/android/server/am/AppCompactor$MemCompactionHandler;->handleMessage(Landroid/os/Message;)V
-HSPLcom/android/server/am/AppCompactor;-><init>(Lcom/android/server/am/ActivityManagerService;)V
-HSPLcom/android/server/am/AppCompactor;->access$1000(Lcom/android/server/am/AppCompactor;)V
-HSPLcom/android/server/am/AppCompactor;->access$700(Lcom/android/server/am/AppCompactor;)Lcom/android/server/am/ActivityManagerService;
-HSPLcom/android/server/am/AppCompactor;->access$800(Lcom/android/server/am/AppCompactor;)Ljava/util/ArrayList;
-HSPLcom/android/server/am/AppCompactor;->access$900(Lcom/android/server/am/AppCompactor;)Ljava/util/Random;
-PLcom/android/server/am/AppCompactor;->dump(Ljava/io/PrintWriter;)V
-HSPLcom/android/server/am/AppCompactor;->init()V
-HSPLcom/android/server/am/AppCompactor;->updateCompactionThrottles()V
-HSPLcom/android/server/am/AppCompactor;->updateUseCompaction()V
-HSPLcom/android/server/am/AppCompactor;->useCompaction()Z
+PLcom/android/server/am/CachedAppOptimizer$1;->onPropertyChanged(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+HSPLcom/android/server/am/CachedAppOptimizer$MemCompactionHandler;->handleMessage(Landroid/os/Message;)V
+HSPLcom/android/server/am/CachedAppOptimizer;-><init>(Lcom/android/server/am/ActivityManagerService;)V
+HSPLcom/android/server/am/CachedAppOptimizer;->access$1000(Lcom/android/server/am/CachedAppOptimizer;)V
+HSPLcom/android/server/am/CachedAppOptimizer;->access$700(Lcom/android/server/am/CachedAppOptimizer;)Lcom/android/server/am/ActivityManagerService;
+HSPLcom/android/server/am/CachedAppOptimizer;->access$800(Lcom/android/server/am/CachedAppOptimizer;)Ljava/util/ArrayList;
+HSPLcom/android/server/am/CachedAppOptimizer;->access$900(Lcom/android/server/am/CachedAppOptimizer;)Ljava/util/Random;
+PLcom/android/server/am/CachedAppOptimizer;->dump(Ljava/io/PrintWriter;)V
+HSPLcom/android/server/am/CachedAppOptimizer;->init()V
+HSPLcom/android/server/am/CachedAppOptimizer;->updateCompactionThrottles()V
+HSPLcom/android/server/am/CachedAppOptimizer;->updateUseCompaction()V
+HSPLcom/android/server/am/CachedAppOptimizer;->useCompaction()Z
 PLcom/android/server/am/AppErrorDialog$1;->handleMessage(Landroid/os/Message;)V
 PLcom/android/server/am/AppErrorDialog;-><init>(Landroid/content/Context;Lcom/android/server/am/ActivityManagerService;Lcom/android/server/am/AppErrorDialog$Data;)V
 PLcom/android/server/am/AppErrorDialog;->onClick(Landroid/view/View;)V
@@ -18632,9 +18632,9 @@
 Lcom/android/server/am/ActivityManagerService$UidObserverRegistration;
 Lcom/android/server/am/ActivityManagerService;
 Lcom/android/server/am/AppBindRecord;
-Lcom/android/server/am/AppCompactor$1;
-Lcom/android/server/am/AppCompactor$MemCompactionHandler;
-Lcom/android/server/am/AppCompactor;
+Lcom/android/server/am/CachedAppOptimizer$1;
+Lcom/android/server/am/CachedAppOptimizer$MemCompactionHandler;
+Lcom/android/server/am/CachedAppOptimizer;
 Lcom/android/server/am/AppErrorDialog$Data;
 Lcom/android/server/am/AppErrorResult;
 Lcom/android/server/am/AppErrors$BadProcessInfo;
diff --git a/services/art-profile-boot b/services/art-profile-boot
index e09424b..fe4178a 100644
--- a/services/art-profile-boot
+++ b/services/art-profile-boot
@@ -538,7 +538,7 @@
 Lcom/android/server/am/ActivityManagerService;->updateLowMemStateLocked(III)Z
 Lcom/android/server/wm/ConfigurationContainer;->getWindowConfiguration()Landroid/app/WindowConfiguration;
 Lcom/android/server/am/OomAdjuster;->applyOomAdjLocked(Lcom/android/server/am/ProcessRecord;ZJJ)Z
-Lcom/android/server/am/AppCompactor;->useCompaction()Z
+Lcom/android/server/am/CachedAppOptimizer;->useCompaction()Z
 Lcom/android/server/am/ProcessList;->procStatesDifferForMem(II)Z
 Lcom/android/server/am/ActivityManagerService;->dispatchUidsChanged()V
 Lcom/android/server/audio/AudioService$VolumeStreamState;->setIndex(IILjava/lang/String;)Z
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 7b95ab5..2c229b4 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -2538,7 +2538,6 @@
                 KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
             } else {
                 if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
-                synchronized (mQueueLock) {
                     // Fire the intent that kicks off the whole shebang...
                     try {
                         mRunBackupIntent.send();
@@ -2546,10 +2545,8 @@
                         // should never happen
                         Slog.e(TAG, "run-backup intent cancelled!");
                     }
-
                     // ...and cancel any pending scheduled job, because we've just superseded it
                     KeyValueBackupJob.cancel(mUserId, mContext);
-                }
             }
         } finally {
             Binder.restoreCallingIdentity(oldId);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 5435cba..c1e23e4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -79,7 +79,6 @@
 import android.net.IpMemoryStore;
 import android.net.IpPrefix;
 import android.net.LinkProperties;
-import android.net.LinkProperties.CompareResult;
 import android.net.MatchAllNetworkSpecifier;
 import android.net.NattSocketKeepalive;
 import android.net.Network;
@@ -87,7 +86,6 @@
 import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
 import android.net.NetworkConfig;
-import android.net.NetworkFactory;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkMonitorManager;
@@ -114,6 +112,7 @@
 import android.net.metrics.NetworkEvent;
 import android.net.netlink.InetDiagMessage;
 import android.net.shared.PrivateDnsConfig;
+import android.net.util.LinkPropertiesUtils.CompareResult;
 import android.net.util.MultinetworkPolicyTracker;
 import android.net.util.NetdService;
 import android.os.Binder;
@@ -2843,26 +2842,11 @@
             return true;
         }
 
-        // TODO: delete when direct use of registerNetworkFactory is no longer supported.
-        private boolean maybeHandleNetworkFactoryMessage(Message msg) {
-            switch (msg.what) {
-                default:
-                    return false;
-                case NetworkFactory.EVENT_UNFULFILLABLE_REQUEST: {
-                    handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.sendingUid,
-                            /* callOnUnavailable */ true);
-                    break;
-                }
-            }
-            return true;
-        }
-
         @Override
         public void handleMessage(Message msg) {
             if (!maybeHandleAsyncChannelMessage(msg)
                     && !maybeHandleNetworkMonitorMessage(msg)
-                    && !maybeHandleNetworkAgentInfoMessage(msg)
-                    && !maybeHandleNetworkFactoryMessage(msg)) {
+                    && !maybeHandleNetworkAgentInfoMessage(msg)) {
                 maybeHandleNetworkAgentMessage(msg);
             }
         }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index db54214..396b977 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2855,8 +2855,10 @@
      */
     @Override
     public void startCheckpoint(int numTries) throws RemoteException {
-        // Only the system process is permitted to start checkpoints
-        if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
+        // Only the root, system_server and shell processes are permitted to start checkpoints
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID
+                && callingUid != Process.SHELL_UID) {
             throw new SecurityException("no permission to start filesystem checkpoint");
         }
 
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index c0f43a8..e7f7846 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -25,11 +25,14 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
+import android.util.ArrayMap;
 import android.util.Slog;
 
 import com.android.server.SystemService.TargetUser;
 import com.android.server.utils.TimingsTraceAndSlog;
 
+import dalvik.system.PathClassLoader;
+
 import java.io.File;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
@@ -63,6 +66,9 @@
     // Services that should receive lifecycle events.
     private final ArrayList<SystemService> mServices = new ArrayList<SystemService>();
 
+    // Map of paths to PathClassLoader, so we don't load the same path multiple times.
+    private final ArrayMap<String, PathClassLoader> mLoadedPaths = new ArrayMap<>();
+
     private int mCurrentPhase = -1;
 
     private UserManagerInternal mUserManagerInternal;
@@ -76,20 +82,46 @@
      *
      * @return The service instance.
      */
-    @SuppressWarnings("unchecked")
     public SystemService startService(String className) {
-        final Class<SystemService> serviceClass;
+        final Class<SystemService> serviceClass = loadClassFromLoader(className,
+                this.getClass().getClassLoader());
+        return startService(serviceClass);
+    }
+
+    /**
+     * Starts a service by class name and a path that specifies the jar where the service lives.
+     *
+     * @return The service instance.
+     */
+    public SystemService startServiceFromJar(String className, String path) {
+        PathClassLoader pathClassLoader = mLoadedPaths.get(path);
+        if (pathClassLoader == null) {
+            // NB: the parent class loader should always be the system server class loader.
+            // Changing it has implications that require discussion with the mainline team.
+            pathClassLoader = new PathClassLoader(path, this.getClass().getClassLoader());
+            mLoadedPaths.put(path, pathClassLoader);
+        }
+        final Class<SystemService> serviceClass = loadClassFromLoader(className, pathClassLoader);
+        return startService(serviceClass);
+    }
+
+    /*
+     * Loads and initializes a class from the given classLoader. Returns the class.
+     */
+    @SuppressWarnings("unchecked")
+    private static Class<SystemService> loadClassFromLoader(String className,
+            ClassLoader classLoader) {
         try {
-            serviceClass = (Class<SystemService>)Class.forName(className);
+            return (Class<SystemService>) Class.forName(className, true, classLoader);
         } catch (ClassNotFoundException ex) {
-            Slog.i(TAG, "Starting " + className);
             throw new RuntimeException("Failed to create service " + className
-                    + ": service class not found, usually indicates that the caller should "
+                    + " from class loader " + classLoader.toString() + ": service class not "
+                    + "found, usually indicates that the caller should "
                     + "have called PackageManager.hasSystemFeature() to check whether the "
                     + "feature is available on this device before trying to start the "
-                    + "services that implement it", ex);
+                    + "services that implement it. Also ensure that the correct path for the "
+                    + "classloader is supplied, if applicable.", ex);
         }
-        return startService(serviceClass);
     }
 
     /**
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 27e0d52..5a56a9f 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -61,7 +61,6 @@
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.util.DebugUtils;
-import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.StatsLog;
@@ -164,8 +163,7 @@
     private int mHapticFeedbackIntensity;
     private int mNotificationIntensity;
     private int mRingIntensity;
-    private SparseArray<Pair<VibrationEffect, VibrationAttributes>> mAlwaysOnEffects =
-            new SparseArray<>();
+    private SparseArray<Vibration> mAlwaysOnEffects = new SparseArray<>();
 
     static native boolean vibratorExists();
     static native void vibratorInit();
@@ -461,6 +459,10 @@
                     Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY),
                     true, mSettingObserver, UserHandle.USER_ALL);
 
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.ZEN_MODE),
+                    true, mSettingObserver, UserHandle.USER_ALL);
+
             mContext.registerReceiver(new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
@@ -508,7 +510,8 @@
     }
 
     @Override // Binder call
-    public boolean setAlwaysOnEffect(int id, VibrationEffect effect, VibrationAttributes attrs) {
+    public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, VibrationEffect effect,
+            VibrationAttributes attrs) {
         if (!hasPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)) {
             throw new SecurityException("Requires VIBRATE_ALWAYS_ON permission");
         }
@@ -518,8 +521,8 @@
         }
         if (effect == null) {
             synchronized (mLock) {
-                mAlwaysOnEffects.delete(id);
-                vibratorAlwaysOnDisable(id);
+                mAlwaysOnEffects.delete(alwaysOnId);
+                vibratorAlwaysOnDisable(alwaysOnId);
             }
         } else {
             if (!verifyVibrationEffect(effect)) {
@@ -529,13 +532,11 @@
                 Slog.e(TAG, "Only prebaked effects supported for always-on.");
                 return false;
             }
-            if (attrs == null) {
-                attrs = new VibrationAttributes.Builder()
-                        .build();
-            }
+            attrs = fixupVibrationAttributes(attrs);
             synchronized (mLock) {
-                mAlwaysOnEffects.put(id, Pair.create(effect, attrs));
-                updateAlwaysOnLocked(id, effect, attrs);
+                Vibration vib = new Vibration(null, effect, attrs, uid, opPkg, null);
+                mAlwaysOnEffects.put(alwaysOnId, vib);
+                updateAlwaysOnLocked(alwaysOnId, vib);
             }
         }
         return true;
@@ -575,6 +576,23 @@
         return true;
     }
 
+    private VibrationAttributes fixupVibrationAttributes(VibrationAttributes attrs) {
+        if (attrs == null) {
+            attrs = DEFAULT_ATTRIBUTES;
+        }
+        if (shouldBypassDnd(attrs)) {
+            if (!(hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+                    || hasPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+                    || hasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING))) {
+                final int flags = attrs.getFlags()
+                        & ~VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
+                attrs = new VibrationAttributes.Builder(attrs).replaceFlags(flags).build();
+            }
+        }
+
+        return attrs;
+    }
+
     private static long[] getLongIntArray(Resources r, int resid) {
         int[] ar = r.getIntArray(resid);
         if (ar == null) {
@@ -604,19 +622,7 @@
                 return;
             }
 
-            if (attrs == null) {
-                attrs = DEFAULT_ATTRIBUTES;
-            }
-
-            if (shouldBypassDnd(attrs)) {
-                if (!(hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
-                        || hasPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-                        || hasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING))) {
-                    final int flags = attrs.getFlags()
-                            & ~VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
-                    attrs = new VibrationAttributes.Builder(attrs).replaceFlags(flags).build();
-                }
-            }
+            attrs = fixupVibrationAttributes(attrs);
 
             // If our current vibration is longer than the new vibration and is the same amplitude,
             // then just let the current one finish.
@@ -777,29 +783,8 @@
     private void startVibrationLocked(final Vibration vib) {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
         try {
-            if (!isAllowedToVibrateLocked(vib)) {
-                return;
-            }
-
             final int intensity = getCurrentIntensityLocked(vib);
-            if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
-                return;
-            }
-
-            if (vib.isRingtone() && !shouldVibrateForRingtone()) {
-                if (DEBUG) {
-                    Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
-                }
-                return;
-            }
-
-            final int mode = getAppOpMode(vib);
-            if (mode != AppOpsManager.MODE_ALLOWED) {
-                if (mode == AppOpsManager.MODE_ERRORED) {
-                    // We might be getting calls from within system_server, so we don't actually
-                    // want to throw a SecurityException here.
-                    Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid);
-                }
+            if (!shouldVibrate(vib, intensity)) {
                 return;
             }
             applyVibrationIntensityScalingLocked(vib, intensity);
@@ -958,6 +943,35 @@
         return mode;
     }
 
+    private boolean shouldVibrate(Vibration vib, int intensity) {
+        if (!isAllowedToVibrateLocked(vib)) {
+            return false;
+        }
+
+        if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+            return false;
+        }
+
+        if (vib.isRingtone() && !shouldVibrateForRingtone()) {
+            if (DEBUG) {
+                Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
+            }
+            return false;
+        }
+
+        final int mode = getAppOpMode(vib);
+        if (mode != AppOpsManager.MODE_ALLOWED) {
+            if (mode == AppOpsManager.MODE_ERRORED) {
+                // We might be getting calls from within system_server, so we don't actually
+                // want to throw a SecurityException here.
+                Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid);
+            }
+            return false;
+        }
+
+        return true;
+    }
+
     @GuardedBy("mLock")
     private void reportFinishVibrationLocked() {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
@@ -1069,14 +1083,12 @@
                 mVibrator.getDefaultRingVibrationIntensity(), UserHandle.USER_CURRENT);
     }
 
-    private void updateAlwaysOnLocked(int id, VibrationEffect effect, VibrationAttributes attrs) {
-        // TODO: Check DND and LowPower settings
-        final Vibration vib = new Vibration(null, effect, attrs, 0, null, null);
+    private void updateAlwaysOnLocked(int id, Vibration vib) {
         final int intensity = getCurrentIntensityLocked(vib);
-        if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+        if (!shouldVibrate(vib, intensity)) {
             vibratorAlwaysOnDisable(id);
         } else {
-            final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
+            final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
             final int strength = intensityToEffectStrength(intensity);
             vibratorAlwaysOnEnable(id, prebaked.getId(), strength);
         }
@@ -1085,8 +1097,8 @@
     private void updateAlwaysOnLocked() {
         for (int i = 0; i < mAlwaysOnEffects.size(); i++) {
             int id = mAlwaysOnEffects.keyAt(i);
-            Pair<VibrationEffect, VibrationAttributes> pair = mAlwaysOnEffects.valueAt(i);
-            updateAlwaysOnLocked(id, pair.first, pair.second);
+            Vibration vib = mAlwaysOnEffects.valueAt(i);
+            updateAlwaysOnLocked(id, vib);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8f6d981..883e7c6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2555,7 +2555,7 @@
             Process.setThreadGroupAndCpuset(BackgroundThread.get().getThreadId(),
                     Process.THREAD_GROUP_SYSTEM);
             Process.setThreadGroupAndCpuset(
-                    mOomAdjuster.mAppCompact.mCompactionThread.getThreadId(),
+                    mOomAdjuster.mCachedAppOptimizer.mCachedAppOptimizerThread.getThreadId(),
                     Process.THREAD_GROUP_SYSTEM);
         } catch (Exception e) {
             Slog.w(TAG, "Setting background thread cpuset failed");
@@ -5304,7 +5304,7 @@
                                 String data, Bundle extras, boolean ordered,
                                 boolean sticky, int sendingUser) {
                             synchronized (ActivityManagerService.this) {
-                                mOomAdjuster.mAppCompact.compactAllSystem();
+                                mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
                                 requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
                             }
                         }
@@ -9000,7 +9000,7 @@
             final long timeSinceLastIdle = now - mLastIdleTime;
 
             // Compact all non-zygote processes to freshen up the page cache.
-            mOomAdjuster.mAppCompact.compactAllSystem();
+            mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
 
             final long lowRamSinceLastIdle = getLowRamTimeSinceIdle(now);
             mLastIdleTime = now;
@@ -9108,13 +9108,14 @@
             final Resources res = mContext.getResources();
             mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString(
                     com.android.internal.R.string.config_appsNotReportingCrashes));
-            mUserController.mUserSwitchUiEnabled = !res.getBoolean(
+            final boolean userSwitchUiEnabled = !res.getBoolean(
                     com.android.internal.R.bool.config_customUserSwitchUi);
-            mUserController.mMaxRunningUsers = res.getInteger(
+            final int maxRunningUsers = res.getInteger(
                     com.android.internal.R.integer.config_multiuserMaxRunningUsers);
-            mUserController.mDelayUserDataLocking = res.getBoolean(
+            final boolean delayUserDataLocking = res.getBoolean(
                     com.android.internal.R.bool.config_multiuserDelayUserDataLocking);
-
+            mUserController.setInitialConfig(userSwitchUiEnabled, maxRunningUsers,
+                    delayUserDataLocking);
             mWaitForNetworkTimeoutMs = waitForNetworkTimeoutMs;
             mPssDeferralTime = pssDeferralMs;
         }
@@ -10020,7 +10021,7 @@
 
         synchronized(this) {
             mConstants.dump(pw);
-            mOomAdjuster.dumpAppCompactorSettings(pw);
+            mOomAdjuster.dumpCachedAppOptimizerSettings(pw);
             pw.println();
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
@@ -10425,7 +10426,7 @@
             } else if ("settings".equals(cmd)) {
                 synchronized (this) {
                     mConstants.dump(pw);
-                    mOomAdjuster.dumpAppCompactorSettings(pw);
+                    mOomAdjuster.dumpCachedAppOptimizerSettings(pw);
                 }
             } else if ("services".equals(cmd) || "s".equals(cmd)) {
                 if (dumpClient) {
@@ -17942,7 +17943,31 @@
 
     @Override
     public int stopUser(final int userId, boolean force, final IStopUserCallback callback) {
-        return mUserController.stopUser(userId, force, callback, null /* keyEvictedCallback */);
+        return mUserController.stopUser(userId, force, /* allowDelayedLocking= */ false,
+                /* callback= */ callback, /* keyEvictedCallback= */ null);
+    }
+
+    /**
+     * Stops user but allow delayed locking. Delayed locking keeps user unlocked even after
+     * stopping only if {@code config_multiuserDelayUserDataLocking} overlay is set true.
+     *
+     * <p>When delayed locking is not enabled through the overlay, this call becomes the same
+     * with {@link #stopUser(int, boolean, IStopUserCallback)} call.
+     *
+     * @param userId User id to stop.
+     * @param force Force stop the user even if the user is related with system user or current
+     *              user.
+     * @param callback Callback called when user has stopped.
+     *
+     * @return {@link ActivityManager#USER_OP_SUCCESS} when user is stopped successfully. Returns
+     *         other {@code ActivityManager#USER_OP_*} codes for failure.
+     *
+     */
+    @Override
+    public int stopUserWithDelayedLocking(final int userId, boolean force,
+            final IStopUserCallback callback) {
+        return mUserController.stopUser(userId, force, /* allowDelayedLocking= */ true,
+                /* callback= */ callback, /* keyEvictedCallback= */ null);
     }
 
     @Override
@@ -18348,7 +18373,7 @@
 
         @Override
         public int getMaxRunningUsers() {
-            return mUserController.mMaxRunningUsers;
+            return mUserController.getMaxRunningUsers();
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
similarity index 97%
rename from services/core/java/com/android/server/am/AppCompactor.java
rename to services/core/java/com/android/server/am/CachedAppOptimizer.java
index b7e2065..3ca5ebc 100644
--- a/services/core/java/com/android/server/am/AppCompactor.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -51,7 +51,7 @@
 import java.util.Random;
 import java.util.Set;
 
-public final class AppCompactor {
+public final class CachedAppOptimizer {
 
     // Flags stored in the DeviceConfig API.
     @VisibleForTesting static final String KEY_USE_COMPACTION = "use_compaction";
@@ -122,7 +122,7 @@
      * that will wipe out the cpuset assignment for system_server threads.
      * Accordingly, this is in the AMS constructor.
      */
-    final ServiceThread mCompactionThread;
+    final ServiceThread mCachedAppOptimizerThread;
 
     private final ArrayList<ProcessRecord> mPendingCompactionProcesses =
             new ArrayList<ProcessRecord>();
@@ -214,15 +214,15 @@
     private int mPersistentCompactionCount;
     private int mBfgsCompactionCount;
 
-    public AppCompactor(ActivityManagerService am) {
+    public CachedAppOptimizer(ActivityManagerService am) {
         mAm = am;
-        mCompactionThread = new ServiceThread("CompactionThread",
+        mCachedAppOptimizerThread = new ServiceThread("CachedAppOptimizerThread",
                 THREAD_PRIORITY_FOREGROUND, true);
         mProcStateThrottle = new HashSet<>();
     }
 
     @VisibleForTesting
-    AppCompactor(ActivityManagerService am, PropertyChangedCallbackForTest callback) {
+    CachedAppOptimizer(ActivityManagerService am, PropertyChangedCallbackForTest callback) {
         this(am);
         mTestCallback = callback;
     }
@@ -243,7 +243,7 @@
             updateFullDeltaRssThrottle();
             updateProcStateThrottle();
         }
-        Process.setThreadGroupAndCpuset(mCompactionThread.getThreadId(),
+        Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),
                 Process.THREAD_GROUP_SYSTEM);
     }
 
@@ -258,7 +258,7 @@
 
     @GuardedBy("mAm")
     void dump(PrintWriter pw) {
-        pw.println("AppCompactor settings");
+        pw.println("CachedAppOptimizer settings");
         synchronized (mPhenotypeFlagLock) {
             pw.println("  " + KEY_USE_COMPACTION + "=" + mUseCompaction);
             pw.println("  " + KEY_COMPACT_ACTION_1 + "=" + mCompactActionSome);
@@ -300,7 +300,7 @@
         app.reqCompactAction = COMPACT_PROCESS_SOME;
         mPendingCompactionProcesses.add(app);
         mCompactionHandler.sendMessage(
-            mCompactionHandler.obtainMessage(
+                mCompactionHandler.obtainMessage(
                 COMPACT_PROCESS_MSG, app.setAdj, app.setProcState));
     }
 
@@ -309,7 +309,7 @@
         app.reqCompactAction = COMPACT_PROCESS_FULL;
         mPendingCompactionProcesses.add(app);
         mCompactionHandler.sendMessage(
-            mCompactionHandler.obtainMessage(
+                mCompactionHandler.obtainMessage(
                 COMPACT_PROCESS_MSG, app.setAdj, app.setProcState));
 
     }
@@ -362,8 +362,8 @@
     private void updateUseCompaction() {
         mUseCompaction = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION);
-        if (mUseCompaction && !mCompactionThread.isAlive()) {
-            mCompactionThread.start();
+        if (mUseCompaction && !mCachedAppOptimizerThread.isAlive()) {
+            mCachedAppOptimizerThread.start();
             mCompactionHandler = new MemCompactionHandler();
         }
     }
@@ -521,7 +521,7 @@
 
     private final class MemCompactionHandler extends Handler {
         private MemCompactionHandler() {
-            super(mCompactionThread.getLooper());
+            super(mCachedAppOptimizerThread.getLooper());
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 0fc885a..f86d6a7 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -122,9 +122,9 @@
     PowerManagerInternal mLocalPowerManager;
 
     /**
-     * Service for compacting background apps.
+     * Service for optimizing resource usage from background apps.
      */
-    AppCompactor mAppCompact;
+    CachedAppOptimizer mCachedAppOptimizer;
 
     ActivityManagerConstants mConstants;
 
@@ -197,7 +197,7 @@
 
         mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
         mConstants = mService.mConstants;
-        mAppCompact = new AppCompactor(mService);
+        mCachedAppOptimizer = new CachedAppOptimizer(mService);
 
         mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> {
             final int pid = msg.arg1;
@@ -224,7 +224,7 @@
     }
 
     void initSettings() {
-        mAppCompact.init();
+        mCachedAppOptimizer.init();
     }
 
     /**
@@ -1978,7 +1978,7 @@
         int changes = 0;
 
         // don't compact during bootup
-        if (mAppCompact.useCompaction() && mService.mBooted) {
+        if (mCachedAppOptimizer.useCompaction() && mService.mBooted) {
             // Cached and prev/home compaction
             if (app.curAdj != app.setAdj) {
                 // Perform a minor compaction when a perceptible app becomes the prev/home app
@@ -1987,26 +1987,26 @@
                 if (app.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ &&
                         (app.curAdj == ProcessList.PREVIOUS_APP_ADJ ||
                                 app.curAdj == ProcessList.HOME_APP_ADJ)) {
-                    mAppCompact.compactAppSome(app);
+                    mCachedAppOptimizer.compactAppSome(app);
                 } else if ((app.setAdj < ProcessList.CACHED_APP_MIN_ADJ
                                 || app.setAdj > ProcessList.CACHED_APP_MAX_ADJ)
                         && app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
                         && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
-                    mAppCompact.compactAppFull(app);
+                    mCachedAppOptimizer.compactAppFull(app);
                 }
             } else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE
                     && app.setAdj < ProcessList.FOREGROUND_APP_ADJ
                     // Because these can fire independent of oom_adj/procstate changes, we need
                     // to throttle the actual dispatch of these requests in addition to the
                     // processing of the requests. As a result, there is throttling both here
-                    // and in AppCompactor.
-                    && mAppCompact.shouldCompactPersistent(app, now)) {
-                mAppCompact.compactAppPersistent(app);
+                    // and in CachedAppOptimizer.
+                    && mCachedAppOptimizer.shouldCompactPersistent(app, now)) {
+                mCachedAppOptimizer.compactAppPersistent(app);
             } else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE
                     && app.getCurProcState()
                         == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
-                    && mAppCompact.shouldCompactBFGS(app, now)) {
-                mAppCompact.compactAppBfgs(app);
+                    && mCachedAppOptimizer.shouldCompactBFGS(app, now)) {
+                mCachedAppOptimizer.compactAppBfgs(app);
             }
         }
 
@@ -2439,7 +2439,7 @@
     }
 
     @GuardedBy("mService")
-    void dumpAppCompactorSettings(PrintWriter pw) {
-        mAppCompact.dump(pw);
+    void dumpCachedAppOptimizerSettings(PrintWriter pw) {
+        mCachedAppOptimizer.dump(pw);
     }
 }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index e11008c..b7f867d 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -647,11 +647,10 @@
         // Get this after boot, and won't be changed until it's rebooted, as we don't
         // want some apps enabled while some apps disabled
         mAppDataIsolationEnabled =
-                SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
+                SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
         mAppDataIsolationWhitelistedApps = new ArrayList<>(
                 SystemConfig.getInstance().getAppDataIsolationWhitelistedApps());
 
-
         if (sKillHandler == null) {
             sKillThread = new ServiceThread(TAG + ":kill",
                     THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 8ae18ff6..f3a2e70 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -93,7 +93,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Preconditions;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
@@ -161,7 +160,8 @@
      * <p>Note: Current and system user (and their related profiles) are never stopped when
      * switching users. Due to that, the actual number of running users can exceed mMaxRunningUsers
      */
-    int mMaxRunningUsers;
+    @GuardedBy("mLock")
+    private int mMaxRunningUsers;
 
     // Lock for internal state.
     private final Object mLock = new Object();
@@ -213,7 +213,8 @@
     private final RemoteCallbackList<IUserSwitchObserver> mUserSwitchObservers
             = new RemoteCallbackList<>();
 
-    boolean mUserSwitchUiEnabled = true;
+    @GuardedBy("mLock")
+    private boolean mUserSwitchUiEnabled = true;
 
     /**
      * Currently active user switch callbacks.
@@ -246,10 +247,11 @@
     /**
      * In this mode, user is always stopped when switched out but locking of user data is
      * postponed until total number of unlocked users in the system reaches mMaxRunningUsers.
-     * Once total number of unlocked users reach mMaxRunningUsers, least recentely used user
+     * Once total number of unlocked users reach mMaxRunningUsers, least recently used user
      * will be locked.
      */
-    boolean mDelayUserDataLocking;
+    @GuardedBy("mLock")
+    private boolean mDelayUserDataLocking;
     /**
      * Keep track of last active users for mDelayUserDataLocking.
      * The latest stopped user is placed in front while the least recently stopped user in back.
@@ -275,6 +277,33 @@
         updateStartedUserArrayLU();
     }
 
+    void setInitialConfig(boolean userSwitchUiEnabled, int maxRunningUsers,
+            boolean delayUserDataLocking) {
+        synchronized (mLock) {
+            mUserSwitchUiEnabled = userSwitchUiEnabled;
+            mMaxRunningUsers = maxRunningUsers;
+            mDelayUserDataLocking = delayUserDataLocking;
+        }
+    }
+
+    private boolean isUserSwitchUiEnabled() {
+        synchronized (mLock) {
+            return mUserSwitchUiEnabled;
+        }
+    }
+
+    int getMaxRunningUsers() {
+        synchronized (mLock) {
+            return mMaxRunningUsers;
+        }
+    }
+
+    private boolean isDelayUserDataLockingEnabled() {
+        synchronized (mLock) {
+            return mDelayUserDataLocking;
+        }
+    }
+
     void finishUserSwitch(UserState uss) {
         // This call holds the AM lock so we post to the handler.
         mHandler.post(() -> {
@@ -321,7 +350,11 @@
                 // Owner/System user and current user can't be stopped
                 continue;
             }
-            if (stopUsersLU(userId, false, null, null) == USER_OP_SUCCESS) {
+            // allowDelayedLocking set here as stopping user is done without any explicit request
+            // from outside.
+            if (stopUsersLU(userId, /* force= */ false, /* allowDelayedLocking= */ true,
+                    /* stopUserCallback= */ null, /* keyEvictedCallback= */ null)
+                    == USER_OP_SUCCESS) {
                 iterator.remove();
             }
         }
@@ -567,8 +600,8 @@
             // intialize it; it should be stopped right away as it's not really a "real" user.
             // TODO(b/143092698): in the long-term, it might be better to add a onCreateUser()
             // callback on SystemService instead.
-            stopUser(userInfo.id, /* force= */ true, /* stopUserCallback= */ null,
-                    /* keyEvictedCallback= */ null);
+            stopUser(userInfo.id, /* force= */ true, /* allowDelayedLocking= */ false,
+                    /* stopUserCallback= */ null, /* keyEvictedCallback= */ null);
             return;
         }
 
@@ -611,7 +644,8 @@
     }
 
     int restartUser(final int userId, final boolean foreground) {
-        return stopUser(userId, /* force */ true, null, new KeyEvictedCallback() {
+        return stopUser(userId, /* force= */ true, /* allowDelayedLocking= */ false,
+                /* stopUserCallback= */ null, new KeyEvictedCallback() {
             @Override
             public void keyEvicted(@UserIdInt int userId) {
                 // Post to the same handler that this callback is called from to ensure the user
@@ -621,15 +655,16 @@
         });
     }
 
-    int stopUser(final int userId, final boolean force, final IStopUserCallback stopUserCallback,
-            KeyEvictedCallback keyEvictedCallback) {
+    int stopUser(final int userId, final boolean force, boolean allowDelayedLocking,
+            final IStopUserCallback stopUserCallback, KeyEvictedCallback keyEvictedCallback) {
         checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "stopUser");
         if (userId < 0 || userId == UserHandle.USER_SYSTEM) {
             throw new IllegalArgumentException("Can't stop system user " + userId);
         }
         enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
         synchronized (mLock) {
-            return stopUsersLU(userId, force, stopUserCallback, keyEvictedCallback);
+            return stopUsersLU(userId, force, allowDelayedLocking, stopUserCallback,
+                    keyEvictedCallback);
         }
     }
 
@@ -638,7 +673,7 @@
      * {@link #getUsersToStopLU(int)} to determine the list of users that should be stopped.
      */
     @GuardedBy("mLock")
-    private int stopUsersLU(final int userId, boolean force,
+    private int stopUsersLU(final int userId, boolean force, boolean allowDelayedLocking,
             final IStopUserCallback stopUserCallback, KeyEvictedCallback keyEvictedCallback) {
         if (userId == UserHandle.USER_SYSTEM) {
             return USER_OP_ERROR_IS_SYSTEM;
@@ -657,7 +692,8 @@
                 if (force) {
                     Slog.i(TAG,
                             "Force stop user " + userId + ". Related users will not be stopped");
-                    stopSingleUserLU(userId, stopUserCallback, keyEvictedCallback);
+                    stopSingleUserLU(userId, allowDelayedLocking, stopUserCallback,
+                            keyEvictedCallback);
                     return USER_OP_SUCCESS;
                 }
                 return USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
@@ -665,21 +701,64 @@
         }
         if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked usersToStop=" + Arrays.toString(usersToStop));
         for (int userIdToStop : usersToStop) {
-            stopSingleUserLU(userIdToStop,
+            stopSingleUserLU(userIdToStop, allowDelayedLocking,
                     userIdToStop == userId ? stopUserCallback : null,
                     userIdToStop == userId ? keyEvictedCallback : null);
         }
         return USER_OP_SUCCESS;
     }
 
+    /**
+     * Stops a single User. This can also trigger locking user data out depending on device's
+     * config ({@code mDelayUserDataLocking}) and arguments.
+     * User will be unlocked when
+     * - {@code mDelayUserDataLocking} is not set.
+     * - {@code mDelayUserDataLocking} is set and {@code keyEvictedCallback} is non-null.
+     * -
+     *
+     * @param userId User Id to stop and lock the data.
+     * @param allowDelayedLocking When set, do not lock user after stopping. Locking can happen
+     *                            later when number of unlocked users reaches
+     *                            {@code mMaxRunnngUsers}. Note that this is respected only when
+     *                            {@code mDelayUserDataLocking} is set and {@keyEvictedCallback} is
+     *                            null. Otherwise the user will be locked.
+     * @param stopUserCallback Callback to notify that user has stopped.
+     * @param keyEvictedCallback Callback to notify that user has been unlocked.
+     */
     @GuardedBy("mLock")
-    private void stopSingleUserLU(final int userId, final IStopUserCallback stopUserCallback,
+    private void stopSingleUserLU(final int userId, boolean allowDelayedLocking,
+            final IStopUserCallback stopUserCallback,
             KeyEvictedCallback keyEvictedCallback) {
         if (DEBUG_MU) Slog.i(TAG, "stopSingleUserLocked userId=" + userId);
         final UserState uss = mStartedUsers.get(userId);
-        if (uss == null) {
-            // User is not started, nothing to do...  but we do need to
-            // callback if requested.
+        if (uss == null) {  // User is not started
+            // If mDelayUserDataLocking is set and allowDelayedLocking is not set, we need to lock
+            // the requested user as the client wants to stop and lock the user. On the other hand,
+            // having keyEvictedCallback set will lead into locking user if mDelayUserDataLocking
+            // is set as that means client wants to lock the user immediately.
+            // If mDelayUserDataLocking is not set, the user was already locked when it was stopped
+            // and no further action is necessary.
+            if (mDelayUserDataLocking) {
+                if (allowDelayedLocking && keyEvictedCallback != null) {
+                    Slog.wtf(TAG, "allowDelayedLocking set with KeyEvictedCallback, ignore it"
+                            + " and lock user:" + userId, new RuntimeException());
+                    allowDelayedLocking = false;
+                }
+                if (!allowDelayedLocking) {
+                    if (mLastActiveUsers.remove(Integer.valueOf(userId))) {
+                        // should lock the user, user is already gone
+                        final ArrayList<KeyEvictedCallback> keyEvictedCallbacks;
+                        if (keyEvictedCallback != null) {
+                            keyEvictedCallbacks = new ArrayList<>(1);
+                            keyEvictedCallbacks.add(keyEvictedCallback);
+                        } else {
+                            keyEvictedCallbacks = null;
+                        }
+                        dispatchUserLocking(userId, keyEvictedCallbacks);
+                    }
+                }
+            }
+            // We do need to post the stopped callback even though user is already stopped.
             if (stopUserCallback != null) {
                 mHandler.post(() -> {
                     try {
@@ -704,6 +783,7 @@
             mInjector.getUserManagerInternal().setUserState(userId, uss.state);
             updateStartedUserArrayLU();
 
+            final boolean allowDelayyLockingCopied = allowDelayedLocking;
             // Post to handler to obtain amLock
             mHandler.post(() -> {
                 // We are going to broadcast ACTION_USER_STOPPING and then
@@ -718,7 +798,8 @@
                     @Override
                     public void performReceive(Intent intent, int resultCode, String data,
                             Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
-                        mHandler.post(() -> finishUserStopping(userId, uss));
+                        mHandler.post(() -> finishUserStopping(userId, uss,
+                                allowDelayyLockingCopied));
                     }
                 };
 
@@ -734,7 +815,8 @@
         }
     }
 
-    void finishUserStopping(final int userId, final UserState uss) {
+    void finishUserStopping(final int userId, final UserState uss,
+            final boolean allowDelayedLocking) {
         Slog.d(TAG, "UserController event: finishUserStopping(" + userId + ")");
         // On to the next.
         final Intent shutdownIntent = new Intent(Intent.ACTION_SHUTDOWN);
@@ -746,7 +828,7 @@
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
-                        finishUserStopped(uss);
+                        finishUserStopped(uss, allowDelayedLocking);
                     }
                 });
             }
@@ -773,7 +855,7 @@
                 Binder.getCallingPid(), userId);
     }
 
-    void finishUserStopped(UserState uss) {
+    void finishUserStopped(UserState uss, boolean allowDelayedLocking) {
         final int userId = uss.mHandle.getIdentifier();
         Slog.d(TAG, "UserController event: finishUserStopped(" + userId + ")");
         final boolean stopped;
@@ -792,7 +874,13 @@
                 mStartedUsers.remove(userId);
                 mUserLru.remove(Integer.valueOf(userId));
                 updateStartedUserArrayLU();
-                userIdToLock = updateUserToLockLU(userId);
+                if (allowDelayedLocking && !keyEvictedCallbacks.isEmpty()) {
+                    Slog.wtf(TAG,
+                            "Delayed locking enabled while KeyEvictedCallbacks not empty, userId:"
+                                    + userId + " callbacks:" + keyEvictedCallbacks);
+                    allowDelayedLocking = false;
+                }
+                userIdToLock = updateUserToLockLU(userId, allowDelayedLocking);
                 if (userIdToLock == UserHandle.USER_NULL) {
                     lockUser = false;
                 }
@@ -826,31 +914,36 @@
             if (!lockUser) {
                 return;
             }
-            final int userIdToLockF = userIdToLock;
-            // Evict the user's credential encryption key. Performed on FgThread to make it
-            // serialized with call to UserManagerService.onBeforeUnlockUser in finishUserUnlocking
-            // to prevent data corruption.
-            FgThread.getHandler().post(() -> {
-                synchronized (mLock) {
-                    if (mStartedUsers.get(userIdToLockF) != null) {
-                        Slog.w(TAG, "User was restarted, skipping key eviction");
-                        return;
-                    }
-                }
-                try {
-                    mInjector.getStorageManager().lockUserKey(userIdToLockF);
-                } catch (RemoteException re) {
-                    throw re.rethrowAsRuntimeException();
-                }
-                if (userIdToLockF == userId) {
-                    for (final KeyEvictedCallback callback : keyEvictedCallbacks) {
-                        callback.keyEvicted(userId);
-                    }
-                }
-            });
+            dispatchUserLocking(userIdToLock, keyEvictedCallbacks);
         }
     }
 
+    private void dispatchUserLocking(@UserIdInt int userId,
+            @Nullable List<KeyEvictedCallback> keyEvictedCallbacks) {
+        // Evict the user's credential encryption key. Performed on FgThread to make it
+        // serialized with call to UserManagerService.onBeforeUnlockUser in finishUserUnlocking
+        // to prevent data corruption.
+        FgThread.getHandler().post(() -> {
+            synchronized (mLock) {
+                if (mStartedUsers.get(userId) != null) {
+                    Slog.w(TAG, "User was restarted, skipping key eviction");
+                    return;
+                }
+            }
+            try {
+                mInjector.getStorageManager().lockUserKey(userId);
+            } catch (RemoteException re) {
+                throw re.rethrowAsRuntimeException();
+            }
+            if (keyEvictedCallbacks == null) {
+                return;
+            }
+            for (int i = 0; i < keyEvictedCallbacks.size(); i++) {
+                keyEvictedCallbacks.get(i).keyEvicted(userId);
+            }
+        });
+    }
+
     /**
      * For mDelayUserDataLocking mode, storage once unlocked is kept unlocked.
      * Total number of unlocked user storage is limited by mMaxRunningUsers.
@@ -861,9 +954,9 @@
      * @return user id to lock. UserHandler.USER_NULL will be returned if no user should be locked.
      */
     @GuardedBy("mLock")
-    private int updateUserToLockLU(@UserIdInt int userId) {
+    private int updateUserToLockLU(@UserIdInt int userId, boolean allowDelayedLocking) {
         int userIdToLock = userId;
-        if (mDelayUserDataLocking && !getUserInfo(userId).isEphemeral()
+        if (mDelayUserDataLocking && allowDelayedLocking && !getUserInfo(userId).isEphemeral()
                 && !hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND, userId)) {
             mLastActiveUsers.remove((Integer) userId); // arg should be object, not index
             mLastActiveUsers.add(0, userId);
@@ -945,7 +1038,8 @@
         if (userInfo.isGuest() || userInfo.isEphemeral()) {
             // This is a user to be stopped.
             synchronized (mLock) {
-                stopUsersLU(oldUserId, true, null, null);
+                stopUsersLU(oldUserId, /* force= */ true, /* allowDelayedLocking= */ false,
+                        null, null);
             }
         }
     }
@@ -977,7 +1071,7 @@
         }
         final int profilesToStartSize = profilesToStart.size();
         int i = 0;
-        for (; i < profilesToStartSize && i < (mMaxRunningUsers - 1); ++i) {
+        for (; i < profilesToStartSize && i < (getMaxRunningUsers() - 1); ++i) {
             startUser(profilesToStart.get(i).id, /* foreground= */ false);
         }
         if (i < profilesToStartSize) {
@@ -1094,7 +1188,7 @@
                 return false;
             }
 
-            if (foreground && mUserSwitchUiEnabled) {
+            if (foreground && isUserSwitchUiEnabled()) {
                 t.traceBegin("startFreezingScreen");
                 mInjector.getWindowManager().startFreezingScreen(
                         R.anim.screen_user_exit, R.anim.screen_user_enter);
@@ -1142,9 +1236,11 @@
             if (foreground) {
                 // Make sure the old user is no longer considering the display to be on.
                 mInjector.reportGlobalUsageEventLocked(UsageEvents.Event.SCREEN_NON_INTERACTIVE);
+                boolean userSwitchUiEnabled;
                 synchronized (mLock) {
                     mCurrentUserId = userId;
                     mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up
+                    userSwitchUiEnabled = mUserSwitchUiEnabled;
                 }
                 mInjector.updateUserConfiguration();
                 updateCurrentProfileIds();
@@ -1152,7 +1248,7 @@
                 mInjector.reportCurWakefulnessUsageEvent();
                 // Once the internal notion of the active user has switched, we lock the device
                 // with the option to show the user switcher on the keyguard.
-                if (mUserSwitchUiEnabled) {
+                if (userSwitchUiEnabled) {
                     mInjector.getWindowManager().setSwitchingUser(true);
                     mInjector.getWindowManager().lockNow(null);
                 }
@@ -1391,10 +1487,12 @@
             Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user");
             return false;
         }
+        boolean userSwitchUiEnabled;
         synchronized (mLock) {
             mTargetUserId = targetUserId;
+            userSwitchUiEnabled = mUserSwitchUiEnabled;
         }
-        if (mUserSwitchUiEnabled) {
+        if (userSwitchUiEnabled) {
             UserInfo currentUserInfo = getUserInfo(currentUserId);
             Pair<UserInfo, UserInfo> userNames = new Pair<>(currentUserInfo, targetUserInfo);
             mUiHandler.removeMessages(START_USER_SWITCH_UI_MSG);
@@ -1458,14 +1556,15 @@
         }
         // If running in background is disabled or mDelayUserDataLocking mode, stop the user.
         boolean disallowRunInBg = hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND,
-                oldUserId) || mDelayUserDataLocking;
+                oldUserId) || isDelayUserDataLockingEnabled();
         if (!disallowRunInBg) {
             return;
         }
         synchronized (mLock) {
             if (DEBUG_MU) Slog.i(TAG, "stopBackgroundUsersIfEnforced stopping " + oldUserId
                     + " and related users");
-            stopUsersLU(oldUserId, false, null, null);
+            stopUsersLU(oldUserId, /* force= */ false, /* allowDelayedLocking= */ true,
+                    null, null);
         }
     }
 
@@ -1551,7 +1650,7 @@
 
     void continueUserSwitch(UserState uss, int oldUserId, int newUserId) {
         Slog.d(TAG, "Continue user switch oldUser #" + oldUserId + ", newUser #" + newUserId);
-        if (mUserSwitchUiEnabled) {
+        if (isUserSwitchUiEnabled()) {
             mInjector.getWindowManager().stopFreezingScreen();
         }
         uss.switching = false;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index cd2272a..eedeeea 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2193,8 +2193,8 @@
     }
 
     private void enforceModifyAudioRoutingPermission() {
-        if (mContext.checkCallingPermission(
-                    android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.MODIFY_AUDIO_ROUTING)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Missing MODIFY_AUDIO_ROUTING permission");
         }
@@ -6427,7 +6427,7 @@
                 return false;
             }
             boolean suppress = false;
-            if (resolvedStream == DEFAULT_VOL_STREAM_NO_PLAYBACK && mController != null) {
+            if (resolvedStream != AudioSystem.STREAM_MUSIC && mController != null) {
                 final long now = SystemClock.uptimeMillis();
                 if ((flags & AudioManager.FLAG_SHOW_UI) != 0 && !mVisible) {
                     // ui will become visible
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index e1a9f3b..8dd3242 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -1400,7 +1400,8 @@
                     if (mCurrentAuthSession.mTokenEscrow != null) {
                         mKeyStore.addAuthToken(mCurrentAuthSession.mTokenEscrow);
                     }
-                    mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
+                    mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded(
+                            Utils.getAuthenticationTypeForResult(reason));
                     break;
 
                 case BiometricPrompt.DISMISSED_REASON_NEGATIVE:
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 19f5358..389763b 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -22,6 +22,7 @@
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.UserHandle;
@@ -210,4 +211,30 @@
         }
         return biometricManagerCode;
     }
+
+    /**
+     * Converts a {@link BiometricPrompt} dismissal reason to an authentication type at the level of
+     * granularity supported by {@link BiometricPrompt.AuthenticationResult}.
+     *
+     * @param reason The reason that the {@link BiometricPrompt} was dismissed. Must be one of:
+     *               {@link BiometricPrompt#DISMISSED_REASON_CREDENTIAL_CONFIRMED},
+     *               {@link BiometricPrompt#DISMISSED_REASON_BIOMETRIC_CONFIRMED}, or
+     *               {@link BiometricPrompt#DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED}
+     * @return An integer representing the authentication type for {@link
+     *         BiometricPrompt.AuthenticationResult}.
+     * @throws IllegalArgumentException if given an invalid dismissal reason.
+     */
+    public static @AuthenticationResultType int getAuthenticationTypeForResult(int reason) {
+        switch (reason) {
+            case BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED:
+                return BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL;
+
+            case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED:
+            case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED:
+                return BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC;
+
+            default:
+                throw new IllegalArgumentException("Unsupported dismissal reason: " + reason);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/DataConnectionStats.java b/services/core/java/com/android/server/connectivity/DataConnectionStats.java
index 1b1c546..5010e46 100644
--- a/services/core/java/com/android/server/connectivity/DataConnectionStats.java
+++ b/services/core/java/com/android/server/connectivity/DataConnectionStats.java
@@ -16,6 +16,9 @@
 
 package com.android.server.connectivity;
 
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -24,6 +27,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
@@ -91,7 +95,10 @@
         boolean visible = (simReadyOrUnknown || isCdma()) // we only check the sim state for GSM
                 && hasService()
                 && mDataState == TelephonyManager.DATA_CONNECTED;
-        int networkType = mServiceState.getDataNetworkType();
+        NetworkRegistrationInfo regInfo =
+                mServiceState.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN);
+        int networkType = regInfo == null ? TelephonyManager.NETWORK_TYPE_UNKNOWN
+                : regInfo.getAccessNetworkTechnology();
         if (DEBUG) Log.d(TAG, String.format("Noting data connection for network type %s: %svisible",
                 networkType, visible ? "" : "not "));
         try {
diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
index 6fa999c..04c792a 100644
--- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
+++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
@@ -26,6 +26,7 @@
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
 import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH;
 import static com.android.server.net.NetworkPolicyManagerService.OPPORTUNISTIC_QUOTA_UNKNOWN;
@@ -46,19 +47,18 @@
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkRequest;
+import android.net.NetworkSpecifier;
 import android.net.NetworkStats;
 import android.net.NetworkTemplate;
-import android.net.StringNetworkSpecifier;
+import android.net.TelephonyNetworkSpecifier;
+import android.net.Uri;
 import android.os.BestClock;
 import android.os.Handler;
 import android.os.SystemClock;
-import android.net.Uri;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
-import android.util.DataUnit;
 import android.util.DebugUtils;
-import android.util.Pair;
 import android.util.Range;
 import android.util.Slog;
 
@@ -74,7 +74,6 @@
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
 import java.time.temporal.ChronoUnit;
-import java.util.Iterator;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
 
@@ -185,7 +184,6 @@
     // Track information on mobile networks as they come and go.
     class MultipathTracker {
         final Network network;
-        final int subId;
         final String subscriberId;
 
         private long mQuota;
@@ -198,13 +196,14 @@
         public MultipathTracker(Network network, NetworkCapabilities nc) {
             this.network = network;
             this.mNetworkCapabilities = new NetworkCapabilities(nc);
-            try {
-                subId = Integer.parseInt(
-                        ((StringNetworkSpecifier) nc.getNetworkSpecifier()).toString());
-            } catch (ClassCastException | NullPointerException | NumberFormatException e) {
+            NetworkSpecifier specifier = nc.getNetworkSpecifier();
+            int subId = INVALID_SUBSCRIPTION_ID;
+            if (specifier instanceof TelephonyNetworkSpecifier) {
+                subId = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
+            } else {
                 throw new IllegalStateException(String.format(
-                        "Can't get subId from mobile network %s (%s): %s",
-                        network, nc, e.getMessage()));
+                        "Can't get subId from mobile network %s (%s)",
+                        network, nc));
             }
 
             TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 2179518..2c41557 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -28,7 +28,7 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.net.NetworkSpecifier;
-import android.net.StringNetworkSpecifier;
+import android.net.TelephonyNetworkSpecifier;
 import android.net.wifi.WifiInfo;
 import android.os.UserHandle;
 import android.telephony.SubscriptionManager;
@@ -223,14 +223,8 @@
                     // name has been added to it
                     NetworkSpecifier specifier = nai.networkCapabilities.getNetworkSpecifier();
                     int subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
-                    if (specifier instanceof StringNetworkSpecifier) {
-                        try {
-                            subId = Integer.parseInt(
-                                    ((StringNetworkSpecifier) specifier).specifier);
-                        } catch (NumberFormatException e) {
-                            Slog.e(TAG, "NumberFormatException on "
-                                    + ((StringNetworkSpecifier) specifier).specifier);
-                        }
+                    if (specifier instanceof TelephonyNetworkSpecifier) {
+                        subId = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
                     }
 
                     details = mTelephonyManager.createForSubscriptionId(subId)
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 8498dcb..decb1f9 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -73,7 +73,7 @@
     private static final int GLOBAL_ID = -1;
 
     // The tolerance within which we consider something approximately equals.
-    private static final float EPSILON = 0.001f;
+    private static final float EPSILON = 0.01f;
 
     private final Object mLock = new Object();
     private final Context mContext;
diff --git a/services/core/java/com/android/server/integrity/model/BitOutputStream.java b/services/core/java/com/android/server/integrity/model/BitOutputStream.java
index b8ea041..da778aa 100644
--- a/services/core/java/com/android/server/integrity/model/BitOutputStream.java
+++ b/services/core/java/com/android/server/integrity/model/BitOutputStream.java
@@ -16,17 +16,25 @@
 
 package com.android.server.integrity.model;
 
-import java.util.BitSet;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
 
 /** A wrapper class for writing a stream of bits. */
 public class BitOutputStream {
 
-    private BitSet mBitSet;
-    private int mIndex;
+    private static final int BUFFER_SIZE = 4 * 1024;
+    private static final int BYTE_BITS = 8;
 
-    public BitOutputStream() {
-        mBitSet = new BitSet();
-        mIndex = 0;
+    private int mNextBitIndex;
+
+    private final OutputStream mOutputStream;
+    private final byte[] mBuffer;
+
+    public BitOutputStream(OutputStream outputStream) {
+        mBuffer = new byte[BUFFER_SIZE];
+        mNextBitIndex = 0;
+        mOutputStream = outputStream;
     }
 
     /**
@@ -35,15 +43,17 @@
      * @param numOfBits The number of bits used to represent the value.
      * @param value The value to convert to bits.
      */
-    public void setNext(int numOfBits, int value) {
+    public void setNext(int numOfBits, int value) throws IOException {
         if (numOfBits <= 0) {
             return;
         }
-        int offset = 1 << (numOfBits - 1);
+
+        // optional: we can do some clever size checking to "OR" an entire segment of bits instead
+        // of setting bits one by one, but it is probably not worth it.
+        int nextBitMask = 1 << (numOfBits - 1);
         while (numOfBits-- > 0) {
-            mBitSet.set(mIndex, (value & offset) != 0);
-            offset >>>= 1;
-            mIndex++;
+            setNext((value & nextBitMask) != 0);
+            nextBitMask >>>= 1;
         }
     }
 
@@ -52,35 +62,43 @@
      *
      * @param value The value to set the bit to.
      */
-    public void setNext(boolean value) {
-        mBitSet.set(mIndex, value);
-        mIndex++;
+    public void setNext(boolean value) throws IOException {
+        int byteToWrite = mNextBitIndex / BYTE_BITS;
+        if (byteToWrite == BUFFER_SIZE) {
+            mOutputStream.write(mBuffer);
+            reset();
+            byteToWrite = 0;
+        }
+        if (value) {
+            mBuffer[byteToWrite] |= 1 << (BYTE_BITS - 1 - (mNextBitIndex % BYTE_BITS));
+        }
+        mNextBitIndex++;
     }
 
     /** Set the next bit in the stream to true. */
-    public void setNext() {
+    public void setNext() throws IOException {
         setNext(/* value= */ true);
     }
 
-    /** Convert BitSet in big-endian to ByteArray in big-endian. */
-    public byte[] toByteArray() {
-        int bitSetSize = mBitSet.length();
-        int numOfBytes = bitSetSize / 8;
-        if (bitSetSize % 8 != 0) {
-            numOfBytes++;
+    /**
+     * Flush the data written to the underlying {@link java.io.OutputStream}. Any unfinished bytes
+     * will be padded with 0.
+     */
+    public void flush() throws IOException {
+        int endByte = mNextBitIndex / BYTE_BITS;
+        if (mNextBitIndex % BYTE_BITS != 0) {
+            // If next bit is not the first bit of a byte, then mNextBitIndex / BYTE_BITS would be
+            // the byte that includes already written bits. We need to increment it so this byte
+            // gets written.
+            endByte++;
         }
-        byte[] bytes = new byte[numOfBytes];
-        for (int i = 0; i < mBitSet.length(); i++) {
-            if (mBitSet.get(i)) {
-                bytes[i / 8] |= 1 << (7 - (i % 8));
-            }
-        }
-        return bytes;
+        mOutputStream.write(mBuffer, 0, endByte);
+        reset();
     }
 
-    /** Clear the stream. */
-    public void clear() {
-        mBitSet.clear();
-        mIndex = 0;
+    /** Reset this output stream to start state. */
+    private void reset() {
+        mNextBitIndex = 0;
+        Arrays.fill(mBuffer, (byte) 0);
     }
 }
diff --git a/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java b/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java
index f575599..0d6807a 100644
--- a/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java
+++ b/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java
@@ -23,31 +23,44 @@
  * An output stream that tracks the total number written bytes since construction and allows
  * querying this value any time during the execution.
  *
- * This class is used for constructing the rule indexing.
+ * <p>This class is used for constructing the rule indexing.
  */
-public class ByteTrackedOutputStream {
+public class ByteTrackedOutputStream extends OutputStream {
 
-    private static int sWrittenBytesCount;
-    private static OutputStream sOutputStream;
+    private static final int INT_BYTES = 4;
+
+    private int mWrittenBytesCount;
+    private final OutputStream mOutputStream;
 
     public ByteTrackedOutputStream(OutputStream outputStream) {
-        sWrittenBytesCount = 0;
-        sOutputStream = outputStream;
+        mWrittenBytesCount = 0;
+        mOutputStream = outputStream;
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        mWrittenBytesCount += INT_BYTES;
+        mOutputStream.write(b);
     }
 
     /**
-     * Writes the given bytes into the output stream provided in constructor and updates the
-     * total number of written bytes.
+     * Writes the given bytes into the output stream provided in constructor and updates the total
+     * number of written bytes.
      */
+    @Override
     public void write(byte[] bytes) throws IOException {
-        sWrittenBytesCount += bytes.length;
-        sOutputStream.write(bytes);
+        mWrittenBytesCount += bytes.length;
+        mOutputStream.write(bytes);
     }
 
-    /**
-     * Returns the total number of bytes written into the output stream at the requested time.
-     */
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+        mWrittenBytesCount += len;
+        mOutputStream.write(b, off, len);
+    }
+
+    /** Returns the total number of bytes written into the output stream at the requested time. */
     public int getWrittenBytesCount() {
-        return sWrittenBytesCount;
+        return mWrittenBytesCount;
     }
 }
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
index 8f53be7..6afadba 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
@@ -97,42 +97,38 @@
                             ruleFileByteTrackedOutputStream);
             LinkedHashMap<String, Integer> unindexedRulesIndexes =
                     serializeRuleList(
-                            indexedRules.get(NOT_INDEXED),
-                            ruleFileByteTrackedOutputStream);
+                            indexedRules.get(NOT_INDEXED), ruleFileByteTrackedOutputStream);
 
             // Serialize their indexes.
-            BitOutputStream indexingBitOutputStream = new BitOutputStream();
-            serializeIndexGroup(packageNameIndexes, indexingBitOutputStream, /* isIndexed= */true);
-            serializeIndexGroup(appCertificateIndexes, indexingBitOutputStream, /* isIndexed= */
-                    true);
-            serializeIndexGroup(unindexedRulesIndexes, indexingBitOutputStream, /* isIndexed= */
-                    false);
-            // TODO(b/147609625): This dummy bit is set for fixing the padding issue. Remove when
-            // the issue is fixed and correct the tests that does this padding too.
-            indexingBitOutputStream.setNext();
-            indexingFileOutputStream.write(indexingBitOutputStream.toByteArray());
+            BitOutputStream indexingBitOutputStream = new BitOutputStream(indexingFileOutputStream);
+            serializeIndexGroup(packageNameIndexes, indexingBitOutputStream, /* isIndexed= */ true);
+            serializeIndexGroup(
+                    appCertificateIndexes, indexingBitOutputStream, /* isIndexed= */ true);
+            serializeIndexGroup(
+                    unindexedRulesIndexes, indexingBitOutputStream, /* isIndexed= */ false);
+            indexingBitOutputStream.flush();
         } catch (Exception e) {
             throw new RuleSerializeException(e.getMessage(), e);
         }
     }
 
-    private void serializeRuleFileMetadata(Optional<Integer> formatVersion,
-            ByteTrackedOutputStream outputStream)
+    private void serializeRuleFileMetadata(
+            Optional<Integer> formatVersion, ByteTrackedOutputStream outputStream)
             throws IOException {
         int formatVersionValue = formatVersion.orElse(DEFAULT_FORMAT_VERSION);
 
-        BitOutputStream bitOutputStream = new BitOutputStream();
+        BitOutputStream bitOutputStream = new BitOutputStream(outputStream);
         bitOutputStream.setNext(FORMAT_VERSION_BITS, formatVersionValue);
-        outputStream.write(bitOutputStream.toByteArray());
+        bitOutputStream.flush();
     }
 
     private LinkedHashMap<String, Integer> serializeRuleList(
             Map<String, List<Rule>> rulesMap, ByteTrackedOutputStream outputStream)
             throws IOException {
-        Preconditions.checkArgument(rulesMap != null,
-                "serializeRuleList should never be called with null rule list.");
+        Preconditions.checkArgument(
+                rulesMap != null, "serializeRuleList should never be called with null rule list.");
 
-        BitOutputStream bitOutputStream = new BitOutputStream();
+        BitOutputStream bitOutputStream = new BitOutputStream(outputStream);
         LinkedHashMap<String, Integer> indexMapping = new LinkedHashMap();
         indexMapping.put(START_INDEXING_KEY, outputStream.getWrittenBytesCount());
 
@@ -145,9 +141,8 @@
             }
 
             for (Rule rule : rulesMap.get(key)) {
-                bitOutputStream.clear();
                 serializeRule(rule, bitOutputStream);
-                outputStream.write(bitOutputStream.toByteArray());
+                bitOutputStream.flush();
                 indexTracker++;
             }
         }
@@ -156,7 +151,7 @@
         return indexMapping;
     }
 
-    private void serializeRule(Rule rule, BitOutputStream bitOutputStream) {
+    private void serializeRule(Rule rule, BitOutputStream bitOutputStream) throws IOException {
         if (rule == null) {
             throw new IllegalArgumentException("Null rule can not be serialized");
         }
@@ -171,7 +166,8 @@
         bitOutputStream.setNext();
     }
 
-    private void serializeFormula(Formula formula, BitOutputStream bitOutputStream) {
+    private void serializeFormula(Formula formula, BitOutputStream bitOutputStream)
+            throws IOException {
         if (formula instanceof AtomicFormula) {
             serializeAtomicFormula((AtomicFormula) formula, bitOutputStream);
         } else if (formula instanceof CompoundFormula) {
@@ -183,7 +179,7 @@
     }
 
     private void serializeCompoundFormula(
-            CompoundFormula compoundFormula, BitOutputStream bitOutputStream) {
+            CompoundFormula compoundFormula, BitOutputStream bitOutputStream) throws IOException {
         if (compoundFormula == null) {
             throw new IllegalArgumentException("Null compound formula can not be serialized");
         }
@@ -197,7 +193,7 @@
     }
 
     private void serializeAtomicFormula(
-            AtomicFormula atomicFormula, BitOutputStream bitOutputStream) {
+            AtomicFormula atomicFormula, BitOutputStream bitOutputStream) throws IOException {
         if (atomicFormula == null) {
             throw new IllegalArgumentException("Null atomic formula can not be serialized");
         }
@@ -231,11 +227,10 @@
     private void serializeIndexGroup(
             LinkedHashMap<String, Integer> indexes,
             BitOutputStream bitOutputStream,
-            boolean isIndexed) {
-
+            boolean isIndexed)
+            throws IOException {
         // Output the starting location of this indexing group.
-        serializeStringValue(
-                START_INDEXING_KEY, /* isHashedValue= */false, bitOutputStream);
+        serializeStringValue(START_INDEXING_KEY, /* isHashedValue= */ false, bitOutputStream);
         serializeIntValue(indexes.get(START_INDEXING_KEY), bitOutputStream);
 
         // If the group is indexed, output the locations of the indexes.
@@ -243,8 +238,8 @@
             for (Map.Entry<String, Integer> entry : indexes.entrySet()) {
                 if (!entry.getKey().equals(START_INDEXING_KEY)
                         && !entry.getKey().equals(END_INDEXING_KEY)) {
-                    serializeStringValue(entry.getKey(), /* isHashedValue= */false,
-                            bitOutputStream);
+                    serializeStringValue(
+                            entry.getKey(), /* isHashedValue= */ false, bitOutputStream);
                     serializeIntValue(entry.getValue(), bitOutputStream);
                 }
             }
@@ -256,7 +251,8 @@
     }
 
     private void serializeStringValue(
-            String value, boolean isHashedValue, BitOutputStream bitOutputStream) {
+            String value, boolean isHashedValue, BitOutputStream bitOutputStream)
+            throws IOException {
         if (value == null) {
             throw new IllegalArgumentException("String value can not be null.");
         }
@@ -269,11 +265,12 @@
         }
     }
 
-    private void serializeIntValue(int value, BitOutputStream bitOutputStream) {
+    private void serializeIntValue(int value, BitOutputStream bitOutputStream) throws IOException {
         bitOutputStream.setNext(/* numOfBits= */ 32, value);
     }
 
-    private void serializeBooleanValue(boolean value, BitOutputStream bitOutputStream) {
+    private void serializeBooleanValue(boolean value, BitOutputStream bitOutputStream)
+            throws IOException {
         bitOutputStream.setNext(value);
     }
 
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 5191833..b67335a 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -306,14 +306,17 @@
                 }
             } else if (state == BluetoothProfile.STATE_DISCONNECTING
                     || state == BluetoothProfile.STATE_DISCONNECTED) {
-                btRoute.connectedProfiles.delete(profile);
-                if (btRoute.connectedProfiles.size() == 0) {
-                    mBluetoothRoutes.remove(device.getAddress());
-                    if (mActiveDevice != null
-                            && TextUtils.equals(mActiveDevice.getAddress(), device.getAddress())) {
-                        mActiveDevice = null;
+                if (btRoute != null) {
+                    btRoute.connectedProfiles.delete(profile);
+                    if (btRoute.connectedProfiles.size() == 0) {
+                        mBluetoothRoutes.remove(device.getAddress());
+                        if (mActiveDevice != null
+                                && TextUtils.equals(mActiveDevice.getAddress(),
+                                device.getAddress())) {
+                            mActiveDevice = null;
+                        }
+                        notifyBluetoothRoutesUpdated();
                     }
-                    notifyBluetoothRoutesUpdated();
                 }
             }
         }
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index df115d0..1905571 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -135,6 +135,7 @@
     private int mMaxVolume = 0;
     private int mCurrentVolume = 0;
     private int mOptimisticVolume = -1;
+    private String mVolumeControlId;
     // End volume handling fields
 
     private boolean mIsActive = false;
@@ -714,7 +715,7 @@
             if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
                 int current = mOptimisticVolume != -1 ? mOptimisticVolume : mCurrentVolume;
                 return new PlaybackInfo(mVolumeType, mVolumeControlType, mMaxVolume, current,
-                        mAudioAttrs);
+                        mAudioAttrs, mVolumeControlId);
             }
             volumeType = mVolumeType;
             attributes = mAudioAttrs;
@@ -723,7 +724,7 @@
         int max = mAudioManager.getStreamMaxVolume(stream);
         int current = mAudioManager.getStreamVolume(stream);
         return new PlaybackInfo(volumeType, VolumeProvider.VOLUME_CONTROL_ABSOLUTE, max,
-                current, attributes);
+                current, attributes, null);
     }
 
     private final Runnable mClearOptimisticVolumeRunnable = new Runnable() {
@@ -886,6 +887,7 @@
             synchronized (mLock) {
                 typeChanged = mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE;
                 mVolumeType = PlaybackInfo.PLAYBACK_TYPE_LOCAL;
+                mVolumeControlId = null;
                 if (attributes != null) {
                     mAudioAttrs = attributes;
                 } else {
@@ -904,13 +906,15 @@
         }
 
         @Override
-        public void setPlaybackToRemote(int control, int max) throws RemoteException {
+        public void setPlaybackToRemote(int control, int max, String controlId)
+                throws RemoteException {
             boolean typeChanged;
             synchronized (mLock) {
                 typeChanged = mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL;
                 mVolumeType = PlaybackInfo.PLAYBACK_TYPE_REMOTE;
                 mVolumeControlType = control;
                 mMaxVolume = max;
+                mVolumeControlId = controlId;
             }
             if (typeChanged) {
                 final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index d8a4655..c518614 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -161,7 +161,7 @@
 import android.net.NetworkState;
 import android.net.NetworkStats;
 import android.net.NetworkTemplate;
-import android.net.StringNetworkSpecifier;
+import android.net.TelephonyNetworkSpecifier;
 import android.net.TrafficStats;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
@@ -2877,17 +2877,6 @@
     }
 
     @Override
-    public void onTetheringChanged(String iface, boolean tethering) {
-        // No need to enforce permission because setRestrictBackground() will do it.
-        synchronized (mUidRulesFirstLock) {
-            if (mRestrictBackground && tethering) {
-                Log.d(TAG, "Tethering on (" + iface +"); disable Data Saver");
-                setRestrictBackground(false);
-            }
-        }
-    }
-
-    @Override
     public void setRestrictBackground(boolean restrictBackground) {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "setRestrictBackground");
         try {
@@ -4522,7 +4511,11 @@
                 }
                 case MSG_STATS_PROVIDER_LIMIT_REACHED: {
                     mNetworkStats.forceUpdate();
+
                     synchronized (mNetworkPoliciesSecondLock) {
+                        // Some providers might hit the limit reached event prior to others. Thus,
+                        // re-calculate and update interface quota for every provider is needed.
+                        updateNetworkRulesNL();
                         updateNetworkEnabledNL();
                         updateNotificationsNL();
                     }
@@ -4530,16 +4523,21 @@
                 }
                 case MSG_LIMIT_REACHED: {
                     final String iface = (String) msg.obj;
+                    synchronized (mNetworkPoliciesSecondLock) {
+                        // fast return if not needed.
+                        if (!mMeteredIfaces.contains(iface)) {
+                            return true;
+                        }
+                    }
+
+                    // force stats update to make sure the service have the numbers that caused
+                    // alert to trigger.
+                    mNetworkStats.forceUpdate();
 
                     synchronized (mNetworkPoliciesSecondLock) {
-                        if (mMeteredIfaces.contains(iface)) {
-                            // force stats update to make sure we have
-                            // numbers that caused alert to trigger.
-                            mNetworkStats.forceUpdate();
-
-                            updateNetworkEnabledNL();
-                            updateNotificationsNL();
-                        }
+                        updateNetworkRulesNL();
+                        updateNetworkEnabledNL();
+                        updateNotificationsNL();
                     }
                     return true;
                 }
@@ -5283,16 +5281,12 @@
     }
 
     private int parseSubId(NetworkState state) {
-        // TODO: moved to using a legitimate NetworkSpecifier instead of string parsing
         int subId = INVALID_SUBSCRIPTION_ID;
         if (state != null && state.networkCapabilities != null
                 && state.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
             NetworkSpecifier spec = state.networkCapabilities.getNetworkSpecifier();
-            if (spec instanceof StringNetworkSpecifier) {
-                try {
-                    subId = Integer.parseInt(((StringNetworkSpecifier) spec).specifier);
-                } catch (NumberFormatException e) {
-                }
+            if (spec instanceof TelephonyNetworkSpecifier) {
+                subId = ((TelephonyNetworkSpecifier) spec).getSubscriptionId();
             }
         }
         return subId;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 159b4e4..410017a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -190,6 +190,7 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
 import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
 import android.content.pm.VerifierDeviceIdentity;
@@ -20021,8 +20022,9 @@
         String initiatingPackageName;
         String originatingPackageName;
 
+        final InstallSource installSource;
         synchronized (mLock) {
-            final InstallSource installSource = getInstallSourceLocked(packageName, callingUid);
+            installSource = getInstallSourceLocked(packageName, callingUid);
             if (installSource == null) {
                 return null;
             }
@@ -20036,9 +20038,16 @@
             }
 
             if (installSource.isInitiatingPackageUninstalled) {
-                // TODO(b/146555198) Allow the app itself to see the info
-                // (at least for non-instant apps)
-                initiatingPackageName = null;
+                // We can't check visibility in the usual way, since the initiating package is no
+                // longer present. So we apply simpler rules to whether to expose the info:
+                // 1. Instant apps can't see it.
+                // 2. Otherwise only the installed app itself can see it.
+                final boolean isInstantApp = getInstantAppPackageName(callingUid) != null;
+                if (!isInstantApp && isCallerSameApp(packageName, callingUid)) {
+                    initiatingPackageName = installSource.initiatingPackageName;
+                } else {
+                    initiatingPackageName = null;
+                }
             } else {
                 // All installSource strings are interned, so == is ok here
                 if (installSource.initiatingPackageName == installSource.installerPackageName) {
@@ -20063,13 +20072,27 @@
             }
         }
 
+        // Remaining work can safely be done outside the lock. (Note that installSource is
+        // immutable so it's ok to carry on reading from it.)
+
         if (originatingPackageName != null && mContext.checkCallingOrSelfPermission(
                 Manifest.permission.INSTALL_PACKAGES) != PackageManager.PERMISSION_GRANTED) {
             originatingPackageName = null;
         }
 
-        return new InstallSourceInfo(initiatingPackageName, originatingPackageName,
-                installerPackageName);
+        // If you can see the initiatingPackageName, and we have valid signing info for it,
+        // then we let you see that too.
+        final SigningInfo initiatingPackageSigningInfo;
+        final PackageSignatures signatures = installSource.initiatingPackageSignatures;
+        if (initiatingPackageName != null && signatures != null
+                && signatures.mSigningDetails != SigningDetails.UNKNOWN) {
+            initiatingPackageSigningInfo = new SigningInfo(signatures.mSigningDetails);
+        } else {
+            initiatingPackageSigningInfo = null;
+        }
+
+        return new InstallSourceInfo(initiatingPackageName, initiatingPackageSigningInfo,
+                originatingPackageName, installerPackageName);
     }
 
     @GuardedBy("mLock")
@@ -22267,6 +22290,13 @@
         mPermissionManager.onNewUserCreated(userId);
     }
 
+    boolean readPermissionStateForUser(@UserIdInt int userId) {
+        synchronized (mPackages) {
+            mSettings.readPermissionStateForUserSyncLPr(userId);
+            return mSettings.areDefaultRuntimePermissionsGrantedLPr(userId);
+        }
+    }
+
     @Override
     public VerifierDeviceIdentity getVerifierDeviceIdentity() throws RemoteException {
         mContext.enforceCallingOrSelfPermission(
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 3e665c3..4f18cb4 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3125,6 +3125,10 @@
         return true;
     }
 
+    void readPermissionStateForUserSyncLPr(@UserIdInt int userId) {
+        mRuntimePermissionsPersistence.readStateForUserSyncLPr(userId);
+    }
+
     void applyDefaultPreferredAppsLPw(int userId) {
         // First pull data from any pre-installed apps.
         final PackageManagerInternal pmInternal =
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 2265d01..7888d1f 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -49,6 +49,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.ParcelableException;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -570,18 +571,17 @@
         } else {
             params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION;
         }
-        int apkSessionId = mPi.createSession(
-                params, originalSession.getInstallerPackageName(),
-                0 /* UserHandle.SYSTEM */);
-        PackageInstallerSession apkSession = mPi.getSession(apkSessionId);
-
         try {
+            int apkSessionId = mPi.createSession(
+                    params, originalSession.getInstallerPackageName(),
+                    0 /* UserHandle.SYSTEM */);
+            PackageInstallerSession apkSession = mPi.getSession(apkSessionId);
             apkSession.open();
             for (String apkFilePath : apkFilePaths) {
                 File apkFile = new File(apkFilePath);
                 ParcelFileDescriptor pfd = ParcelFileDescriptor.open(apkFile,
                         ParcelFileDescriptor.MODE_READ_ONLY);
-                long sizeBytes = pfd.getStatSize();
+                long sizeBytes = (pfd == null) ? -1 : pfd.getStatSize();
                 if (sizeBytes < 0) {
                     Slog.e(TAG, "Unable to get size of: " + apkFilePath);
                     throw new PackageManagerException(errorCode,
@@ -589,11 +589,11 @@
                 }
                 apkSession.write(apkFile.getName(), 0, sizeBytes, pfd);
             }
-        } catch (IOException e) {
+            return apkSession;
+        } catch (IOException | ParcelableException e) {
             Slog.e(TAG, "Failure to install APK staged session " + originalSession.sessionId, e);
-            throw new PackageManagerException(errorCode, "Failed to write APK session", e);
+            throw new PackageManagerException(errorCode, "Failed to create/write APK session", e);
         }
-        return apkSession;
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index e5d5b57..6ee8648 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -492,6 +492,10 @@
         public void onBootPhase(int phase) {
             if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
                 mUms.cleanupPartialUsers();
+
+                if (mUms.mPm.isDeviceUpgrading()) {
+                    mUms.cleanupPreCreatedUsers();
+                }
             }
         }
 
@@ -617,6 +621,33 @@
         }
     }
 
+    /**
+     * Removes any pre-created users from the system. Should be invoked after OTAs, to ensure
+     * pre-created users are not stale. New pre-created pool can be re-created after the update.
+     */
+    void cleanupPreCreatedUsers() {
+        final ArrayList<UserInfo> preCreatedUsers;
+        synchronized (mUsersLock) {
+            final int userSize = mUsers.size();
+            preCreatedUsers = new ArrayList<>(userSize);
+            for (int i = 0; i < userSize; i++) {
+                UserInfo ui = mUsers.valueAt(i).info;
+                if (ui.preCreated) {
+                    preCreatedUsers.add(ui);
+                    addRemovingUserIdLocked(ui.id);
+                    ui.flags |= UserInfo.FLAG_DISABLED;
+                    ui.partial = true;
+                }
+            }
+        }
+        final int preCreatedSize = preCreatedUsers.size();
+        for (int i = 0; i < preCreatedSize; i++) {
+            UserInfo ui = preCreatedUsers.get(i);
+            Slog.i(LOG_TAG, "Removing pre-created user " + ui.id);
+            removeUserState(ui.id);
+        }
+    }
+
     @Override
     public String getUserAccount(@UserIdInt int userId) {
         checkManageUserAndAcrossUsersFullPermission("get user account");
@@ -3078,7 +3109,6 @@
             @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int parentId,
             boolean preCreate, @Nullable String[] disallowedPackages,
             @NonNull TimingsTraceAndSlog t) {
-
         final UserTypeDetails userTypeDetails = mUserTypes.get(userType);
         if (userTypeDetails == null) {
             Slog.e(LOG_TAG, "Cannot create user of invalid user type: " + userType);
@@ -3254,9 +3284,9 @@
                 mBaseUserRestrictions.append(userId, restrictions);
             }
 
-            t.traceBegin("PM.onNewUserCreated");
+            t.traceBegin("PM.onNewUserCreated-" + userId);
             mPm.onNewUserCreated(userId);
-
+            t.traceEnd();
             if (preCreate) {
                 // Must start user (which will be stopped right away, through
                 // UserController.finishUserUnlockedCompleted) so services can properly
@@ -3323,11 +3353,16 @@
         preCreatedUser.preCreated = false;
         preCreatedUser.creationTime = getCreationTime();
 
-        dispatchUserAddedIntent(preCreatedUser);
         synchronized (mPackagesLock) {
             writeUserLP(preCreatedUserData);
             writeUserListLP();
         }
+        updateUserIds();
+        if (!mPm.readPermissionStateForUser(preCreatedUser.id)) {
+            // Could not read the existing permissions, re-grant them.
+            mPm.onNewUserCreated(preCreatedUser.id);
+        }
+        dispatchUserAddedIntent(preCreatedUser);
         return preCreatedUser;
     }
 
@@ -4027,14 +4062,16 @@
         synchronized (mUsersLock) {
             final int userSize = mUsers.size();
             for (int i = 0; i < userSize; i++) {
-                if (!mUsers.valueAt(i).info.partial) {
+                UserInfo userInfo = mUsers.valueAt(i).info;
+                if (!userInfo.partial && !userInfo.preCreated) {
                     num++;
                 }
             }
             final int[] newUsers = new int[num];
             int n = 0;
             for (int i = 0; i < userSize; i++) {
-                if (!mUsers.valueAt(i).info.partial) {
+                UserInfo userInfo = mUsers.valueAt(i).info;
+                if (!userInfo.partial && !userInfo.preCreated) {
                     newUsers[n++] = mUsers.keyAt(i);
                 }
             }
@@ -4095,7 +4132,10 @@
      * recycled.
      */
     void reconcileUsers(String volumeUuid) {
-        mUserDataPreparer.reconcileUsers(volumeUuid, getUsers(true /* excludeDying */));
+        mUserDataPreparer.reconcileUsers(volumeUuid, getUsers(
+                /* excludePartial= */ true,
+                /* excludeDying= */ true,
+                /* excludePreCreated= */ false));
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 815f7b4..89030ed 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -199,13 +199,31 @@
     );
 
     /**
-     * Special user restrictions that are applied globally when set by the profile owner of a
-     * managed profile that was created during the device provisioning flow.
+     * Special user restrictions that profile owner of an organization-owned managed profile can
+     * set on the parent profile instance to apply them globally.
      */
     private static final Set<String> PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS =
             Sets.newArraySet(
                     UserManager.DISALLOW_CONFIG_DATE_TIME,
-                    UserManager.DISALLOW_CAMERA
+                    UserManager.DISALLOW_CAMERA,
+                    UserManager.DISALLOW_ADD_USER,
+                    UserManager.DISALLOW_BLUETOOTH,
+                    UserManager.DISALLOW_BLUETOOTH_SHARING,
+                    UserManager.DISALLOW_CONFIG_BLUETOOTH,
+                    UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
+                    UserManager.DISALLOW_CONFIG_LOCATION,
+                    UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
+                    UserManager.DISALLOW_CONFIG_PRIVATE_DNS,
+                    UserManager.DISALLOW_CONFIG_TETHERING,
+                    UserManager.DISALLOW_CONFIG_WIFI,
+                    UserManager.DISALLOW_CONTENT_CAPTURE,
+                    UserManager.DISALLOW_CONTENT_SUGGESTIONS,
+                    UserManager.DISALLOW_DATA_ROAMING,
+                    UserManager.DISALLOW_DEBUGGING_FEATURES,
+                    UserManager.DISALLOW_SAFE_BOOT,
+                    UserManager.DISALLOW_SHARE_LOCATION,
+                    UserManager.DISALLOW_SMS,
+                    UserManager.DISALLOW_USB_FILE_TRANSFER
     );
 
     /**
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 9f592b8..7d0072a 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -172,6 +172,13 @@
     @Nullable public final String mInstallerPackageName;
 
     /**
+     * This array holds all of the rollback tokens associated with package sessions included in
+     * this rollback.
+     */
+    @GuardedBy("mLock")
+    private final IntArray mTokens = new IntArray();
+
+    /**
      * Constructs a new, empty Rollback instance.
      *
      * @param rollbackId the id of the rollback.
@@ -766,6 +773,26 @@
         }
     }
 
+    /**
+     * Adds a rollback token to be associated with this rollback. This may be used to
+     * identify which rollback should be removed in case {@link PackageManager} sends an
+     * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} intent.
+     */
+    void addToken(int token) {
+        synchronized (mLock) {
+            mTokens.add(token);
+        }
+    }
+
+    /**
+     * Returns true if this rollback is associated with the provided {@code token}.
+     */
+    boolean hasToken(int token) {
+        synchronized (mLock) {
+            return mTokens.indexOf(token) != -1;
+        }
+    }
+
     static String rollbackStateToString(@RollbackState int state) {
         switch (state) {
             case Rollback.ROLLBACK_STATE_ENABLING: return "enabling";
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 8f8a5c4..eefcde6 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -235,12 +235,17 @@
                         Slog.v(TAG, "broadcast=ACTION_CANCEL_ENABLE_ROLLBACK token=" + token);
                     }
                     synchronized (mLock) {
-                        for (NewRollback rollback : mNewRollbacks) {
-                            if (rollback.hasToken(token)) {
-                                rollback.setCancelled();
-                                return;
+                        NewRollback found = null;
+                        for (NewRollback newRollback : mNewRollbacks) {
+                            if (newRollback.rollback.hasToken(token)) {
+                                found = newRollback;
+                                break;
                             }
                         }
+                        if (found != null) {
+                            mNewRollbacks.remove(found);
+                            found.rollback.delete(mAppDataRollbackHelper);
+                        }
                     }
                 }
             }
@@ -433,10 +438,14 @@
                     rollback.delete(mAppDataRollbackHelper);
                 }
             }
-            for (NewRollback newRollback : mNewRollbacks) {
+            Iterator<NewRollback> iter2 = mNewRollbacks.iterator();
+            while (iter2.hasNext()) {
+                NewRollback newRollback = iter2.next();
                 if (newRollback.rollback.includesPackage(packageName)) {
-                    newRollback.setCancelled();
+                    iter2.remove();
+                    newRollback.rollback.delete(mAppDataRollbackHelper);
                 }
+
             }
         }
     }
@@ -798,7 +807,7 @@
                 mNewRollbacks.add(newRollback);
             }
         }
-        newRollback.addToken(token);
+        newRollback.rollback.addToken(token);
 
         return enableRollbackForPackageSession(newRollback.rollback, packageSession);
     }
@@ -1220,12 +1229,6 @@
             Slog.v(TAG, "completeEnableRollback id=" + rollback.info.getRollbackId());
         }
 
-        if (newRollback.isCancelled()) {
-            Slog.e(TAG, "Rollback has been cancelled by PackageManager");
-            rollback.delete(mAppDataRollbackHelper);
-            return null;
-        }
-
         // We are checking if number of packages (excluding apk-in-apex) we enabled for rollback is
         // equal to the number of sessions we are installing, to ensure we didn't skip enabling
         // of any sessions. If we successfully enable an apex, then we can assume we enabled
@@ -1335,22 +1338,12 @@
         public final Rollback rollback;
 
         /**
-         * This array holds all of the rollback tokens associated with package sessions included in
-         * this rollback.
-         */
-        @GuardedBy("mNewRollbackLock")
-        private final IntArray mTokens = new IntArray();
-
-        /**
          * Session ids for all packages in the install. For multi-package sessions, this is the list
          * of child session ids. For normal sessions, this list is a single element with the normal
          * session id.
          */
         private final int[] mPackageSessionIds;
 
-        @GuardedBy("mNewRollbackLock")
-        private boolean mIsCancelled = false;
-
         /**
          * The number of sessions in the install which are notified with success by
          * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)}.
@@ -1367,52 +1360,6 @@
         }
 
         /**
-         * Adds a rollback token to be associated with this NewRollback. This may be used to
-         * identify which rollback should be cancelled in case {@link PackageManager} sends an
-         * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} intent.
-         */
-        void addToken(int token) {
-            synchronized (mNewRollbackLock) {
-                mTokens.add(token);
-            }
-        }
-
-        /**
-         * Returns true if this NewRollback is associated with the provided {@code token}.
-         */
-        boolean hasToken(int token) {
-            synchronized (mNewRollbackLock) {
-                return mTokens.indexOf(token) != -1;
-            }
-        }
-
-        /**
-         * Returns true if this NewRollback has been cancelled.
-         *
-         * <p>Rollback could be invalidated and cancelled if RollbackManager receives
-         * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} from {@link PackageManager}.
-         *
-         * <p>The main underlying assumption here is that if enabling the rollback times out, then
-         * {@link PackageManager} will NOT send
-         * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)} before it broadcasts
-         * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK}.
-         */
-        boolean isCancelled() {
-            synchronized (mNewRollbackLock) {
-                return mIsCancelled;
-            }
-        }
-
-        /**
-         * Sets this NewRollback to be marked as cancelled.
-         */
-        void setCancelled() {
-            synchronized (mNewRollbackLock) {
-                mIsCancelled = true;
-            }
-        }
-
-        /**
          * Returns true if this NewRollback contains the provided {@code packageSessionId}.
          */
         boolean containsSessionId(int packageSessionId) {
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index a0ef8cf..b9ef7b3 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -280,7 +280,6 @@
                             .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED,
                             WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN,
                             "");
-                    mContext.getSystemService(PowerManager.class).reboot("Rollback staged install");
                 } else if (sessionInfo.isStagedSessionFailed()
                         && markStagedSessionHandled(rollbackId)) {
                     logEvent(moduleMetadataPackage,
@@ -291,6 +290,11 @@
                 }
             }
         }
+
+        // Wait for all pending staged sessions to get handled before rebooting.
+        if (isPendingStagedSessionsEmpty()) {
+            mContext.getSystemService(PowerManager.class).reboot("Rollback staged install");
+        }
     }
 
     /**
@@ -303,6 +307,16 @@
         }
     }
 
+    /**
+     * Returns {@code true} if all pending staged rollback sessions were marked as handled,
+     * {@code false} if there is any left.
+     */
+    private boolean isPendingStagedSessionsEmpty() {
+        synchronized (mPendingStagedRollbackIds) {
+            return mPendingStagedRollbackIds.isEmpty();
+        }
+    }
+
     private void saveLastStagedRollbackId(int stagedRollbackId) {
         try {
             FileOutputStream fos = new FileOutputStream(mLastStagedRollbackIdFile);
@@ -414,6 +428,9 @@
                             reasonToLog, failedPackageToLog);
                 }
             } else {
+                if (rollback.isStaged()) {
+                    markStagedSessionHandled(rollback.getRollbackId());
+                }
                 logEvent(logPackage,
                         StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
                         reasonToLog, failedPackageToLog);
@@ -431,6 +448,16 @@
         RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
         List<RollbackInfo> rollbacks = rollbackManager.getAvailableRollbacks();
 
+        // Add all rollback ids to mPendingStagedRollbackIds, so that we do not reboot before all
+        // pending staged rollbacks are handled.
+        synchronized (mPendingStagedRollbackIds) {
+            for (RollbackInfo rollback : rollbacks) {
+                if (rollback.isStaged()) {
+                    mPendingStagedRollbackIds.add(rollback.getRollbackId());
+                }
+            }
+        }
+
         for (RollbackInfo rollback : rollbacks) {
             VersionedPackage sample = rollback.getPackages().get(0).getVersionRolledBackFrom();
             rollbackPackage(rollback, sample, PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java b/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java
new file mode 100644
index 0000000..b19e2ed
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java
@@ -0,0 +1,31 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
+
+/**
+ * A factory for creating instances of {@link ISoundTriggerHw}.
+ *
+ * @hide
+ */
+public interface HalFactory {
+    /**
+     * @return An instance of {@link ISoundTriggerHw}.
+     */
+    ISoundTriggerHw create();
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
index 9d51b65..9f4b09a 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
@@ -78,18 +78,17 @@
     }
 
     /**
-     * Most generic constructor - gets an array of HAL driver instances.
+     * Constructor - gets an array of HAL driver factories.
      */
-    public SoundTriggerMiddlewareImpl(@NonNull ISoundTriggerHw[] halServices,
+    public SoundTriggerMiddlewareImpl(@NonNull HalFactory[] halFactories,
             @NonNull AudioSessionProvider audioSessionProvider) {
-        List<SoundTriggerModule> modules = new ArrayList<>(halServices.length);
+        List<SoundTriggerModule> modules = new ArrayList<>(halFactories.length);
 
-        for (int i = 0; i < halServices.length; ++i) {
-            ISoundTriggerHw service = halServices[i];
+        for (int i = 0; i < halFactories.length; ++i) {
             try {
-                modules.add(new SoundTriggerModule(service, audioSessionProvider));
+                modules.add(new SoundTriggerModule(halFactories[i], audioSessionProvider));
             } catch (Exception e) {
-                Log.e(TAG, "Failed to a SoundTriggerModule instance", e);
+                Log.e(TAG, "Failed to add a SoundTriggerModule instance", e);
             }
         }
 
@@ -97,11 +96,11 @@
     }
 
     /**
-     * Convenience constructor - gets a single HAL driver instance.
+     * Convenience constructor - gets a single HAL factory.
      */
-    public SoundTriggerMiddlewareImpl(@NonNull ISoundTriggerHw halService,
+    public SoundTriggerMiddlewareImpl(@NonNull HalFactory factory,
             @NonNull AudioSessionProvider audioSessionProvider) {
-        this(new ISoundTriggerHw[]{halService}, audioSessionProvider);
+        this(new HalFactory[]{factory}, audioSessionProvider);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
index 5a06a2c..12f9fd9 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
@@ -113,9 +113,9 @@
 public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareService.Stub {
     static private final String TAG = "SoundTriggerMiddlewareService";
 
-    final ISoundTriggerMiddlewareService mDelegate;
-    final Context mContext;
-    Set<Integer> mModuleHandles;
+    private final ISoundTriggerMiddlewareService mDelegate;
+    private final Context mContext;
+    private Set<Integer> mModuleHandles;
 
     /**
      * Constructor for internal use only. Could be exposed for testing purposes in the future.
@@ -280,7 +280,7 @@
         }
 
         /** Activity state. */
-        public Activity activityState = Activity.LOADED;
+        Activity activityState = Activity.LOADED;
 
         /**
          * A map of known parameter support. A missing key means we don't know yet whether the
@@ -294,7 +294,7 @@
          *
          * @param modelParam The parameter key.
          */
-        public void checkSupported(int modelParam) {
+        void checkSupported(int modelParam) {
             if (!parameterSupport.containsKey(modelParam)) {
                 throw new IllegalStateException("Parameter has not been checked for support.");
             }
@@ -311,7 +311,7 @@
          * @param modelParam The parameter key.
          * @param value      The value.
          */
-        public void checkSupported(int modelParam, int value) {
+        void checkSupported(int modelParam, int value) {
             if (!parameterSupport.containsKey(modelParam)) {
                 throw new IllegalStateException("Parameter has not been checked for support.");
             }
@@ -329,7 +329,7 @@
          * @param modelParam The parameter key.
          * @param range      The parameter value range, or null if not supported.
          */
-        public void updateParameterSupport(int modelParam, @Nullable ModelParameterRange range) {
+        void updateParameterSupport(int modelParam, @Nullable ModelParameterRange range) {
             parameterSupport.put(modelParam, range);
         }
     }
@@ -338,27 +338,26 @@
      * Entry-point to this module: exposes the module as a {@link SystemService}.
      */
     public static final class Lifecycle extends SystemService {
-        private SoundTriggerMiddlewareService mService;
-
         public Lifecycle(Context context) {
             super(context);
         }
 
         @Override
         public void onStart() {
-            ISoundTriggerHw[] services;
-            try {
-                services = new ISoundTriggerHw[]{ISoundTriggerHw.getService(true)};
-                Log.d(TAG, "Connected to default ISoundTriggerHw");
-            } catch (Exception e) {
-                Log.e(TAG, "Failed to connect to default ISoundTriggerHw", e);
-                services = new ISoundTriggerHw[0];
-            }
+            HalFactory[] factories = new HalFactory[]{() -> {
+                try {
+                    Log.d(TAG, "Connecting to default ISoundTriggerHw");
+                    return ISoundTriggerHw.getService(true);
+                } catch (RemoteException e) {
+                    throw e.rethrowAsRuntimeException();
+                }
+            }};
 
-            mService = new SoundTriggerMiddlewareService(
-                    new SoundTriggerMiddlewareImpl(services, new AudioSessionProviderImpl()),
-                    getContext());
-            publishBinderService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE, mService);
+            publishBinderService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE,
+                    new SoundTriggerMiddlewareService(
+                            new SoundTriggerMiddlewareImpl(factories,
+                                    new AudioSessionProviderImpl()),
+                            getContext()));
         }
     }
 
@@ -370,7 +369,7 @@
             DeathRecipient {
         private final ISoundTriggerCallback mCallback;
         private ISoundTriggerModule mDelegate;
-        private Map<Integer, ModelState> mLoadedModels = new HashMap<>();
+        private @NonNull Map<Integer, ModelState> mLoadedModels = new HashMap<>();
 
         ModuleService(@NonNull ISoundTriggerCallback callback) {
             mCallback = callback;
@@ -680,7 +679,7 @@
                 } catch (RemoteException e) {
                     // Dead client will be handled by binderDied() - no need to handle here.
                     // In any case, client callbacks are considered best effort.
-                    Log.e(TAG, "Client callback execption.", e);
+                    Log.e(TAG, "Client callback exception.", e);
                 }
             }
         }
@@ -696,20 +695,33 @@
                 } catch (RemoteException e) {
                     // Dead client will be handled by binderDied() - no need to handle here.
                     // In any case, client callbacks are considered best effort.
-                    Log.e(TAG, "Client callback execption.", e);
+                    Log.e(TAG, "Client callback exception.", e);
                 }
             }
         }
 
         @Override
-        public void onRecognitionAvailabilityChange(boolean available) throws RemoteException {
+        public void onRecognitionAvailabilityChange(boolean available) {
             synchronized (this) {
                 try {
                     mCallback.onRecognitionAvailabilityChange(available);
                 } catch (RemoteException e) {
                     // Dead client will be handled by binderDied() - no need to handle here.
                     // In any case, client callbacks are considered best effort.
-                    Log.e(TAG, "Client callback execption.", e);
+                    Log.e(TAG, "Client callback exception.", e);
+                }
+            }
+        }
+
+        @Override
+        public void onModuleDied() {
+            synchronized (this) {
+                try {
+                    mCallback.onModuleDied();
+                } catch (RemoteException e) {
+                    // Dead client will be handled by binderDied() - no need to handle here.
+                    // In any case, client callbacks are considered best effort.
+                    Log.e(TAG, "Client callback exception.", e);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index f024ede..adf16fa 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -30,7 +30,9 @@
 import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
 import android.media.soundtrigger_middleware.Status;
 import android.os.IBinder;
+import android.os.IHwBinder;
 import android.os.RemoteException;
+import android.os.ServiceSpecificException;
 import android.util.Log;
 
 import java.util.HashMap;
@@ -57,6 +59,7 @@
  * gracefully handle driver malfunction and such behavior will result in undefined behavior. If this
  * service is to used with an untrusted driver, the driver must be wrapped with validation / error
  * recovery code.
+ * <li>Recovery from driver death is supported.</li>
  * <li>RemoteExceptions thrown by the driver are treated as RuntimeExceptions - they are not
  * considered recoverable faults and should not occur in a properly functioning system.
  * <li>There is no binder instance associated with this implementation. Do not call asBinder().
@@ -79,27 +82,29 @@
  *
  * @hide
  */
-class SoundTriggerModule {
+class SoundTriggerModule implements IHwBinder.DeathRecipient {
     static private final String TAG = "SoundTriggerModule";
-    @NonNull private final ISoundTriggerHw2 mHalService;
+    @NonNull private HalFactory mHalFactory;
+    @NonNull private ISoundTriggerHw2 mHalService;
     @NonNull private final SoundTriggerMiddlewareImpl.AudioSessionProvider mAudioSessionProvider;
     private final Set<Session> mActiveSessions = new HashSet<>();
     private int mNumLoadedModels = 0;
-    private SoundTriggerModuleProperties mProperties = null;
+    private SoundTriggerModuleProperties mProperties;
     private boolean mRecognitionAvailable;
 
     /**
      * Ctor.
      *
-     * @param halService The underlying HAL driver.
+     * @param halFactory A factory for the underlying HAL driver.
      */
-    SoundTriggerModule(@NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw halService,
+    SoundTriggerModule(@NonNull HalFactory halFactory,
             @NonNull SoundTriggerMiddlewareImpl.AudioSessionProvider audioSessionProvider) {
-        assert halService != null;
-        mHalService = new SoundTriggerHw2Compat(halService);
+        assert halFactory != null;
+        mHalFactory = halFactory;
         mAudioSessionProvider = audioSessionProvider;
-        mProperties = ConversionUtil.hidl2aidlProperties(mHalService.getProperties());
 
+        attachToHal();
+        mProperties = ConversionUtil.hidl2aidlProperties(mHalService.getProperties());
         // We conservatively assume that external capture is active until explicitly told otherwise.
         mRecognitionAvailable = mProperties.concurrentCapture;
     }
@@ -117,7 +122,7 @@
      * @return The interface through which this module can be controlled.
      */
     synchronized @NonNull
-    Session attach(@NonNull ISoundTriggerCallback callback) {
+    ISoundTriggerModule attach(@NonNull ISoundTriggerCallback callback) {
         Log.d(TAG, "attach()");
         Session session = new Session(callback);
         mActiveSessions.add(session);
@@ -145,6 +150,7 @@
      */
     synchronized void setExternalCaptureState(boolean active) {
         Log.d(TAG, String.format("setExternalCaptureState(active=%b)", active));
+
         if (mProperties.concurrentCapture) {
             // If we support concurrent capture, we don't care about any of this.
             return;
@@ -162,6 +168,23 @@
         }
     }
 
+    @Override
+    public synchronized  void serviceDied(long cookie) {
+        Log.w(TAG, String.format("Underlying HAL driver died."));
+        for (Session session : mActiveSessions) {
+            session.moduleDied();
+        }
+        attachToHal();
+    }
+
+    /**
+     * Attached to the HAL service via factory.
+     */
+    private void attachToHal() {
+        mHalService = new SoundTriggerHw2Compat(mHalFactory.create());
+        mHalService.linkToDeath(this, 0);
+    }
+
     /**
      * Remove session from the list of active sessions.
      *
@@ -204,7 +227,11 @@
         public void detach() {
             Log.d(TAG, "detach()");
             synchronized (SoundTriggerModule.this) {
+                if (mCallback == null) {
+                    return;
+                }
                 removeSession(this);
+                mCallback = null;
             }
         }
 
@@ -212,6 +239,7 @@
         public int loadModel(@NonNull SoundModel model) {
             Log.d(TAG, String.format("loadModel(model=%s)", model));
             synchronized (SoundTriggerModule.this) {
+                checkValid();
                 if (mNumLoadedModels == mProperties.maxSoundModels) {
                     throw new RecoverableException(Status.RESOURCE_CONTENTION,
                             "Maximum number of models loaded.");
@@ -227,6 +255,7 @@
         public int loadPhraseModel(@NonNull PhraseSoundModel model) {
             Log.d(TAG, String.format("loadPhraseModel(model=%s)", model));
             synchronized (SoundTriggerModule.this) {
+                checkValid();
                 if (mNumLoadedModels == mProperties.maxSoundModels) {
                     throw new RecoverableException(Status.RESOURCE_CONTENTION,
                             "Maximum number of models loaded.");
@@ -243,6 +272,7 @@
         public void unloadModel(int modelHandle) {
             Log.d(TAG, String.format("unloadModel(handle=%d)", modelHandle));
             synchronized (SoundTriggerModule.this) {
+                checkValid();
                 mLoadedModels.get(modelHandle).unload();
                 --mNumLoadedModels;
             }
@@ -253,6 +283,7 @@
             Log.d(TAG,
                     String.format("startRecognition(handle=%d, config=%s)", modelHandle, config));
             synchronized (SoundTriggerModule.this) {
+                checkValid();
                 mLoadedModels.get(modelHandle).startRecognition(config);
             }
         }
@@ -269,26 +300,28 @@
         public void forceRecognitionEvent(int modelHandle) {
             Log.d(TAG, String.format("forceRecognitionEvent(handle=%d)", modelHandle));
             synchronized (SoundTriggerModule.this) {
+                checkValid();
                 mLoadedModels.get(modelHandle).forceRecognitionEvent();
             }
         }
 
         @Override
-        public void setModelParameter(int modelHandle, int modelParam, int value)
-                throws RemoteException {
+        public void setModelParameter(int modelHandle, int modelParam, int value) {
             Log.d(TAG,
                     String.format("setModelParameter(handle=%d, param=%d, value=%d)", modelHandle,
                             modelParam, value));
             synchronized (SoundTriggerModule.this) {
+                checkValid();
                 mLoadedModels.get(modelHandle).setParameter(modelParam, value);
             }
         }
 
         @Override
-        public int getModelParameter(int modelHandle, int modelParam) throws RemoteException {
+        public int getModelParameter(int modelHandle, int modelParam) {
             Log.d(TAG, String.format("getModelParameter(handle=%d, param=%d)", modelHandle,
                     modelParam));
             synchronized (SoundTriggerModule.this) {
+                checkValid();
                 return mLoadedModels.get(modelHandle).getParameter(modelParam);
             }
         }
@@ -299,6 +332,7 @@
             Log.d(TAG, String.format("queryModelParameterSupport(handle=%d, param=%d)", modelHandle,
                     modelParam));
             synchronized (SoundTriggerModule.this) {
+                checkValid();
                 return mLoadedModels.get(modelHandle).queryModelParameterSupport(modelParam);
             }
         }
@@ -322,6 +356,25 @@
             }
         }
 
+        /**
+         * The underlying module HAL is dead.
+         */
+        private void moduleDied() {
+            try {
+                mCallback.onModuleDied();
+                removeSession(this);
+                mCallback = null;
+            } catch (RemoteException e) {
+                e.rethrowAsRuntimeException();
+            }
+        }
+
+        private void checkValid() {
+            if (mCallback == null) {
+                throw new ServiceSpecificException(Status.DEAD_OBJECT);
+            }
+        }
+
         @Override
         public @NonNull
         IBinder asBinder() {
@@ -350,10 +403,6 @@
                 SoundTriggerModule.this.notifyAll();
             }
 
-            private void waitStateChange() throws InterruptedException {
-                SoundTriggerModule.this.wait();
-            }
-
             private int load(@NonNull SoundModel model) {
                 mModelType = model.type;
                 ISoundTriggerHw.SoundModel hidlModel = ConversionUtil.aidl2hidlSoundModel(model);
diff --git a/services/core/java/com/android/server/stats/StatsPullAtomService.java b/services/core/java/com/android/server/stats/StatsPullAtomService.java
index 5ee7eff..1f7ed8a 100644
--- a/services/core/java/com/android/server/stats/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/StatsPullAtomService.java
@@ -210,6 +210,18 @@
     @Override
     public void onStart() {
         mStatsManager = (StatsManager) mContext.getSystemService(Context.STATS_MANAGER);
+
+        // Used to initialize the CPU Frequency atom.
+        PowerProfile powerProfile = new PowerProfile(mContext);
+        final int numClusters = powerProfile.getNumCpuClusters();
+        mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters];
+        int firstCpuOfCluster = 0;
+        for (int i = 0; i < numClusters; i++) {
+            final int numSpeedSteps = powerProfile.getNumSpeedStepsInCpuCluster(i);
+            mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster,
+                    numSpeedSteps);
+            firstCpuOfCluster += powerProfile.getNumCoresInCpuCluster(i);
+        }
     }
 
     @Override
@@ -610,52 +622,193 @@
         return StatsManager.PULL_SUCCESS;
     }
 
+    private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
+    private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
+
     private void registerKernelWakelock() {
-        // No op.
+        int tagId = StatsLog.KERNEL_WAKELOCK;
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                /* PullAtomMetadata */ null,
+                (atomTag, data) -> pullKernelWakelock(atomTag, data),
+                Executors.newSingleThreadExecutor()
+        );
     }
 
-    private void pullKernelWakelock() {
-        // No op.
+    private int pullKernelWakelock(int atomTag, List<StatsEvent> pulledData) {
+        final KernelWakelockStats wakelockStats =
+                mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
+        for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
+            String name = ent.getKey();
+            KernelWakelockStats.Entry kws = ent.getValue();
+            StatsEvent e = StatsEvent.newBuilder()
+                    .setAtomId(atomTag)
+                    .writeString(name)
+                    .writeInt(kws.mCount)
+                    .writeInt(kws.mVersion)
+                    .writeLong(kws.mTotalTime)
+                    .build();
+            pulledData.add(e);
+        }
+        return StatsManager.PULL_SUCCESS;
     }
 
+    private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
+    // Disables throttler on CPU time readers.
+    private KernelCpuUidUserSysTimeReader mCpuUidUserSysTimeReader =
+            new KernelCpuUidUserSysTimeReader(false);
+    private KernelCpuUidFreqTimeReader mCpuUidFreqTimeReader =
+            new KernelCpuUidFreqTimeReader(false);
+    private KernelCpuUidActiveTimeReader mCpuUidActiveTimeReader =
+            new KernelCpuUidActiveTimeReader(false);
+    private KernelCpuUidClusterTimeReader mCpuUidClusterTimeReader =
+            new KernelCpuUidClusterTimeReader(false);
+
     private void registerCpuTimePerFreq() {
-        // No op.
+        int tagId = StatsLog.CPU_TIME_PER_FREQ;
+        PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+                .setAdditiveFields(new int[] {3})
+                .build();
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                metadata,
+                (atomTag, data) -> pullCpuTimePerFreq(atomTag, data),
+                Executors.newSingleThreadExecutor()
+        );
     }
 
-    private void pullCpuTimePerFreq() {
-        // No op.
+    private int pullCpuTimePerFreq(int atomTag, List<StatsEvent> pulledData) {
+        for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
+            long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute();
+            if (clusterTimeMs != null) {
+                for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
+                    StatsEvent e = StatsEvent.newBuilder()
+                            .setAtomId(atomTag)
+                            .writeInt(cluster)
+                            .writeInt(speed)
+                            .writeLong(clusterTimeMs[speed])
+                            .build();
+                    pulledData.add(e);
+                }
+            }
+        }
+        return StatsManager.PULL_SUCCESS;
     }
 
     private void registerCpuTimePerUid() {
-        // No op.
+        int tagId = StatsLog.CPU_TIME_PER_UID;
+        PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+                .setAdditiveFields(new int[] {2, 3})
+                .build();
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                metadata,
+                (atomTag, data) -> pullCpuTimePerUid(atomTag, data),
+                Executors.newSingleThreadExecutor()
+        );
     }
 
-    private void pullCpuTimePerUid() {
-        // No op.
+    private int pullCpuTimePerUid(int atomTag, List<StatsEvent> pulledData) {
+        mCpuUidUserSysTimeReader.readAbsolute((uid, timesUs) -> {
+            long userTimeUs = timesUs[0], systemTimeUs = timesUs[1];
+            StatsEvent e = StatsEvent.newBuilder()
+                    .setAtomId(atomTag)
+                    .writeInt(uid)
+                    .writeLong(userTimeUs)
+                    .writeLong(systemTimeUs)
+                    .build();
+            pulledData.add(e);
+        });
+        return StatsManager.PULL_SUCCESS;
     }
 
     private void registerCpuTimePerUidFreq() {
-        // No op.
+        // the throttling is 3sec, handled in
+        // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
+        int tagId = StatsLog.CPU_TIME_PER_UID_FREQ;
+        PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+                .setAdditiveFields(new int[] {4})
+                .build();
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                metadata,
+                (atomTag, data) -> pullCpuTimeperUidFreq(atomTag, data),
+                Executors.newSingleThreadExecutor()
+        );
     }
 
-    private void pullCpuTimeperUidFreq() {
-        // No op.
+    private int pullCpuTimeperUidFreq(int atomTag, List<StatsEvent> pulledData) {
+        mCpuUidFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> {
+            for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) {
+                if (cpuFreqTimeMs[freqIndex] != 0) {
+                    StatsEvent e = StatsEvent.newBuilder()
+                            .setAtomId(atomTag)
+                            .writeInt(uid)
+                            .writeInt(freqIndex)
+                            .writeLong(cpuFreqTimeMs[freqIndex])
+                            .build();
+                    pulledData.add(e);
+                }
+            }
+        });
+        return StatsManager.PULL_SUCCESS;
     }
 
     private void registerCpuActiveTime() {
-        // No op.
+        // the throttling is 3sec, handled in
+        // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
+        int tagId = StatsLog.CPU_ACTIVE_TIME;
+        PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+                .setAdditiveFields(new int[] {2})
+                .build();
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                metadata,
+                (atomTag, data) -> pullCpuActiveTime(atomTag, data),
+                Executors.newSingleThreadExecutor()
+        );
     }
 
-    private void pullCpuActiveTime() {
-        // No op.
+    private int pullCpuActiveTime(int atomTag, List<StatsEvent> pulledData) {
+        mCpuUidActiveTimeReader.readAbsolute((uid, cpuActiveTimesMs) -> {
+            StatsEvent e = StatsEvent.newBuilder()
+                    .setAtomId(atomTag)
+                    .writeInt(uid)
+                    .writeLong(cpuActiveTimesMs)
+                    .build();
+            pulledData.add(e);
+        });
+        return StatsManager.PULL_SUCCESS;
     }
 
     private void registerCpuClusterTime() {
-        // No op.
+        // the throttling is 3sec, handled in
+        // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
+        int tagId = StatsLog.CPU_CLUSTER_TIME;
+        PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+                .setAdditiveFields(new int[] {3})
+                .build();
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                metadata,
+                (atomTag, data) -> pullCpuClusterTime(atomTag, data),
+                Executors.newSingleThreadExecutor()
+        );
     }
 
-    private int pullCpuClusterTime() {
-        return 0;
+    private int pullCpuClusterTime(int atomTag, List<StatsEvent> pulledData) {
+        mCpuUidClusterTimeReader.readAbsolute((uid, cpuClusterTimesMs) -> {
+            for (int i = 0; i < cpuClusterTimesMs.length; i++) {
+                StatsEvent e = StatsEvent.newBuilder()
+                        .setAtomId(atomTag)
+                        .writeInt(uid)
+                        .writeInt(i)
+                        .writeLong(cpuClusterTimesMs[i])
+                        .build();
+                pulledData.add(e);
+            }
+        });
+        return StatsManager.PULL_SUCCESS;
     }
 
     private void registerWifiActivityInfo() {
@@ -711,11 +864,22 @@
     }
 
     private void registerSystemUptime() {
-        // No op.
+        int tagId = StatsLog.SYSTEM_UPTIME;
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                (atomTag, data) -> pullSystemUptime(atomTag, data),
+                Executors.newSingleThreadExecutor()
+        );
     }
 
-    private void pullSystemUptime() {
-        // No op.
+    private int pullSystemUptime(int atomTag, List<StatsEvent> pulledData) {
+        StatsEvent e = StatsEvent.newBuilder()
+                .setAtomId(atomTag)
+                .writeLong(SystemClock.elapsedRealtime())
+                .build();
+        pulledData.add(e);
+        return StatsManager.PULL_SUCCESS;
     }
 
     private void registerRemainingBatteryCapacity() {
@@ -990,11 +1154,30 @@
     }
 
     private void registerBuildInformation() {
-        // No op.
+        int tagId = StatsLog.BUILD_INFORMATION;
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                (atomTag, data) -> pullBuildInformation(atomTag, data),
+                BackgroundThread.getExecutor()
+        );
     }
 
-    private void pullBuildInformation() {
-        // No op.
+    private int pullBuildInformation(int atomTag, List<StatsEvent> pulledData) {
+        StatsEvent e = StatsEvent.newBuilder()
+                .setAtomId(atomTag)
+                .writeString(Build.FINGERPRINT)
+                .writeString(Build.BRAND)
+                .writeString(Build.PRODUCT)
+                .writeString(Build.DEVICE)
+                .writeString(Build.VERSION.RELEASE)
+                .writeString(Build.ID)
+                .writeString(Build.VERSION.INCREMENTAL)
+                .writeString(Build.TYPE)
+                .writeString(Build.TAGS)
+                .build();
+        pulledData.add(e);
+        return StatsManager.PULL_SUCCESS;
     }
 
     private void registerRoleHolder() {
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index c964795..468b806 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -61,10 +61,10 @@
         /** Acquire a suitable wake lock. Must be followed by {@link #releaseWakeLock()} */
         void acquireWakeLock();
 
-        /** Returns the elapsedRealtimeMillis clock value. The WakeLock must be held. */
+        /** Returns the elapsedRealtimeMillis clock value. */
         long elapsedRealtimeMillis();
 
-        /** Returns the system clock value. The WakeLock must be held. */
+        /** Returns the system clock value. */
         long systemClockMillis();
 
         /** Sets the device system clock. The WakeLock must be held. */
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
index 9b89d94..19484db 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
@@ -88,13 +88,11 @@
 
     @Override
     public long elapsedRealtimeMillis() {
-        checkWakeLockHeld();
         return SystemClock.elapsedRealtime();
     }
 
     @Override
     public long systemClockMillis() {
-        checkWakeLockHeld();
         return System.currentTimeMillis();
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 320be2d..c9a702f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1633,12 +1633,7 @@
         requestedVrComponent = (aInfo.requestedVrComponent == null) ?
                 null : ComponentName.unflattenFromString(aInfo.requestedVrComponent);
 
-        lockTaskLaunchMode = aInfo.lockTaskLaunchMode;
-        if (info.applicationInfo.isPrivilegedApp()
-                && (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
-                || lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
-            lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
-        }
+        lockTaskLaunchMode = getLockTaskLaunchMode(aInfo, options);
 
         if (options != null) {
             pendingOptions = options;
@@ -1646,15 +1641,27 @@
             if (usageReport != null) {
                 appTimeTracker = new AppTimeTracker(usageReport);
             }
-            final boolean useLockTask = pendingOptions.getLockTaskMode();
-            if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
-                lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
-            }
             // Gets launch display id from options. It returns INVALID_DISPLAY if not set.
             mHandoverLaunchDisplayId = options.getLaunchDisplayId();
         }
     }
 
+    static int getLockTaskLaunchMode(ActivityInfo aInfo, @Nullable ActivityOptions options) {
+        int lockTaskLaunchMode = aInfo.lockTaskLaunchMode;
+        if (aInfo.applicationInfo.isPrivilegedApp()
+                && (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
+                || lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
+            lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
+        }
+        if (options != null) {
+            final boolean useLockTask = options.getLockTaskMode();
+            if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
+                lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
+            }
+        }
+        return lockTaskLaunchMode;
+    }
+
     @Override
     ActivityRecord asActivityRecord() {
         // I am an activity record!
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index df97caa..d61d29d 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -52,6 +52,7 @@
 import android.os.UserManager;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.BlockedAppActivity;
 import com.android.internal.app.HarmfulAppWarningActivity;
 import com.android.internal.app.SuspendedAppActivity;
 import com.android.internal.app.UnlaunchableAppActivity;
@@ -166,6 +167,9 @@
             // no user action can undo this.
             return true;
         }
+        if (interceptLockTaskModeViolationPackageIfNeeded()) {
+            return true;
+        }
         if (interceptHarmfulAppIfNeeded()) {
             // If the app has a "harmful app" warning associated with it, we should ask to uninstall
             // before issuing the work challenge.
@@ -262,6 +266,25 @@
         return true;
     }
 
+    private boolean interceptLockTaskModeViolationPackageIfNeeded() {
+        if (mAInfo == null || mAInfo.applicationInfo == null) {
+            return false;
+        }
+        LockTaskController controller = mService.getLockTaskController();
+        String packageName = mAInfo.applicationInfo.packageName;
+        int lockTaskLaunchMode = ActivityRecord.getLockTaskLaunchMode(mAInfo, mActivityOptions);
+        if (controller.isActivityAllowed(mUserId, packageName, lockTaskLaunchMode)) {
+            return false;
+        }
+        mIntent = BlockedAppActivity.createIntent(mUserId, mAInfo.applicationInfo.packageName);
+        mCallingPid = mRealCallingPid;
+        mCallingUid = mRealCallingUid;
+        mResolvedType = null;
+        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid);
+        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
+        return true;
+    }
+
     private boolean interceptWorkProfileChallengeIfNeeded() {
         final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mAInfo, mUserId);
         if (interceptingIntent == null) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index ded603c..3092a08 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -28,7 +28,7 @@
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
 import static android.Manifest.permission.STOP_APP_SWITCHES;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
@@ -1436,7 +1436,7 @@
 
     int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
         return mAmInternal.handleIncomingUser(callingPid, callingUid, userId, false /* allowAll */,
-                ALLOW_FULL_ONLY, name, null /* callerPackage */);
+                ALLOW_NON_FULL, name, null /* callerPackage */);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 02413bb..33b0453 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -23,6 +23,8 @@
 import static android.content.Context.DEVICE_POLICY_SERVICE;
 import static android.content.Context.STATUS_BAR_SERVICE;
 import static android.content.Intent.ACTION_CALL_EMERGENCY;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
 import static android.os.UserHandle.USER_ALL;
 import static android.os.UserHandle.USER_CURRENT;
 import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
@@ -339,6 +341,20 @@
                 & DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD) != 0;
     }
 
+    boolean isActivityAllowed(int userId, String packageName, int lockTaskLaunchMode) {
+        if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED) {
+            return true;
+        }
+        switch (lockTaskLaunchMode) {
+            case LOCK_TASK_LAUNCH_MODE_ALWAYS:
+                return true;
+            case LOCK_TASK_LAUNCH_MODE_NEVER:
+                return false;
+            default:
+        }
+        return isPackageWhitelisted(userId, packageName);
+    }
+
     private boolean isEmergencyCallTask(Task task) {
         final Intent intent = task.intent;
         if (intent == null) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 74fdba1..e3b593e9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4602,13 +4602,13 @@
 
                     if (newFocus != null) {
                         ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Gaining focus: %s", newFocus);
-                        newFocus.reportFocusChangedSerialized(true, mInTouchMode);
+                        newFocus.reportFocusChangedSerialized(true);
                         notifyFocusChanged();
                     }
 
                     if (lastFocus != null) {
                         ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Losing focus: %s", lastFocus);
-                        lastFocus.reportFocusChangedSerialized(false, mInTouchMode);
+                        lastFocus.reportFocusChangedSerialized(false);
                     }
                     break;
                 }
@@ -4626,7 +4626,7 @@
                     for (int i = 0; i < N; i++) {
                         ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Losing delayed focus: %s",
                                 losers.get(i));
-                        losers.get(i).reportFocusChangedSerialized(false, mInTouchMode);
+                        losers.get(i).reportFocusChangedSerialized(false);
                     }
                     break;
                 }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ba40f62..c2eb0e4 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3339,11 +3339,7 @@
      * Report a focus change.  Must be called with no locks held, and consistently
      * from the same serialized thread (such as dispatched from a handler).
      */
-    void reportFocusChangedSerialized(boolean focused, boolean inTouchMode) {
-        try {
-            mClient.windowFocusChanged(focused, inTouchMode);
-        } catch (RemoteException e) {
-        }
+    void reportFocusChangedSerialized(boolean focused) {
         if (mFocusCallbacks != null) {
             final int N = mFocusCallbacks.beginBroadcast();
             for (int i=0; i<N; i++) {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 03969b0..77d814e 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -51,7 +51,7 @@
         "com_android_server_VibratorService.cpp",
         "com_android_server_PersistentDataBlockService.cpp",
         "com_android_server_GraphicsStatsService.cpp",
-        "com_android_server_am_AppCompactor.cpp",
+        "com_android_server_am_CachedAppOptimizer.cpp",
         "com_android_server_am_LowMemDetector.cpp",
         "com_android_server_incremental_IncrementalManagerService.cpp",
         "onload.cpp",
diff --git a/services/core/jni/com_android_server_am_AppCompactor.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
similarity index 89%
rename from services/core/jni/com_android_server_am_AppCompactor.cpp
rename to services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index de6aa8b..6a6da0e 100644
--- a/services/core/jni/com_android_server_am_AppCompactor.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "AppCompactor"
+#define LOG_TAG "CachedAppOptimizer"
 //#define LOG_NDEBUG 0
 
 #include <dirent.h>
@@ -42,7 +42,7 @@
 // or potentially some mainline modules. The only process that should definitely
 // not be compacted is system_server, since compacting system_server around the
 // time of BOOT_COMPLETE could result in perceptible issues.
-static void com_android_server_am_AppCompactor_compactSystem(JNIEnv *, jobject) {
+static void com_android_server_am_CachedAppOptimizer_compactSystem(JNIEnv *, jobject) {
     std::unique_ptr<DIR, decltype(&closedir)> proc(opendir("/proc"), closedir);
     struct dirent* current;
     while ((current = readdir(proc.get()))) {
@@ -76,12 +76,12 @@
 
 static const JNINativeMethod sMethods[] = {
     /* name, signature, funcPtr */
-    {"compactSystem", "()V", (void*)com_android_server_am_AppCompactor_compactSystem},
+    {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
 };
 
-int register_android_server_am_AppCompactor(JNIEnv* env)
+int register_android_server_am_CachedAppOptimizer(JNIEnv* env)
 {
-    return jniRegisterNativeMethods(env, "com/android/server/am/AppCompactor",
+    return jniRegisterNativeMethods(env, "com/android/server/am/CachedAppOptimizer",
                                     sMethods, NELEM(sMethods));
 }
 
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index c0a6e4e..19fa062 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -54,7 +54,7 @@
 int register_android_server_net_NetworkStatsFactory(JNIEnv* env);
 int register_android_server_net_NetworkStatsService(JNIEnv* env);
 int register_android_server_security_VerityUtils(JNIEnv* env);
-int register_android_server_am_AppCompactor(JNIEnv* env);
+int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
 int register_android_server_am_LowMemDetector(JNIEnv* env);
 int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
         JNIEnv* env);
@@ -106,7 +106,7 @@
     register_android_server_net_NetworkStatsFactory(env);
     register_android_server_net_NetworkStatsService(env);
     register_android_server_security_VerityUtils(env);
-    register_android_server_am_AppCompactor(env);
+    register_android_server_am_CachedAppOptimizer(env);
     register_android_server_am_LowMemDetector(env);
     register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
             env);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b6a8ca4..ea2385f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -213,6 +213,8 @@
             "com.android.server.print.PrintManagerService";
     private static final String COMPANION_DEVICE_MANAGER_SERVICE_CLASS =
             "com.android.server.companion.CompanionDeviceManagerService";
+    private static final String STATS_COMPANION_APEX_PATH =
+            "/apex/com.android.os.statsd/javalib/service-statsd.jar";
     private static final String STATS_COMPANION_LIFECYCLE_CLASS =
             "com.android.server.stats.StatsCompanion$Lifecycle";
     private static final String STATS_PULL_ATOM_SERVICE_CLASS =
@@ -1980,7 +1982,8 @@
 
         // Statsd helper
         t.traceBegin("StartStatsCompanion");
-        mSystemServiceManager.startService(STATS_COMPANION_LIFECYCLE_CLASS);
+        mSystemServiceManager.startServiceFromJar(
+                STATS_COMPANION_LIFECYCLE_CLASS, STATS_COMPANION_APEX_PATH);
         t.traceEnd();
 
         // Statsd pulled atoms
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 9c7cfc1..cf84bdf 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -23,7 +23,6 @@
     name: "services-tethering-shared-srcs",
     srcs: [
         ":framework-annotations",
-        "java/android/net/util/NetdService.java",
         "java/android/net/util/NetworkConstants.java",
     ],
     visibility: ["//frameworks/base/packages/Tethering"],
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java
deleted file mode 100644
index 48e459f..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java
+++ /dev/null
@@ -1,680 +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.server.am;
-
-import static com.android.server.am.ActivityManagerService.Injector;
-import static com.android.server.am.AppCompactor.compactActionIntToString;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Process;
-import android.platform.test.annotations.Presubmit;
-import android.provider.DeviceConfig;
-import android.text.TextUtils;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import com.android.server.ServiceThread;
-import com.android.server.appop.AppOpsService;
-import com.android.server.testables.TestableDeviceConfig;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-import java.io.File;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Tests for {@link AppCompactor}.
- *
- * Build/Install/Run:
- * atest FrameworksMockingServicesTests:AppCompactorTest
- */
-@Presubmit
-@RunWith(MockitoJUnitRunner.class)
-public final class AppCompactorTest {
-
-    private ServiceThread mThread;
-
-    @Mock
-    private AppOpsService mAppOpsService;
-    private AppCompactor mCompactorUnderTest;
-    private HandlerThread mHandlerThread;
-    private Handler mHandler;
-    private CountDownLatch mCountDown;
-
-    @Rule
-    public TestableDeviceConfig.TestableDeviceConfigRule
-            mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
-
-    @Before
-    public void setUp() {
-        mHandlerThread = new HandlerThread("");
-        mHandlerThread.start();
-        mHandler = new Handler(mHandlerThread.getLooper());
-
-        mThread = new ServiceThread("TestServiceThread", Process.THREAD_PRIORITY_DEFAULT,
-                true /* allowIo */);
-        mThread.start();
-
-        ActivityManagerService ams = new ActivityManagerService(
-                new TestInjector(InstrumentationRegistry.getInstrumentation().getContext()),
-                mThread);
-        mCompactorUnderTest = new AppCompactor(ams,
-                new AppCompactor.PropertyChangedCallbackForTest() {
-                    @Override
-                    public void onPropertyChanged() {
-                        if (mCountDown != null) {
-                            mCountDown.countDown();
-                        }
-                    }
-                });
-    }
-
-    @After
-    public void tearDown() {
-        mHandlerThread.quit();
-        mThread.quit();
-        mCountDown = null;
-    }
-
-    @Test
-    public void init_setsDefaults() {
-        mCompactorUnderTest.init();
-        assertThat(mCompactorUnderTest.useCompaction()).isEqualTo(
-                AppCompactor.DEFAULT_USE_COMPACTION);
-        assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
-                compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1));
-        assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
-                compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2));
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
-        assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
-        assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
-        assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(
-                AppCompactor.DEFAULT_STATSD_SAMPLE_RATE);
-        assertThat(mCompactorUnderTest.mFullAnonRssThrottleKb).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
-        assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_5);
-        assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_6);
-        assertThat(mCompactorUnderTest.mFullAnonRssThrottleKb).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
-        assertThat(mCompactorUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
-
-        Set<Integer> expected = new HashSet<>();
-        for (String s : TextUtils.split(AppCompactor.DEFAULT_COMPACT_PROC_STATE_THROTTLE, ",")) {
-            expected.add(Integer.parseInt(s));
-        }
-        assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactlyElementsIn(expected);
-    }
-
-    @Test
-    public void init_withDeviceConfigSetsParameters() {
-        // When the DeviceConfig already has a flag value stored (note this test will need to
-        // change if the default value changes from false).
-        assertThat(AppCompactor.DEFAULT_USE_COMPACTION).isFalse();
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_USE_COMPACTION, "true", false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_ACTION_1,
-                Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_ACTION_2,
-                Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_THROTTLE_1,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_THROTTLE_2,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_THROTTLE_3,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_THROTTLE_4,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_THROTTLE_5,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_5 + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_THROTTLE_6,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_6 + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE,
-                Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_FULL_RSS_THROTTLE_KB,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false);
-
-        // Then calling init will read and set that flag.
-        mCompactorUnderTest.init();
-        assertThat(mCompactorUnderTest.useCompaction()).isTrue();
-        assertThat(mCompactorUnderTest.mCompactionThread.isAlive()).isTrue();
-
-        assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
-                compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1));
-        assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
-                compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1));
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1);
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1);
-        assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1);
-        assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1);
-        assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_5 + 1);
-        assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_6 + 1);
-        assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(
-                AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
-        assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_5 + 1);
-        assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_6 + 1);
-        assertThat(mCompactorUnderTest.mFullAnonRssThrottleKb).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1);
-        assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactly(1, 2, 3);
-    }
-
-    @Test
-    public void useCompaction_listensToDeviceConfigChanges() throws InterruptedException {
-        assertThat(mCompactorUnderTest.useCompaction()).isEqualTo(
-                AppCompactor.DEFAULT_USE_COMPACTION);
-        // When we call init and change some the flag value...
-        mCompactorUnderTest.init();
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_USE_COMPACTION, "true", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
-        // Then that new flag value is updated in the implementation.
-        assertThat(mCompactorUnderTest.useCompaction()).isTrue();
-        assertThat(mCompactorUnderTest.mCompactionThread.isAlive()).isTrue();
-
-        // And again, setting the flag the other way.
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_USE_COMPACTION, "false", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-        assertThat(mCompactorUnderTest.useCompaction()).isFalse();
-    }
-
-    @Test
-    public void useCompaction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
-        assertThat(mCompactorUnderTest.useCompaction()).isEqualTo(
-                AppCompactor.DEFAULT_USE_COMPACTION);
-        mCompactorUnderTest.init();
-
-        // When we push an invalid flag value...
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_USE_COMPACTION, "foobar", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
-        // Then we set the default.
-        assertThat(mCompactorUnderTest.useCompaction()).isEqualTo(
-                AppCompactor.DEFAULT_USE_COMPACTION);
-    }
-
-    @Test
-    public void compactAction_listensToDeviceConfigChanges() throws InterruptedException {
-        mCompactorUnderTest.init();
-
-        // When we override new values for the compaction action with reasonable values...
-
-        // There are four possible values for compactAction[Some|Full].
-        for (int i = 1; i < 5; i++) {
-            mCountDown = new CountDownLatch(2);
-            int expectedSome = (AppCompactor.DEFAULT_COMPACT_ACTION_1 + i) % 4 + 1;
-            DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                    AppCompactor.KEY_COMPACT_ACTION_1, Integer.toString(expectedSome), false);
-            int expectedFull = (AppCompactor.DEFAULT_COMPACT_ACTION_2 + i) % 4 + 1;
-            DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                    AppCompactor.KEY_COMPACT_ACTION_2, Integer.toString(expectedFull), false);
-            assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
-            // Then the updates are reflected in the flags.
-            assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
-                    compactActionIntToString(expectedSome));
-            assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
-                    compactActionIntToString(expectedFull));
-        }
-    }
-
-    @Test
-    public void compactAction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
-        mCompactorUnderTest.init();
-
-        // When we override new values for the compaction action with bad values ...
-        mCountDown = new CountDownLatch(2);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_ACTION_1, "foo", false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_ACTION_2, "foo", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
-        // Then the default values are reflected in the flag
-        assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
-                compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1));
-        assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
-                compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2));
-
-        mCountDown = new CountDownLatch(2);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_ACTION_1, "", false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_ACTION_2, "", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
-        assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
-                compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1));
-        assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
-                compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2));
-    }
-
-    @Test
-    public void compactThrottle_listensToDeviceConfigChanges() throws InterruptedException {
-        mCompactorUnderTest.init();
-
-        // When we override new reasonable throttle values after init...
-        mCountDown = new CountDownLatch(6);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_THROTTLE_1,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_THROTTLE_2,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_THROTTLE_3,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_THROTTLE_4,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_THROTTLE_5,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_5 + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_THROTTLE_6,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_6 + 1), false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
-        // Then those flags values are reflected in the compactor.
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1);
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1);
-        assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1);
-        assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1);
-        assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_5 + 1);
-        assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_6 + 1);
-    }
-
-    @Test
-    public void compactThrottle_listensToDeviceConfigChangesBadValues()
-            throws InterruptedException {
-        mCompactorUnderTest.init();
-
-        // When one of the throttles is overridden with a bad value...
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_THROTTLE_1, "foo", false);
-        // Then all the throttles have the defaults set.
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
-        assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
-        assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
-        assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_5);
-        assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_6);
-
-        // Repeat for each of the throttle keys.
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_THROTTLE_2, "foo", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
-        assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
-        assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
-        assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_5);
-        assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_6);
-
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_THROTTLE_3, "foo", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
-        assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
-        assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
-        assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_5);
-        assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_6);
-
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_THROTTLE_4, "foo", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
-        assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
-        assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
-        assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_5);
-        assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_6);
-
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_THROTTLE_5, "foo", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
-        assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
-        assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
-        assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_5);
-        assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_6);
-
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_THROTTLE_6, "foo", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
-        assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
-        assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
-        assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_5);
-        assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_THROTTLE_6);
-    }
-
-    @Test
-    public void statsdSampleRate_listensToDeviceConfigChanges() throws InterruptedException {
-        mCompactorUnderTest.init();
-
-        // When we override mStatsdSampleRate with a reasonable value ...
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE,
-                Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
-        // Then that override is reflected in the compactor.
-        assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(
-                AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
-    }
-
-    @Test
-    public void statsdSampleRate_listensToDeviceConfigChangesBadValues()
-            throws InterruptedException {
-        mCompactorUnderTest.init();
-
-        // When we override mStatsdSampleRate with an unreasonable value ...
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE, "foo", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
-        // Then that override is reflected in the compactor.
-        assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(
-                AppCompactor.DEFAULT_STATSD_SAMPLE_RATE);
-    }
-
-    @Test
-    public void statsdSampleRate_listensToDeviceConfigChangesOutOfRangeValues()
-            throws InterruptedException {
-        mCompactorUnderTest.init();
-
-        // When we override mStatsdSampleRate with an value outside of [0..1]...
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE,
-                Float.toString(-1.0f), false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
-        // Then the values is capped in the range.
-        assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(0.0f);
-
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE,
-                Float.toString(1.01f), false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
-        // Then the values is capped in the range.
-        assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(1.0f);
-    }
-
-    @Test
-    public void fullCompactionRssThrottleKb_listensToDeviceConfigChanges()
-            throws InterruptedException {
-        mCompactorUnderTest.init();
-
-        // When we override mStatsdSampleRate with a reasonable value ...
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_FULL_RSS_THROTTLE_KB,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1), false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
-        // Then that override is reflected in the compactor.
-        assertThat(mCompactorUnderTest.mFullAnonRssThrottleKb).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1);
-    }
-
-    @Test
-    public void fullCompactionRssThrottleKb_listensToDeviceConfigChangesBadValues()
-            throws InterruptedException {
-        mCompactorUnderTest.init();
-
-        // When we override mStatsdSampleRate with an unreasonable value ...
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_FULL_RSS_THROTTLE_KB, "foo", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
-        // Then that override is reflected in the compactor.
-        assertThat(mCompactorUnderTest.mFullAnonRssThrottleKb).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
-
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_FULL_RSS_THROTTLE_KB, "-100", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
-        // Then that override is reflected in the compactor.
-        assertThat(mCompactorUnderTest.mFullAnonRssThrottleKb).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
-    }
-
-    @Test
-    public void fullCompactionDeltaRssThrottleKb_listensToDeviceConfigChanges()
-            throws InterruptedException {
-        mCompactorUnderTest.init();
-
-        // When we override mStatsdSampleRate with a reasonable value ...
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1), false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
-        // Then that override is reflected in the compactor.
-        assertThat(mCompactorUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1);
-    }
-
-    @Test
-    public void fullCompactionDeltaRssThrottleKb_listensToDeviceConfigChangesBadValues()
-            throws InterruptedException {
-        mCompactorUnderTest.init();
-
-        // When we override mStatsdSampleRate with an unreasonable value ...
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, "foo", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
-        // Then that override is reflected in the compactor.
-        assertThat(mCompactorUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
-
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, "-100", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
-        // Then that override is reflected in the compactor.
-        assertThat(mCompactorUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
-                AppCompactor.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
-    }
-
-    @Test
-    public void procStateThrottle_listensToDeviceConfigChanges()
-            throws InterruptedException {
-        mCompactorUnderTest.init();
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-        assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactly(1, 2, 3);
-
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, "", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-        assertThat(mCompactorUnderTest.mProcStateThrottle).isEmpty();
-    }
-
-    @Test
-    public void procStateThrottle_listensToDeviceConfigChangesBadValues()
-            throws InterruptedException {
-        mCompactorUnderTest.init();
-
-        Set<Integer> expected = new HashSet<>();
-        for (String s : TextUtils.split(AppCompactor.DEFAULT_COMPACT_PROC_STATE_THROTTLE, ",")) {
-            expected.add(Integer.parseInt(s));
-        }
-
-        // Not numbers
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, "foo", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-        assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactlyElementsIn(expected);
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, "1,foo", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-        assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactlyElementsIn(expected);
-
-        // Empty splits
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, ",", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-        assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactlyElementsIn(expected);
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, ",,3", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-        assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactlyElementsIn(expected);
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, "1,,3", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-        assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactlyElementsIn(expected);
-    }
-
-    private class TestInjector extends Injector {
-
-        TestInjector(Context context) {
-            super(context);
-        }
-
-        @Override
-        public AppOpsService getAppOpsService(File file, Handler handler) {
-            return mAppOpsService;
-        }
-
-        @Override
-        public Handler getUiHandler(ActivityManagerService service) {
-            return mHandler;
-        }
-    }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
new file mode 100644
index 0000000..f037692
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -0,0 +1,692 @@
+/*
+ * 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.server.am;
+
+import static com.android.server.am.ActivityManagerService.Injector;
+import static com.android.server.am.CachedAppOptimizer.compactActionIntToString;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
+import android.text.TextUtils;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.ServiceThread;
+import com.android.server.appop.AppOpsService;
+import com.android.server.testables.TestableDeviceConfig;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for {@link CachedAppOptimizer}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksMockingServicesTests:CachedAppOptimizerTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner.class)
+public final class CachedAppOptimizerTest {
+
+    private ServiceThread mThread;
+
+    @Mock
+    private AppOpsService mAppOpsService;
+    private CachedAppOptimizer mCachedAppOptimizerUnderTest;
+    private HandlerThread mHandlerThread;
+    private Handler mHandler;
+    private CountDownLatch mCountDown;
+
+    @Rule
+    public TestableDeviceConfig.TestableDeviceConfigRule
+            mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
+
+    @Before
+    public void setUp() {
+        mHandlerThread = new HandlerThread("");
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+
+        mThread = new ServiceThread("TestServiceThread", Process.THREAD_PRIORITY_DEFAULT,
+                true /* allowIo */);
+        mThread.start();
+
+        ActivityManagerService ams = new ActivityManagerService(
+                new TestInjector(InstrumentationRegistry.getInstrumentation().getContext()),
+                mThread);
+        mCachedAppOptimizerUnderTest = new CachedAppOptimizer(ams,
+                new CachedAppOptimizer.PropertyChangedCallbackForTest() {
+                    @Override
+                    public void onPropertyChanged() {
+                        if (mCountDown != null) {
+                            mCountDown.countDown();
+                        }
+                    }
+                });
+    }
+
+    @After
+    public void tearDown() {
+        mHandlerThread.quit();
+        mThread.quit();
+        mCountDown = null;
+    }
+
+    @Test
+    public void init_setsDefaults() {
+        mCachedAppOptimizerUnderTest.init();
+        assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
+                CachedAppOptimizer.DEFAULT_USE_COMPACTION);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
+                compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
+        assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
+                compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
+        assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(
+                CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
+        assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
+        assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
+        assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
+
+        Set<Integer> expected = new HashSet<>();
+        for (String s : TextUtils.split(
+                CachedAppOptimizer.DEFAULT_COMPACT_PROC_STATE_THROTTLE, ",")) {
+            expected.add(Integer.parseInt(s));
+        }
+        assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
+                .containsExactlyElementsIn(expected);
+    }
+
+    @Test
+    public void init_withDeviceConfigSetsParameters() {
+        // When the DeviceConfig already has a flag value stored (note this test will need to
+        // change if the default value changes from false).
+        assertThat(CachedAppOptimizer.DEFAULT_USE_COMPACTION).isFalse();
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_USE_COMPACTION, "true", false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_ACTION_1,
+                Integer.toString((CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_ACTION_2,
+                Integer.toString((CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_THROTTLE_1,
+                Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_THROTTLE_2,
+                Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_THROTTLE_3,
+                Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_THROTTLE_4,
+                Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_THROTTLE_5,
+                Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_THROTTLE_6,
+                Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE,
+                Float.toString(CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB,
+                Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB,
+                Long.toString(
+                        CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false);
+
+        // Then calling init will read and set that flag.
+        mCachedAppOptimizerUnderTest.init();
+        assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isTrue();
+        assertThat(mCachedAppOptimizerUnderTest.mCachedAppOptimizerThread.isAlive()).isTrue();
+
+        assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
+                compactActionIntToString(
+                        (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1));
+        assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
+                compactActionIntToString(
+                        (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1));
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2 + 1);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3 + 1);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4 + 1);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1);
+        assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(
+                CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1);
+        assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1);
+        assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle).containsExactly(1, 2, 3);
+    }
+
+    @Test
+    public void useCompaction_listensToDeviceConfigChanges() throws InterruptedException {
+        assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
+                CachedAppOptimizer.DEFAULT_USE_COMPACTION);
+        // When we call init and change some the flag value...
+        mCachedAppOptimizerUnderTest.init();
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_USE_COMPACTION, "true", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then that new flag value is updated in the implementation.
+        assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isTrue();
+        assertThat(mCachedAppOptimizerUnderTest.mCachedAppOptimizerThread.isAlive()).isTrue();
+
+        // And again, setting the flag the other way.
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_USE_COMPACTION, "false", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isFalse();
+    }
+
+    @Test
+    public void useCompaction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
+        assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
+                CachedAppOptimizer.DEFAULT_USE_COMPACTION);
+        mCachedAppOptimizerUnderTest.init();
+
+        // When we push an invalid flag value...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_USE_COMPACTION, "foobar", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then we set the default.
+        assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
+                CachedAppOptimizer.DEFAULT_USE_COMPACTION);
+    }
+
+    @Test
+    public void compactAction_listensToDeviceConfigChanges() throws InterruptedException {
+        mCachedAppOptimizerUnderTest.init();
+
+        // When we override new values for the compaction action with reasonable values...
+
+        // There are four possible values for compactAction[Some|Full].
+        for (int i = 1; i < 5; i++) {
+            mCountDown = new CountDownLatch(2);
+            int expectedSome = (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1 + i) % 4 + 1;
+            DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    CachedAppOptimizer.KEY_COMPACT_ACTION_1, Integer.toString(expectedSome), false);
+            int expectedFull = (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2 + i) % 4 + 1;
+            DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    CachedAppOptimizer.KEY_COMPACT_ACTION_2, Integer.toString(expectedFull), false);
+            assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+            // Then the updates are reflected in the flags.
+            assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
+                    compactActionIntToString(expectedSome));
+            assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
+                    compactActionIntToString(expectedFull));
+        }
+    }
+
+    @Test
+    public void compactAction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
+        mCachedAppOptimizerUnderTest.init();
+
+        // When we override new values for the compaction action with bad values ...
+        mCountDown = new CountDownLatch(2);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_ACTION_1, "foo", false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_ACTION_2, "foo", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then the default values are reflected in the flag
+        assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
+                compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
+        assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
+                compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+
+        mCountDown = new CountDownLatch(2);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_ACTION_1, "", false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_ACTION_2, "", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
+                compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
+        assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
+                compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+    }
+
+    @Test
+    public void compactThrottle_listensToDeviceConfigChanges() throws InterruptedException {
+        mCachedAppOptimizerUnderTest.init();
+
+        // When we override new reasonable throttle values after init...
+        mCountDown = new CountDownLatch(6);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_THROTTLE_1,
+                Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_THROTTLE_2,
+                Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_THROTTLE_3,
+                Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_THROTTLE_4,
+                Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_THROTTLE_5,
+                Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_THROTTLE_6,
+                Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1), false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then those flags values are reflected in the compactor.
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2 + 1);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3 + 1);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4 + 1);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1);
+    }
+
+    @Test
+    public void compactThrottle_listensToDeviceConfigChangesBadValues()
+            throws InterruptedException {
+        mCachedAppOptimizerUnderTest.init();
+
+        // When one of the throttles is overridden with a bad value...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_THROTTLE_1, "foo", false);
+        // Then all the throttles have the defaults set.
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
+
+        // Repeat for each of the throttle keys.
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_THROTTLE_2, "foo", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
+
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_THROTTLE_3, "foo", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
+
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_THROTTLE_4, "foo", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
+
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_THROTTLE_5, "foo", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
+
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_THROTTLE_6, "foo", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
+    }
+
+    @Test
+    public void statsdSampleRate_listensToDeviceConfigChanges() throws InterruptedException {
+        mCachedAppOptimizerUnderTest.init();
+
+        // When we override mStatsdSampleRate with a reasonable value ...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE,
+                Float.toString(CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then that override is reflected in the compactor.
+        assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(
+                CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
+    }
+
+    @Test
+    public void statsdSampleRate_listensToDeviceConfigChangesBadValues()
+            throws InterruptedException {
+        mCachedAppOptimizerUnderTest.init();
+
+        // When we override mStatsdSampleRate with an unreasonable value ...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE, "foo", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then that override is reflected in the compactor.
+        assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(
+                CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
+    }
+
+    @Test
+    public void statsdSampleRate_listensToDeviceConfigChangesOutOfRangeValues()
+            throws InterruptedException {
+        mCachedAppOptimizerUnderTest.init();
+
+        // When we override mStatsdSampleRate with an value outside of [0..1]...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE,
+                Float.toString(-1.0f), false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then the values is capped in the range.
+        assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(0.0f);
+
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE,
+                Float.toString(1.01f), false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then the values is capped in the range.
+        assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(1.0f);
+    }
+
+    @Test
+    public void fullCompactionRssThrottleKb_listensToDeviceConfigChanges()
+            throws InterruptedException {
+        mCachedAppOptimizerUnderTest.init();
+
+        // When we override mStatsdSampleRate with a reasonable value ...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB,
+                Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1), false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then that override is reflected in the compactor.
+        assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1);
+    }
+
+    @Test
+    public void fullCompactionRssThrottleKb_listensToDeviceConfigChangesBadValues()
+            throws InterruptedException {
+        mCachedAppOptimizerUnderTest.init();
+
+        // When we override mStatsdSampleRate with an unreasonable value ...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB, "foo", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then that override is reflected in the compactor.
+        assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
+
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB, "-100", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then that override is reflected in the compactor.
+        assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
+    }
+
+    @Test
+    public void fullCompactionDeltaRssThrottleKb_listensToDeviceConfigChanges()
+            throws InterruptedException {
+        mCachedAppOptimizerUnderTest.init();
+
+        // When we override mStatsdSampleRate with a reasonable value ...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB,
+                Long.toString(
+                        CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1), false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then that override is reflected in the compactor.
+        assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1);
+    }
+
+    @Test
+    public void fullCompactionDeltaRssThrottleKb_listensToDeviceConfigChangesBadValues()
+            throws InterruptedException {
+        mCachedAppOptimizerUnderTest.init();
+
+        // When we override mStatsdSampleRate with an unreasonable value ...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, "foo", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then that override is reflected in the compactor.
+        assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
+
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, "-100", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then that override is reflected in the compactor.
+        assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
+                CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
+    }
+
+    @Test
+    public void procStateThrottle_listensToDeviceConfigChanges()
+            throws InterruptedException {
+        mCachedAppOptimizerUnderTest.init();
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle).containsExactly(1, 2, 3);
+
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle).isEmpty();
+    }
+
+    @Test
+    public void procStateThrottle_listensToDeviceConfigChangesBadValues()
+            throws InterruptedException {
+        mCachedAppOptimizerUnderTest.init();
+
+        Set<Integer> expected = new HashSet<>();
+        for (String s : TextUtils.split(
+                CachedAppOptimizer.DEFAULT_COMPACT_PROC_STATE_THROTTLE, ",")) {
+            expected.add(Integer.parseInt(s));
+        }
+
+        // Not numbers
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "foo", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
+                .containsExactlyElementsIn(expected);
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,foo", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
+                .containsExactlyElementsIn(expected);
+
+        // Empty splits
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, ",", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
+                .containsExactlyElementsIn(expected);
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, ",,3", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
+                .containsExactlyElementsIn(expected);
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,,3", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
+                .containsExactlyElementsIn(expected);
+    }
+
+    private class TestInjector extends Injector {
+
+        TestInjector(Context context) {
+            super(context);
+        }
+
+        @Override
+        public AppOpsService getAppOpsService(File file, Handler handler) {
+            return mAppOpsService;
+        }
+
+        @Override
+        public Handler getUiHandler(ActivityManagerService service) {
+            return mHandler;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index c223f13..e1e9b7e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -75,6 +75,7 @@
 import android.os.IPowerManager;
 import android.os.PowerManager;
 import android.os.Process;
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.testing.DexmakerShareClassLoaderRule;
 import android.view.Display;
@@ -693,6 +694,18 @@
         assertThat(result, is(false));
     }
 
+    @Test
+    public void takeScreenshot_returnNull() {
+        // no canTakeScreenshot, should return null.
+        when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(false);
+        assertThat(mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY), is(nullValue()));
+
+        // no checkAccessibilityAccess, should return null.
+        when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(true);
+        when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(false);
+        assertThat(mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY), is(nullValue()));
+    }
+
     private void updateServiceInfo(AccessibilityServiceInfo serviceInfo, int eventType,
             int feedbackType, int flags, String[] packageNames, int notificationTimeout) {
         serviceInfo.eventTypes = eventType;
@@ -832,5 +845,8 @@
 
         @Override
         public void onFingerprintGesture(int gesture) {}
+
+        @Override
+        public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {}
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
index 04ac7fe..1504097 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
@@ -346,6 +346,14 @@
     }
 
     @Test
+    public void canTakeScreenshot_hasCapability_returnTrue() {
+        when(mMockA11yServiceConnection.getCapabilities())
+                .thenReturn(AccessibilityServiceInfo.CAPABILITY_CAN_TAKE_SCREENSHOT);
+
+        assertTrue(mA11ySecurityPolicy.canTakeScreenshotLocked(mMockA11yServiceConnection));
+    }
+
+    @Test
     public void resolveProfileParent_userIdIsCurrentUser_returnCurrentUser() {
         final int currentUserId = 10;
         final int userId = currentUserId;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index 96d9c47..ac5169c 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -113,7 +113,6 @@
         mUserState.mAccessibilityButtonTargets.add(COMPONENT_NAME.flattenToString());
         mUserState.setTouchExplorationEnabledLocked(true);
         mUserState.setDisplayMagnificationEnabledLocked(true);
-        mUserState.setNavBarMagnificationEnabledLocked(true);
         mUserState.setAutoclickEnabledLocked(true);
         mUserState.setUserNonInteractiveUiTimeoutLocked(30);
         mUserState.setUserInteractiveUiTimeoutLocked(30);
@@ -132,7 +131,6 @@
         assertTrue(mUserState.mAccessibilityButtonTargets.isEmpty());
         assertFalse(mUserState.isTouchExplorationEnabledLocked());
         assertFalse(mUserState.isDisplayMagnificationEnabledLocked());
-        assertFalse(mUserState.isNavBarMagnificationEnabledLocked());
         assertFalse(mUserState.isAutoclickEnabledLocked());
         assertEquals(0, mUserState.getUserNonInteractiveUiTimeoutLocked());
         assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked());
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 79cc3db..f122014 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -31,6 +31,7 @@
 
 import static com.google.android.collect.Lists.newArrayList;
 import static com.google.android.collect.Sets.newHashSet;
+import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.junit.Assert.assertEquals;
@@ -54,6 +55,7 @@
 import static org.mockito.Mockito.when;
 
 import android.annotation.UserIdInt;
+import android.app.ActivityManager;
 import android.app.IUserSwitchObserver;
 import android.content.Context;
 import android.content.IIntentReceiver;
@@ -74,10 +76,10 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.Log;
 
-
 import androidx.test.filters.SmallTest;
 
 import com.android.server.FgThread;
+import com.android.server.am.UserState.KeyEvictedCallback;
 import com.android.server.pm.UserManagerService;
 import com.android.server.wm.WindowManagerService;
 
@@ -107,6 +109,7 @@
     private static final int TEST_USER_ID = 100;
     private static final int TEST_USER_ID1 = 101;
     private static final int TEST_USER_ID2 = 102;
+    private static final int TEST_USER_ID3 = 103;
     private static final int NONEXIST_USER_ID = 2;
     private static final int TEST_PRE_CREATED_USER_ID = 103;
 
@@ -120,6 +123,8 @@
     private TestInjector mInjector;
     private final HashMap<Integer, UserState> mUserStates = new HashMap<>();
 
+    private final KeyEvictedCallback mKeyEvictedCallback = (userId) -> { /* ignore */ };
+
     private static final List<String> START_FOREGROUND_USER_ACTIONS = newArrayList(
             Intent.ACTION_USER_STARTED,
             Intent.ACTION_USER_SWITCHED,
@@ -153,6 +158,7 @@
             doNothing().when(mInjector).activityManagerOnUserStopped(anyInt());
             doNothing().when(mInjector).clearBroadcastQueueForUser(anyInt());
             doNothing().when(mInjector).stackSupervisorRemoveUser(anyInt());
+            // All UserController params are set to default.
             mUserController = new UserController(mInjector);
             setUpUser(TEST_USER_ID, NO_USERINFO_FLAGS);
             setUpUser(TEST_PRE_CREATED_USER_ID, NO_USERINFO_FLAGS, /* preCreated=*/ true);
@@ -187,7 +193,9 @@
 
     @Test
     public void testStartUserUIDisabled() {
-        mUserController.mUserSwitchUiEnabled = false;
+        mUserController.setInitialConfig(/* userSwitchUiEnabled= */ false,
+                /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
+
         mUserController.startUser(TEST_USER_ID, true /* foreground */);
         verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
         verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
@@ -245,7 +253,9 @@
 
     @Test
     public void testFailedStartUserInForeground() {
-        mUserController.mUserSwitchUiEnabled = false;
+        mUserController.setInitialConfig(/* userSwitchUiEnabled= */ false,
+                /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
+
         mUserController.startUserInForeground(NONEXIST_USER_ID);
         verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
         verify(mInjector.getWindowManager()).setSwitchingUser(false);
@@ -326,7 +336,9 @@
 
     @Test
     public void testContinueUserSwitchUIDisabled() throws RemoteException {
-        mUserController.mUserSwitchUiEnabled = false;
+        mUserController.setInitialConfig(/* userSwitchUiEnabled= */ false,
+                /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
+
         // Start user -- this will update state of mUserController
         mUserController.startUser(TEST_USER_ID, true);
         Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
@@ -389,11 +401,13 @@
      * Test stopping of user from max running users limit.
      */
     @Test
-    public void testUserStoppingForMultipleUsersNormalMode()
+    public void testUserLockingFromUserSwitchingForMultipleUsersNonDelayedLocking()
             throws InterruptedException, RemoteException {
+        mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+                /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
+
         setUpUser(TEST_USER_ID1, 0);
         setUpUser(TEST_USER_ID2, 0);
-        mUserController.mMaxRunningUsers = 3;
         int numerOfUserSwitches = 1;
         addForegroundUserAndContinueUserSwitch(TEST_USER_ID, UserHandle.USER_SYSTEM,
                 numerOfUserSwitches, false);
@@ -430,12 +444,13 @@
      * all middle steps which takes too much work to mock.
      */
     @Test
-    public void testUserStoppingForMultipleUsersDelayedLockingMode()
+    public void testUserLockingFromUserSwitchingForMultipleUsersDelayedLockingMode()
             throws InterruptedException, RemoteException {
+        mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+                /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ true);
+
         setUpUser(TEST_USER_ID1, 0);
         setUpUser(TEST_USER_ID2, 0);
-        mUserController.mMaxRunningUsers = 3;
-        mUserController.mDelayUserDataLocking = true;
         int numerOfUserSwitches = 1;
         addForegroundUserAndContinueUserSwitch(TEST_USER_ID, UserHandle.USER_SYSTEM,
                 numerOfUserSwitches, false);
@@ -456,7 +471,7 @@
         // Skip all other steps and test unlock delaying only
         UserState uss = mUserStates.get(TEST_USER_ID);
         uss.setState(UserState.STATE_SHUTDOWN); // necessary state change from skipped part
-        mUserController.finishUserStopped(uss);
+        mUserController.finishUserStopped(uss, /* allowDelayedLocking= */ true);
         // Cannot mock FgThread handler, so confirm that there is no posted message left before
         // checking.
         waitForHandlerToComplete(FgThread.getHandler(), HANDLER_WAIT_TIME_MS);
@@ -473,12 +488,87 @@
                 mUserController.getRunningUsersLU());
         UserState ussUser1 = mUserStates.get(TEST_USER_ID1);
         ussUser1.setState(UserState.STATE_SHUTDOWN);
-        mUserController.finishUserStopped(ussUser1);
+        mUserController.finishUserStopped(ussUser1, /* allowDelayedLocking= */ true);
         waitForHandlerToComplete(FgThread.getHandler(), HANDLER_WAIT_TIME_MS);
         verify(mInjector.mStorageManagerMock, times(1))
                 .lockUserKey(TEST_USER_ID);
     }
 
+    /**
+     * Test locking user with mDelayUserDataLocking false.
+     */
+    @Test
+    public void testUserLockingWithStopUserForNonDelayedLockingMode() throws Exception {
+        mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+                /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
+
+        setUpAndStartUserInBackground(TEST_USER_ID);
+        assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID, /* delayedLocking= */ true,
+                /* keyEvictedCallback= */ null, /* expectLocking= */ true);
+
+        setUpAndStartUserInBackground(TEST_USER_ID1);
+        assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* delayedLocking= */ true,
+                /* keyEvictedCallback= */ mKeyEvictedCallback, /* expectLocking= */ true);
+
+        setUpAndStartUserInBackground(TEST_USER_ID2);
+        assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID2, /* delayedLocking= */ false,
+                /* keyEvictedCallback= */ null, /* expectLocking= */ true);
+
+        setUpAndStartUserInBackground(TEST_USER_ID3);
+        assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID3, /* delayedLocking= */ false,
+                /* keyEvictedCallback= */ mKeyEvictedCallback, /* expectLocking= */ true);
+    }
+
+    /**
+     * Test conditional delayed locking with mDelayUserDataLocking true.
+     */
+    @Test
+    public void testUserLockingForDelayedLockingMode() throws Exception {
+        mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+                /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ true);
+
+        // delayedLocking set and no KeyEvictedCallback, so it should not lock.
+        setUpAndStartUserInBackground(TEST_USER_ID);
+        assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID, /* delayedLocking= */ true,
+                /* keyEvictedCallback= */ null, /* expectLocking= */ false);
+
+        setUpAndStartUserInBackground(TEST_USER_ID1);
+        assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* delayedLocking= */ true,
+                /* keyEvictedCallback= */ mKeyEvictedCallback, /* expectLocking= */ true);
+
+        setUpAndStartUserInBackground(TEST_USER_ID2);
+        assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID2, /* delayedLocking= */ false,
+                /* keyEvictedCallback= */ null, /* expectLocking= */ true);
+
+        setUpAndStartUserInBackground(TEST_USER_ID3);
+        assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID3, /* delayedLocking= */ false,
+                /* keyEvictedCallback= */ mKeyEvictedCallback, /* expectLocking= */ true);
+    }
+
+    private void setUpAndStartUserInBackground(int userId) throws Exception {
+        setUpUser(userId, 0);
+        mUserController.startUser(userId, /* foreground= */ false);
+        verify(mInjector.mStorageManagerMock, times(1))
+                .unlockUserKey(TEST_USER_ID, 0, null, null);
+        mUserStates.put(userId, mUserController.getStartedUserState(userId));
+    }
+
+    private void assertUserLockedOrUnlockedAfterStopping(int userId, boolean delayedLocking,
+            KeyEvictedCallback keyEvictedCallback, boolean expectLocking)  throws Exception {
+        int r = mUserController.stopUser(userId, /* force= */ true, /* delayedLocking= */
+                delayedLocking, null, keyEvictedCallback);
+        assertThat(r).isEqualTo(ActivityManager.USER_OP_SUCCESS);
+        // fake all interim steps
+        UserState ussUser = mUserStates.get(userId);
+        ussUser.setState(UserState.STATE_SHUTDOWN);
+        // Passing delayedLocking invalidates incorrect internal data passing but currently there is
+        // no easy way to get that information passed through lambda.
+        mUserController.finishUserStopped(ussUser, delayedLocking);
+        waitForHandlerToComplete(FgThread.getHandler(), HANDLER_WAIT_TIME_MS);
+        verify(mInjector.mStorageManagerMock, times(expectLocking ? 1 : 0))
+                .lockUserKey(userId);
+    }
+
     private void addForegroundUserAndContinueUserSwitch(int newUserId, int expectedOldUserId,
             int expectedNumberOfCalls, boolean expectOldUserStopping)
             throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index f96d996..bec265e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -411,7 +411,8 @@
         // HAT sent to keystore
         verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
         // Send onAuthenticated to client
-        verify(mReceiver1).onAuthenticationSucceeded();
+        verify(mReceiver1).onAuthenticationSucceeded(
+                BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC);
         // Current session becomes null
         assertNull(mBiometricService.mCurrentAuthSession);
     }
@@ -461,7 +462,8 @@
                 BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED);
         waitForIdle();
         verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
-        verify(mReceiver1).onAuthenticationSucceeded();
+        verify(mReceiver1).onAuthenticationSucceeded(
+                BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
index abe39f0..312ff2c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
@@ -223,4 +223,22 @@
                     Utils.biometricConstantsToBiometricManager(testCases[i][0]));
         }
     }
+
+    @Test
+    public void testGetAuthenticationTypeForResult_getsCorrectType() {
+        assertEquals(Utils.getAuthenticationTypeForResult(
+                BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED),
+                BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL);
+        assertEquals(Utils.getAuthenticationTypeForResult(
+                BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED),
+                BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC);
+        assertEquals(Utils.getAuthenticationTypeForResult(
+                BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED),
+                BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetAuthResultType_throwsForInvalidReason() {
+        Utils.getAuthenticationTypeForResult(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
+    }
 }
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 45729e5..d670361 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1968,6 +1968,29 @@
         // TODO Make sure restrictions are written to the file.
     }
 
+    private static final Set<String> PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS =
+            Sets.newSet(
+                    UserManager.DISALLOW_CONFIG_DATE_TIME,
+                    UserManager.DISALLOW_ADD_USER,
+                    UserManager.DISALLOW_BLUETOOTH,
+                    UserManager.DISALLOW_BLUETOOTH_SHARING,
+                    UserManager.DISALLOW_CONFIG_BLUETOOTH,
+                    UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
+                    UserManager.DISALLOW_CONFIG_LOCATION,
+                    UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
+                    UserManager.DISALLOW_CONFIG_PRIVATE_DNS,
+                    UserManager.DISALLOW_CONFIG_TETHERING,
+                    UserManager.DISALLOW_CONFIG_WIFI,
+                    UserManager.DISALLOW_CONTENT_CAPTURE,
+                    UserManager.DISALLOW_CONTENT_SUGGESTIONS,
+                    UserManager.DISALLOW_DATA_ROAMING,
+                    UserManager.DISALLOW_DEBUGGING_FEATURES,
+                    UserManager.DISALLOW_SAFE_BOOT,
+                    UserManager.DISALLOW_SHARE_LOCATION,
+                    UserManager.DISALLOW_SMS,
+                    UserManager.DISALLOW_USB_FILE_TRANSFER
+            );
+
     public void testSetUserRestriction_asPoOfOrgOwnedDevice() throws Exception {
         final int MANAGED_PROFILE_USER_ID = DpmMockContext.CALLER_USER_HANDLE;
         final int MANAGED_PROFILE_ADMIN_UID =
@@ -1980,15 +2003,9 @@
         when(getServices().userManager.getProfileParent(MANAGED_PROFILE_USER_ID))
                 .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
 
-        parentDpm.addUserRestriction(admin1, UserManager.DISALLOW_CONFIG_DATE_TIME);
-        verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
-                eq(MANAGED_PROFILE_USER_ID),
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_CONFIG_DATE_TIME),
-                eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE));
-        reset(getServices().userManagerInternal);
-
-        parentDpm.clearUserRestriction(admin1, UserManager.DISALLOW_CONFIG_DATE_TIME);
-        reset(getServices().userManagerInternal);
+        for (String restriction : PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS) {
+            addAndRemoveUserRestrictionOnParentDpm(restriction);
+        }
 
         parentDpm.setCameraDisabled(admin1, true);
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
@@ -2005,6 +2022,20 @@
         reset(getServices().userManagerInternal);
     }
 
+    private void addAndRemoveUserRestrictionOnParentDpm(String restriction) {
+        parentDpm.addUserRestriction(admin1, restriction);
+        verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
+                eq(DpmMockContext.CALLER_USER_HANDLE),
+                MockUtils.checkUserRestrictions(restriction),
+                eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE));
+        parentDpm.clearUserRestriction(admin1, restriction);
+        DpmTestUtils.assertRestrictions(
+                DpmTestUtils.newRestrictions(),
+                parentDpm.getUserRestrictions(admin1)
+        );
+        reset(getServices().userManagerInternal);
+    }
+
     public void testNoDefaultEnabledUserRestrictions() throws Exception {
         mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
         mContext.callerPermissions.add(permission.MANAGE_USERS);
diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
index 7a070ee..eda2b70 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
@@ -188,7 +188,7 @@
     }
 
     @Test
-    public void testEvaluateRules_ruleNotInDNF_ignoreAndAllow() {
+    public void testEvaluateRules_orRules() {
         CompoundFormula compoundFormula =
                 new CompoundFormula(
                         CompoundFormula.OR,
@@ -206,11 +206,11 @@
         IntegrityCheckResult result =
                 RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
 
-        assertEquals(ALLOW, result.getEffect());
+        assertEquals(DENY, result.getEffect());
     }
 
     @Test
-    public void testEvaluateRules_compoundFormulaWithNot_allow() {
+    public void testEvaluateRules_compoundFormulaWithNot_deny() {
         CompoundFormula openSubFormula =
                 new CompoundFormula(
                         CompoundFormula.AND,
@@ -230,7 +230,7 @@
         IntegrityCheckResult result =
                 RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
 
-        assertEquals(ALLOW, result.getEffect());
+        assertEquals(DENY, result.getEffect());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java
index c7cc343..57274bf 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java
@@ -53,17 +53,17 @@
         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
         ByteTrackedOutputStream byteTrackedOutputStream = new ByteTrackedOutputStream(outputStream);
 
-        BitOutputStream bitOutputStream = new BitOutputStream();
+        BitOutputStream bitOutputStream = new BitOutputStream(byteTrackedOutputStream);
         bitOutputStream.setNext(/* numOfBits= */5, /* value= */1);
-        byteTrackedOutputStream.write(bitOutputStream.toByteArray());
+        bitOutputStream.flush();
 
         // Even though we wrote 5 bits, this will complete to 1 byte.
         assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(1);
 
         // Add a bit less than 2 bytes (10 bits).
-        bitOutputStream.clear();
         bitOutputStream.setNext(/* numOfBits= */10, /* value= */1);
-        byteTrackedOutputStream.write(bitOutputStream.toByteArray());
+        bitOutputStream.flush();
+        assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(3);
 
         assertThat(outputStream.toByteArray().length).isEqualTo(3);
     }
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
index 291cfee..742952e 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
@@ -115,8 +115,7 @@
                                 + getKeyValueString(START_INDEXING_KEY, 500)
                                 + getKeyValueString(END_INDEXING_KEY, 900)
                                 + getKeyValueString(START_INDEXING_KEY, 900)
-                                + getKeyValueString(END_INDEXING_KEY, 945)
-                                + getBits(1, 1));
+                                + getKeyValueString(END_INDEXING_KEY, 945));
         ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
         rule.put(stringBytes);
         InputStream inputStream = new ByteArrayInputStream(rule.array());
@@ -153,8 +152,7 @@
                                 + getKeyValueString("888", 800)
                                 + getKeyValueString(END_INDEXING_KEY, 900)
                                 + getKeyValueString(START_INDEXING_KEY, 900)
-                                + getKeyValueString(END_INDEXING_KEY, 945)
-                                + getBits(1, 1));
+                                + getKeyValueString(END_INDEXING_KEY, 945));
         ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
         rule.put(stringBytes);
         return new ByteArrayInputStream(rule.array());
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
index bc2aac0..8ee5f5f 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
@@ -139,10 +139,12 @@
                 SERIALIZED_START_INDEXING_KEY
                         + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
                         + SERIALIZED_END_INDEXING_KEY
-                        + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */32);
+                        + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32);
         byte[] expectedIndexingBytes =
-                getBytes(serializedIndexingBytes + serializedIndexingBytes
-                        + serializedIndexingBytes + getBits(1, 1));
+                getBytes(
+                        serializedIndexingBytes
+                                + serializedIndexingBytes
+                                + serializedIndexingBytes);
         expectedIndexingOutputStream.write(expectedIndexingBytes);
         assertThat(indexingOutputStream.toByteArray())
                 .isEqualTo(expectedIndexingOutputStream.toByteArray());
@@ -200,11 +202,14 @@
                 SERIALIZED_START_INDEXING_KEY
                         + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
                         + SERIALIZED_END_INDEXING_KEY
-                        + getBits(DEFAULT_FORMAT_VERSION_BYTES.length
-                        + getBytes(expectedBits).length, /* numOfBits= */ 32);
-        expectedIndexingOutputStream.write(getBytes(
-                expectedIndexingBitsForIndexed + expectedIndexingBitsForIndexed
-                        + expectedIndexingBitsForUnindexed + getBits(1, 1)));
+                        + getBits(
+                                DEFAULT_FORMAT_VERSION_BYTES.length + getBytes(expectedBits).length,
+                                /* numOfBits= */ 32);
+        expectedIndexingOutputStream.write(
+                getBytes(
+                        expectedIndexingBitsForIndexed
+                                + expectedIndexingBitsForIndexed
+                                + expectedIndexingBitsForUnindexed));
 
         assertThat(indexingOutputStream.toByteArray())
                 .isEqualTo(expectedIndexingOutputStream.toByteArray());
@@ -513,16 +518,19 @@
         // and 225 non-indexed rules..
         List<Rule> ruleList = new ArrayList();
         for (int count = 0; count < ruleCount; count++) {
-            ruleList.add(getRuleWithPackageNameAndSampleInstallerName(
-                    String.format("%s%04d", packagePrefix, count)));
+            ruleList.add(
+                    getRuleWithPackageNameAndSampleInstallerName(
+                            String.format("%s%04d", packagePrefix, count)));
         }
         for (int count = 0; count < ruleCount; count++) {
-            ruleList.add(getRuleWithAppCertificateAndSampleInstallerName(
-                    String.format("%s%04d", appCertificatePrefix, count)));
+            ruleList.add(
+                    getRuleWithAppCertificateAndSampleInstallerName(
+                            String.format("%s%04d", appCertificatePrefix, count)));
         }
         for (int count = 0; count < ruleCount; count++) {
-            ruleList.add(getNonIndexedRuleWithInstallerName(
-                    String.format("%s%04d", installerNamePrefix, count)));
+            ruleList.add(
+                    getNonIndexedRuleWithInstallerName(
+                            String.format("%s%04d", installerNamePrefix, count)));
         }
 
         // Serialize the rules.
@@ -543,8 +551,7 @@
         int totalBytesWritten = DEFAULT_FORMAT_VERSION_BYTES.length;
 
         String expectedIndexingBytesForPackageNameIndexed =
-                SERIALIZED_START_INDEXING_KEY
-                        + getBits(totalBytesWritten, /* numOfBits= */ 32);
+                SERIALIZED_START_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32);
         for (int count = 0; count < ruleCount; count++) {
             String packageName = String.format("%s%04d", packagePrefix, count);
             if (count > 0 && count % INDEXING_BLOCK_SIZE == 0) {
@@ -556,18 +563,17 @@
             }
 
             byte[] bytesForPackage =
-                    getBytes(getSerializedCompoundRuleWithPackageNameAndSampleInstallerName(
-                            packageName));
+                    getBytes(
+                            getSerializedCompoundRuleWithPackageNameAndSampleInstallerName(
+                                    packageName));
             expectedOrderedRuleOutputStream.write(bytesForPackage);
             totalBytesWritten += bytesForPackage.length;
         }
         expectedIndexingBytesForPackageNameIndexed +=
-                SERIALIZED_END_INDEXING_KEY
-                        + getBits(totalBytesWritten, /* numOfBits= */ 32);
+                SERIALIZED_END_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32);
 
         String expectedIndexingBytesForAppCertificateIndexed =
-                SERIALIZED_START_INDEXING_KEY
-                        + getBits(totalBytesWritten, /* numOfBits= */ 32);
+                SERIALIZED_START_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32);
         for (int count = 0; count < ruleCount; count++) {
             String appCertificate = String.format("%s%04d", appCertificatePrefix, count);
             if (count > 0 && count % INDEXING_BLOCK_SIZE == 0) {
@@ -579,33 +585,32 @@
             }
 
             byte[] bytesForPackage =
-                    getBytes(getSerializedCompoundRuleWithCertificateNameAndSampleInstallerName(
-                            appCertificate));
+                    getBytes(
+                            getSerializedCompoundRuleWithCertificateNameAndSampleInstallerName(
+                                    appCertificate));
             expectedOrderedRuleOutputStream.write(bytesForPackage);
             totalBytesWritten += bytesForPackage.length;
         }
         expectedIndexingBytesForAppCertificateIndexed +=
-                SERIALIZED_END_INDEXING_KEY
-                        + getBits(totalBytesWritten, /* numOfBits= */ 32);
+                SERIALIZED_END_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32);
 
         String expectedIndexingBytesForUnindexed =
-                SERIALIZED_START_INDEXING_KEY
-                        + getBits(totalBytesWritten, /* numOfBits= */ 32);
+                SERIALIZED_START_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32);
         for (int count = 0; count < ruleCount; count++) {
             byte[] bytesForPackage =
-                    getBytes(getSerializedCompoundRuleWithInstallerNameAndInstallerCert(
-                            String.format("%s%04d", installerNamePrefix, count)));
+                    getBytes(
+                            getSerializedCompoundRuleWithInstallerNameAndInstallerCert(
+                                    String.format("%s%04d", installerNamePrefix, count)));
             expectedOrderedRuleOutputStream.write(bytesForPackage);
             totalBytesWritten += bytesForPackage.length;
         }
         expectedIndexingBytesForUnindexed +=
-                SERIALIZED_END_INDEXING_KEY
-                        + getBits(totalBytesWritten, /* numOfBits= */ 32);
+                SERIALIZED_END_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32);
         expectedIndexingOutputStream.write(
-                getBytes(expectedIndexingBytesForPackageNameIndexed
-                        + expectedIndexingBytesForAppCertificateIndexed
-                        + expectedIndexingBytesForUnindexed
-                        + getBits(1, 1)));
+                getBytes(
+                        expectedIndexingBytesForPackageNameIndexed
+                                + expectedIndexingBytesForAppCertificateIndexed
+                                + expectedIndexingBytesForUnindexed));
 
         assertThat(ruleOutputStream.toByteArray())
                 .isEqualTo(expectedOrderedRuleOutputStream.toByteArray());
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index e1c489e..355cada 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -112,7 +112,7 @@
 import android.net.NetworkStats;
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
-import android.net.StringNetworkSpecifier;
+import android.net.TelephonyNetworkSpecifier;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.INetworkManagementService;
@@ -1804,9 +1804,11 @@
                 .getService(NetworkPolicyManagerInternal.class);
         npmi.onStatsProviderLimitReached("TEST");
 
-        // Verifies that the limit reached leads to a force update.
+        // Verifies that the limit reached leads to a force update and new limit should be set.
         postMsgAndWaitForCompletion();
         verify(mStatsService).forceUpdate();
+        postMsgAndWaitForCompletion();
+        verify(mStatsService).setStatsProviderLimit(TEST_IFACE, 10000L - 4999L - 1999L);
     }
 
     /**
@@ -1911,7 +1913,8 @@
         if (!roaming) {
             nc.addCapability(NET_CAPABILITY_NOT_ROAMING);
         }
-        nc.setNetworkSpecifier(new StringNetworkSpecifier(String.valueOf(subId)));
+        nc.setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
+                .setSubscriptionId(subId).build());
         return nc;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
index cc170af..c56034a 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
@@ -335,7 +335,9 @@
             }).when(driver).getProperties_2_3(any());
         }
 
-        mService = new SoundTriggerMiddlewareImpl(mHalDriver, mAudioSessionProvider);
+        mService = new SoundTriggerMiddlewareImpl(() -> {
+            return mHalDriver;
+        }, mAudioSessionProvider);
     }
 
     private Pair<Integer, SoundTriggerHwCallback> loadGenericModel_2_0(ISoundTriggerModule module,
@@ -798,12 +800,12 @@
 
             @Override
             public boolean linkToDeath(DeathRecipient recipient, long cookie) {
-                throw new UnsupportedOperationException();
+                return true;
             }
 
             @Override
             public boolean unlinkToDeath(DeathRecipient recipient) {
-                throw new UnsupportedOperationException();
+                return true;
             }
         };
 
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
index 8a3183f..d940a6a 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -651,7 +651,6 @@
 
         @Override
         public long systemClockMillis() {
-            assertWakeLockAcquired();
             return mSystemClockMillis;
         }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 399cf49..135d005 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
 import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
@@ -44,6 +45,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.app.BlockedAppActivity;
 import com.android.internal.app.HarmfulAppWarningActivity;
 import com.android.internal.app.SuspendedAppActivity;
 import com.android.internal.app.UnlaunchableAppActivity;
@@ -105,6 +107,8 @@
     private PackageManagerService mPackageManager;
     @Mock
     private ActivityManagerInternal mAmInternal;
+    @Mock
+    private LockTaskController mLockTaskController;
 
     private ActivityStartInterceptor mInterceptor;
     private ActivityInfo mAInfo = new ActivityInfo();
@@ -145,6 +149,13 @@
         when(mPackageManager.getHarmfulAppWarning(TEST_PACKAGE_NAME, TEST_USER_ID))
                 .thenReturn(null);
 
+        // Mock LockTaskController
+        mAInfo.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
+        when(mService.getLockTaskController()).thenReturn(mLockTaskController);
+        when(mLockTaskController.isActivityAllowed(
+                TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))
+                .thenReturn(true);
+
         // Initialise activity info
         mAInfo.applicationInfo = new ApplicationInfo();
         mAInfo.packageName = mAInfo.applicationInfo.packageName = TEST_PACKAGE_NAME;
@@ -196,6 +207,18 @@
     }
 
     @Test
+    public void testInterceptLockTaskModeViolationPackage() {
+        when(mLockTaskController.isActivityAllowed(
+                TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))
+                .thenReturn(false);
+
+        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+
+        assertTrue(BlockedAppActivity.createIntent(TEST_USER_ID, TEST_PACKAGE_NAME)
+                .filterEquals(mInterceptor.mIntent));
+    }
+
+    @Test
     public void testInterceptQuietProfile() {
         // GIVEN that the user the activity is starting as is currently in quiet mode
         when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index 039ff60..75ec53d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -29,6 +29,9 @@
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
 import static android.os.Process.SYSTEM_UID;
 import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
 
@@ -693,6 +696,38 @@
         assertTrue((StatusBarManager.DISABLE2_QUICK_SETTINGS & flags.second) != 0);
     }
 
+    @Test
+    public void testIsActivityAllowed() {
+        // WHEN lock task mode is not enabled
+        assertTrue(mLockTaskController.isActivityAllowed(
+                TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
+
+        // WHEN lock task mode is enabled
+        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+
+        // package with LOCK_TASK_LAUNCH_MODE_ALWAYS should always be allowed
+        assertTrue(mLockTaskController.isActivityAllowed(
+                TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_ALWAYS));
+
+        // unwhitelisted package should not be allowed
+        assertFalse(mLockTaskController.isActivityAllowed(
+                TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
+
+        // update the whitelist
+        String[] whitelist = new String[] { TEST_PACKAGE_NAME };
+        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
+
+        // whitelisted package should be allowed
+        assertTrue(mLockTaskController.isActivityAllowed(
+                TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
+
+        // package with LOCK_TASK_LAUNCH_MODE_NEVER should never be allowed
+        assertFalse(mLockTaskController.isActivityAllowed(
+                TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_NEVER));
+    }
+
     private Task getTask(int lockTaskAuth) {
         return getTask(TEST_PACKAGE_NAME, lockTaskAuth);
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 0ffeccf..7ee68ea 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1104,6 +1104,16 @@
      */
     public static final int CDMA_ROAMING_MODE_ANY = 2;
 
+    /** @hide */
+    @IntDef(prefix = { "CDMA_ROAMING_MODE_" }, value = {
+            CDMA_ROAMING_MODE_RADIO_DEFAULT,
+            CDMA_ROAMING_MODE_HOME,
+            CDMA_ROAMING_MODE_AFFILIATED,
+            CDMA_ROAMING_MODE_ANY
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CdmaRoamingMode{}
+
     /**
      * An unknown carrier id. It could either be subscription unavailable or the subscription
      * carrier cannot be recognized. Unrecognized carriers here means
@@ -8974,8 +8984,9 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    public int getCdmaRoamingMode() {
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @CdmaRoamingMode int getCdmaRoamingMode() {
         int mode = CDMA_ROAMING_MODE_RADIO_DEFAULT;
         try {
             ITelephony telephony = getITelephony();
@@ -9002,8 +9013,9 @@
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public boolean setCdmaRoamingMode(int mode) {
+    public boolean setCdmaRoamingMode(@CdmaRoamingMode int mode) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
@@ -9015,6 +9027,36 @@
         return false;
     }
 
+    /** @hide */
+    @IntDef(flag = true, prefix = { "CDMA_SUBSCRIPTION_" }, value = {
+            CDMA_SUBSCRIPTION_UNKNOWN,
+            CDMA_SUBSCRIPTION_RUIM_SIM,
+            CDMA_SUBSCRIPTION_NV
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CdmaSubscription{}
+
+    /** Used for CDMA subscription mode, it'll be UNKNOWN if there is no Subscription source.
+     * @hide
+     */
+    @SystemApi
+    public static final int CDMA_SUBSCRIPTION_UNKNOWN  = -1;
+
+    /** Used for CDMA subscription mode: RUIM/SIM (default)
+     * @hide
+     */
+    @SystemApi
+    public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0;
+
+    /** Used for CDMA subscription mode: NV -> non-volatile memory
+     * @hide
+     */
+    @SystemApi
+    public static final int CDMA_SUBSCRIPTION_NV       = 1;
+
+    /** @hide */
+    public static final int PREFERRED_CDMA_SUBSCRIPTION = CDMA_SUBSCRIPTION_RUIM_SIM;
+
     /**
      * Sets the subscription mode for CDMA phone to the given mode {@code mode}.
      *
@@ -9022,14 +9064,15 @@
      *
      * @return {@code true} if successed.
      *
-     * @see Phone#CDMA_SUBSCRIPTION_UNKNOWN
-     * @see Phone#CDMA_SUBSCRIPTION_RUIM_SIM
-     * @see Phone#CDMA_SUBSCRIPTION_NV
+     * @see #CDMA_SUBSCRIPTION_UNKNOWN
+     * @see #CDMA_SUBSCRIPTION_RUIM_SIM
+     * @see #CDMA_SUBSCRIPTION_NV
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public boolean setCdmaSubscriptionMode(int mode) {
+    public boolean setCdmaSubscriptionMode(@CdmaSubscription int mode) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 6005f77..ab22d46 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -597,19 +597,25 @@
     /**
      * Notify the framework that an RCS autoconfiguration XML file has been received for
      * provisioning.
+     * <p>
+     * Requires Permission: Manifest.permission.MODIFY_PHONE_STATE or that the calling app has
+     * carrier privileges (see {@link #hasCarrierPrivileges}).
      * @param config The XML file to be read. ASCII/UTF8 encoded text if not compressed.
      * @param isCompressed The XML file is compressed in gzip format and must be decompressed
      *         before being read.
-     * @hide
+     *
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) {
         if (config == null) {
             throw new IllegalArgumentException("Must include a non-null config XML file.");
         }
-        // TODO: Connect to ImsConfigImplBase.
-        throw new UnsupportedOperationException("notifyRcsAutoConfigurationReceived is not"
-                + "supported");
+        try {
+            getITelephony().notifyRcsAutoConfigurationReceived(mSubId, config, isCompressed);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+
     }
 
     private static boolean isImsAvailableOnDevice() {
diff --git a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
index 53e4596..57206c9 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
@@ -40,4 +40,5 @@
     // Return result code defined in ImsConfig#OperationStatusConstants
     int setConfigString(int item, String value);
     void updateImsCarrierConfigs(in PersistableBundle bundle);
+    void notifyRcsAutoConfigurationReceived(in byte[] config, boolean isCompressed);
 }
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index 60cf216..6a2638b 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -17,6 +17,7 @@
 package android.telephony.ims.stub;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.content.Context;
@@ -200,6 +201,12 @@
             }
         }
 
+        @Override
+        public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed)
+                throws RemoteException {
+            getImsConfigImpl().notifyRcsAutoConfigurationReceived(config, isCompressed);
+        }
+
         private void notifyImsConfigChanged(int item, int value) throws RemoteException {
             getImsConfigImpl().notifyConfigChanged(item, value);
         }
@@ -358,9 +365,9 @@
      * @param config The XML file to be read, if not compressed, it should be in ASCII/UTF8 format.
      * @param isCompressed The XML file is compressed in gzip format and must be decompressed
      *         before being read.
-     * @hide
+     *
      */
-    public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) {
+    public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) {
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 28f3974..cdb95a8 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2137,4 +2137,9 @@
      * Command line command to enable or disable handling of CEP data for test purposes.
      */
     oneway void setCepEnabled(boolean isCepEnabled);
+
+    /**
+     * Notify Rcs auto config received.
+     */
+    void notifyRcsAutoConfigurationReceived(int subId, in byte[] config, boolean isCompressed);
 }
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 3877cc1..ce20311 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -23,12 +23,14 @@
 
 import android.Manifest;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.rollback.RollbackInfo;
 import android.content.rollback.RollbackManager;
 import android.os.ParcelFileDescriptor;
+import android.os.storage.StorageManager;
 import android.provider.DeviceConfig;
 
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -185,12 +187,6 @@
      */
     @Test
     public void testNativeWatchdogTriggersRollback_Phase1() throws Exception {
-        // When multiple staged sessions are installed on a device which doesn't support checkpoint,
-        // only the 1st one will prevail. We have to check no other rollbacks available to ensure
-        // TestApp.A is always the 1st and the only one to commit so rollback can work as intended.
-        // If there are leftover rollbacks from previous tests, this assertion will fail.
-        assertThat(RollbackUtils.getRollbackManager().getAvailableRollbacks()).isEmpty();
-
         Uninstall.packages(TestApp.A);
         Install.single(TestApp.A1).commit();
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
@@ -220,6 +216,64 @@
                 TestApp.A)).isNotNull();
     }
 
+    /**
+     * Stage install an apk with rollback that will be later triggered by unattributable crash.
+     */
+    @Test
+    public void testNativeWatchdogTriggersRollbackForAll_Phase1() throws Exception {
+        Uninstall.packages(TestApp.A);
+        Install.single(TestApp.A1).commit();
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+
+        Install.single(TestApp.A2).setEnableRollback().setStaged().commit();
+    }
+
+    /**
+     * Verify the rollback is available and then install another package with rollback.
+     */
+    @Test
+    public void testNativeWatchdogTriggersRollbackForAll_Phase2() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+        RollbackManager rm = RollbackUtils.getRollbackManager();
+        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+                TestApp.A)).isNotNull();
+
+        // Install another package with rollback
+        Uninstall.packages(TestApp.B);
+        Install.single(TestApp.B1).commit();
+        assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1);
+
+        Install.single(TestApp.B2).setEnableRollback().setStaged().commit();
+    }
+
+    /**
+     * Verify the rollbacks are available.
+     */
+    @Test
+    public void testNativeWatchdogTriggersRollbackForAll_Phase3() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
+        RollbackManager rm = RollbackUtils.getRollbackManager();
+        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+                TestApp.A)).isNotNull();
+        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+                TestApp.B)).isNotNull();
+    }
+
+    /**
+     * Verify the rollbacks are committed after crashing.
+     */
+    @Test
+    public void testNativeWatchdogTriggersRollbackForAll_Phase4() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1);
+        RollbackManager rm = RollbackUtils.getRollbackManager();
+        assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
+                TestApp.A)).isNotNull();
+        assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
+                TestApp.B)).isNotNull();
+    }
+
     @Test
     public void testNetworkFailedRollback_Phase1() throws Exception {
         // Remove available rollbacks and uninstall NetworkStack on /data/
@@ -438,6 +492,7 @@
         RollbackManager rm = RollbackUtils.getRollbackManager();
         rm.getAvailableRollbacks().stream().flatMap(info -> info.getPackages().stream())
                 .map(info -> info.getPackageName()).forEach(rm::expireRollbackForPackage);
+        assertThat(RollbackUtils.getRollbackManager().getAvailableRollbacks()).isEmpty();
     }
 
     private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test";
@@ -498,4 +553,11 @@
                 .executeShellCommand(cmd);
         IoUtils.closeQuietly(pfd);
     }
+
+    @Test
+    public void isCheckpointSupported() {
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
+        assertThat(sm.isCheckpointSupported()).isTrue();
+    }
 }
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 6daa6bc..181e29a 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
@@ -17,6 +17,7 @@
 package com.android.tests.rollback.host;
 
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 import static org.testng.Assert.assertThrows;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
@@ -62,6 +63,7 @@
                 "rm -f /system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex "
                         + "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex");
         getDevice().reboot();
+        runPhase("testCleanUp");
     }
 
     @After
@@ -95,7 +97,6 @@
 
     @Test
     public void testNativeWatchdogTriggersRollback() throws Exception {
-        //Stage install ModuleMetadata package - this simulates a Mainline module update
         runPhase("testNativeWatchdogTriggersRollback_Phase1");
 
         // Reboot device to activate staged package
@@ -121,6 +122,40 @@
         runPhase("testNativeWatchdogTriggersRollback_Phase3");
     }
 
+    @Test
+    public void testNativeWatchdogTriggersRollbackForAll() throws Exception {
+        // This test requires committing multiple staged rollbacks
+        assumeTrue(isCheckpointSupported());
+
+        // Install a package with rollback enabled.
+        runPhase("testNativeWatchdogTriggersRollbackForAll_Phase1");
+        getDevice().reboot();
+
+        // Once previous staged install is applied, install another package
+        runPhase("testNativeWatchdogTriggersRollbackForAll_Phase2");
+        getDevice().reboot();
+
+        // Verify the new staged install has also been applied successfully.
+        runPhase("testNativeWatchdogTriggersRollbackForAll_Phase3");
+
+        // crash system_server enough times to trigger a rollback
+        crashProcess("system_server", NATIVE_CRASHES_THRESHOLD);
+
+        // Rollback should be committed automatically now.
+        // Give time for rollback to be committed. This could take a while,
+        // because we need all of the following to happen:
+        // 1. system_server comes back up and boot completes.
+        // 2. Rollback health observer detects updatable crashing signal.
+        // 3. Staged rollback session becomes ready.
+        // 4. Device actually reboots.
+        // So we give a generous timeout here.
+        assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5)));
+        getDevice().waitForDeviceAvailable();
+
+        // verify all available rollbacks have been committed
+        runPhase("testNativeWatchdogTriggersRollbackForAll_Phase4");
+    }
+
     /**
      * Tests failed network health check triggers watchdog staged rollbacks.
      */
@@ -244,4 +279,13 @@
         // Find the NetworkStack path (can be NetworkStack.apk or NetworkStackNext.apk)
         return getDevice().executeShellCommand("ls /system/priv-app/NetworkStack*/*.apk");
     }
+
+    private boolean isCheckpointSupported() throws Exception {
+        try {
+            runPhase("isCheckpointSupported");
+            return true;
+        } catch (AssertionError ignore) {
+            return false;
+        }
+    }
 }
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index a7328ac..6005cc3 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -27,8 +27,8 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.net.LinkProperties.CompareResult;
 import android.net.LinkProperties.ProvisioningChange;
+import android.net.util.LinkPropertiesUtils.CompareResult;
 import android.system.OsConstants;
 import android.util.ArraySet;
 
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index daf187d..91c9a2a 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -22,6 +22,8 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.net.util.MacAddressUtils;
+
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -122,11 +124,11 @@
 
         for (MacAddress mac : multicastAddresses) {
             String msg = mac.toString() + " expected to be a multicast address";
-            assertTrue(msg, mac.isMulticastAddress());
+            assertTrue(msg, MacAddressUtils.isMulticastAddress(mac));
         }
         for (MacAddress mac : unicastAddresses) {
             String msg = mac.toString() + " expected not to be a multicast address";
-            assertFalse(msg, mac.isMulticastAddress());
+            assertFalse(msg, MacAddressUtils.isMulticastAddress(mac));
         }
     }
 
@@ -156,7 +158,7 @@
     public void testMacAddressConversions() {
         final int iterations = 10000;
         for (int i = 0; i < iterations; i++) {
-            MacAddress mac = MacAddress.createRandomUnicastAddress();
+            MacAddress mac = MacAddressUtils.createRandomUnicastAddress();
 
             String stringRepr = mac.toString();
             byte[] bytesRepr = mac.toByteArray();
@@ -188,7 +190,7 @@
         final String expectedLocalOui = "26:5f:78";
         final MacAddress base = MacAddress.fromString(anotherOui + ":0:0:0");
         for (int i = 0; i < iterations; i++) {
-            MacAddress mac = MacAddress.createRandomUnicastAddress(base, r);
+            MacAddress mac = MacAddressUtils.createRandomUnicastAddress(base, r);
             String stringRepr = mac.toString();
 
             assertTrue(stringRepr + " expected to be a locally assigned address",
@@ -199,7 +201,7 @@
         }
 
         for (int i = 0; i < iterations; i++) {
-            MacAddress mac = MacAddress.createRandomUnicastAddress();
+            MacAddress mac = MacAddressUtils.createRandomUnicastAddress();
             String stringRepr = mac.toString();
 
             assertTrue(stringRepr + " expected to be a locally assigned address",
diff --git a/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java b/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java
new file mode 100644
index 0000000..47afed4
--- /dev/null
+++ b/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package android.net;
+
+import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
+
+import static org.junit.Assert.assertEquals;
+
+import android.telephony.SubscriptionManager;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link android.net.TelephonyNetworkSpecifier}.
+ */
+@SmallTest
+public class TelephonyNetworkSpecifierTest {
+    private static final int TEST_SUBID = 5;
+
+    /**
+     * Validate that IllegalArgumentException will be thrown if build TelephonyNetworkSpecifier
+     * without calling {@link TelephonyNetworkSpecifier.Builder#setSubscriptionId(int)}.
+     */
+    @Test
+    public void testBuilderBuildWithDefault() {
+        try {
+            new TelephonyNetworkSpecifier.Builder().build();
+        } catch (IllegalArgumentException iae) {
+            // expected, test pass
+        }
+    }
+
+    /**
+     * Validate that no exception will be thrown even if pass invalid subscription id to
+     * {@link TelephonyNetworkSpecifier.Builder#setSubscriptionId(int)}.
+     */
+    @Test
+    public void testBuilderBuildWithInvalidSubId() {
+        TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier.Builder()
+                .setSubscriptionId(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+                .build();
+        assertEquals(specifier.getSubscriptionId(), SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+    }
+
+    /**
+     * Validate the correctness of TelephonyNetworkSpecifier when provide valid subId.
+     */
+    @Test
+    public void testBuilderBuildWithValidSubId() {
+        final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier.Builder()
+                .setSubscriptionId(TEST_SUBID)
+                .build();
+        assertEquals(TEST_SUBID, specifier.getSubscriptionId());
+    }
+
+    /**
+     * Validate that parcel marshalling/unmarshalling works.
+     */
+    @Test
+    public void testParcel() {
+        TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier.Builder()
+                .setSubscriptionId(TEST_SUBID)
+                .build();
+        assertParcelSane(specifier, 1 /* fieldCount */);
+    }
+}
diff --git a/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java b/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
index b783467..de1028c 100644
--- a/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
+++ b/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
@@ -51,6 +51,7 @@
 import android.net.NetworkPolicyManager;
 import android.net.NetworkTemplate;
 import android.net.StringNetworkSpecifier;
+import android.net.TelephonyNetworkSpecifier;
 import android.os.Handler;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
@@ -229,7 +230,7 @@
         verify(mCM).registerNetworkCallback(any(), networkCallback.capture(), any());
 
         // Simulate callback after capability changes
-        final NetworkCapabilities capabilities = new NetworkCapabilities()
+        NetworkCapabilities capabilities = new NetworkCapabilities()
                 .addCapability(NET_CAPABILITY_INTERNET)
                 .addTransportType(TRANSPORT_CELLULAR)
                 .setNetworkSpecifier(new StringNetworkSpecifier("234"));
@@ -239,6 +240,19 @@
         networkCallback.getValue().onCapabilitiesChanged(
                 TEST_NETWORK,
                 capabilities);
+
+        // make sure it also works with the new introduced  TelephonyNetworkSpecifier
+        capabilities = new NetworkCapabilities()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .addTransportType(TRANSPORT_CELLULAR)
+                .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
+                        .setSubscriptionId(234).build());
+        if (!roaming) {
+            capabilities.addCapability(NET_CAPABILITY_NOT_ROAMING);
+        }
+        networkCallback.getValue().onCapabilitiesChanged(
+                TEST_NETWORK,
+                capabilities);
     }
 
     @Test
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index ee2b575..d4fd903 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -29,6 +29,7 @@
 import android.net.ProxyInfo;
 import android.net.StaticIpConfiguration;
 import android.net.Uri;
+import android.net.util.MacAddressUtils;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -1166,7 +1167,7 @@
      * @return true if mac is good to use
      */
     public static boolean isValidMacAddressForRandomization(MacAddress mac) {
-        return mac != null && !mac.isMulticastAddress() && mac.isLocallyAssigned()
+        return mac != null && !MacAddressUtils.isMulticastAddress(mac) && mac.isLocallyAssigned()
                 && !MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS).equals(mac);
     }
 
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 8689a38..0ef75aa 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -27,6 +27,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.net.MacAddress;
+import android.net.util.MacAddressUtils;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
 import android.os.Parcel;
@@ -63,7 +64,7 @@
         config.updateIdentifier = "1234";
         config.fromWifiNetworkSpecifier = true;
         config.fromWifiNetworkSuggestion = true;
-        config.setRandomizedMacAddress(MacAddress.createRandomUnicastAddress());
+        config.setRandomizedMacAddress(MacAddressUtils.createRandomUnicastAddress());
         MacAddress macBeforeParcel = config.getRandomizedMacAddress();
         Parcel parcelW = Parcel.obtain();
         config.writeToParcel(parcelW, 0);
@@ -169,7 +170,7 @@
         MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
         assertEquals(defaultMac, config.getRandomizedMacAddress());
 
-        MacAddress macToChangeInto = MacAddress.createRandomUnicastAddress();
+        MacAddress macToChangeInto = MacAddressUtils.createRandomUnicastAddress();
         config.setRandomizedMacAddress(macToChangeInto);
         MacAddress macAfterChange = config.getRandomizedMacAddress();