Merge "Fix sizes of audio recording indicator"
diff --git a/Android.bp b/Android.bp
index eb9928c..0e3d374 100644
--- a/Android.bp
+++ b/Android.bp
@@ -221,7 +221,6 @@
         ":framework-sax-sources",
         ":framework-telecomm-sources",
         ":framework-telephony-common-sources",
-        ":framework-telephony-sources",
         ":framework-wifi-annotations",
         ":framework-wifi-non-updatable-sources",
         ":PacProcessor-aidl-sources",
@@ -256,6 +255,9 @@
         // etc.
         ":framework-javastream-protos",
         ":framework-statslog-gen",
+
+        // telephony annotations
+        ":framework-telephony-annotations",
     ],
 }
 
@@ -268,7 +270,9 @@
         ":framework-tethering-srcs",
         ":updatable-media-srcs",
         ":framework-mediaprovider-sources",
+        ":framework-permission-sources",
         ":framework-wifi-updatable-sources",
+        ":framework-telephony-sources",
         ":ike-srcs",
     ]
 }
@@ -302,7 +306,6 @@
             "rs/java",
             "sax/java",
             "telecomm/java",
-            "telephony/java",
             "wifi/java",
         ],
     },
@@ -382,6 +385,7 @@
         "updatable_media_stubs",
         "framework_mediaprovider_stubs",
         "framework-tethering",
+        "framework-telephony-stubs",
     ],
 
     jarjar_rules: ":framework-jarjar-rules",
@@ -438,8 +442,9 @@
     srcs: [":framework-non-updatable-sources"],
     libs: [
         "framework-appsearch-stubs",
-        // TODO(b/146167933): Use framework-statsd-stubs
-        "framework-statsd",
+        "framework-sdkextensions-stubs-systemapi",
+        "framework-statsd", // TODO(b/146167933): Use framework-statsd-stubs
+        "framework-permission-stubs",
         "framework-wifi-stubs",
         "ike-stubs",
     ],
@@ -465,7 +470,9 @@
         "//frameworks/base/apex/appsearch/framework",
         "//frameworks/base/apex/blobstore/framework",
         "//frameworks/base/apex/jobscheduler/framework",
+        "//frameworks/base/apex/permission/framework",
         "//frameworks/base/apex/statsd/service",
+        "//frameworks/base/telephony",
         "//frameworks/base/wifi",
         "//frameworks/opt/net/wifi/service",
     ],
@@ -489,6 +496,7 @@
         "updatable_media_stubs",
         "framework_mediaprovider_stubs",
         "framework-appsearch", // TODO(b/146218515): should be framework-appsearch-stubs
+        "framework-permission-stubs",
         "framework-sdkextensions-stubs-systemapi",
         // TODO(b/146167933): Use framework-statsd-stubs instead.
         "framework-statsd",
@@ -497,6 +505,9 @@
         "ike-stubs",
         // TODO(b/147200698): should be the stub of framework-tethering
         "framework-tethering",
+        // TODO (b/147688669) should be framework-telephony-stubs
+        "framework-telephony",
+        // TODO(jiyong): add stubs for APEXes here
     ],
     sdk_version: "core_platform",
     apex_available: ["//apex_available:platform"],
@@ -1178,6 +1189,7 @@
         "core/java/com/android/internal/util/Protocol.java",
         "core/java/com/android/internal/util/Preconditions.java",
         "telephony/java/android/telephony/Annotation.java",
+	":net-utils-framework-wifi-common-srcs",
     ],
     libs: [
         "framework-annotations-lib",
@@ -1222,3 +1234,82 @@
     "StubLibraries.bp",
     "ApiDocs.bp",
 ]
+
+// TODO(b/147699819): move to frameworks/base/telephony/ folder
+droidstubs {
+    name: "framework-telephony-stubs-srcs",
+    srcs: [
+        ":framework-telephony-sources",
+        ":framework_native_aidl",
+        ":framework-javastream-protos",
+    ],
+    aidl: {
+        local_include_dirs: [
+            "core/java",
+            "telecomm/java"
+        ],
+    },
+    libs: [
+        "framework-annotations-lib",
+        "android.hardware.radio-V1.5-java",
+    ],
+    defaults: ["framework-module-stubs-defaults-systemapi"],
+    filter_packages: ["android.telephony"],
+    sdk_version: "system_current",
+}
+
+java_library {
+    name: "framework-telephony-stubs",
+    srcs: [":framework-telephony-stubs-srcs"],
+    // TODO(b/147699819): move public aidls to a separate folder and potentially remove
+    // below aidl exports.
+    aidl: {
+        export_include_dirs: ["telephony/java"],
+    },
+    sdk_version: "system_current",
+}
+
+java_library {
+    name: "framework-telephony",
+    srcs: [
+        ":framework-telephony-sources",
+    ],
+    // TODO: change to framework-system-stub to build against system APIs.
+    libs: [
+        "framework-minus-apex",
+        "unsupportedappusage",
+    ],
+    static_libs: [
+        "libphonenumber-platform",
+        "app-compat-annotations",
+    ],
+    sdk_version: "core_platform",
+    aidl: {
+        export_include_dirs: ["telephony/java"],
+        include_dirs: [
+            "frameworks/native/aidl/binder",
+            "frameworks/native/aidl/gui",
+        ]
+    },
+    jarjar_rules: ":telephony-framework-jarjar-rules",
+    dxflags: [
+        "--core-library",
+        "--multi-dex",
+    ],
+    // This is to break the dependency from boot jars.
+    dex_preopt: {
+        enabled: false,
+    },
+    installable: true,
+}
+
+filegroup {
+    // TODO (b/147690217): move to frameworks/base/telephony/common.
+    name: "framework-telephony-annotations",
+    srcs: ["telephony/java/android/telephony/Annotation.java"],
+}
+
+filegroup {
+    name: "telephony-framework-jarjar-rules",
+    srcs: ["telephony/framework-telephony-jarjar-rules.txt"],
+}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index baa3c61..cdc0d32 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -51,6 +51,10 @@
         ":core_public_api_files",
         ":ike-api-srcs",
     ],
+    // TODO(b/147699819): remove below aidl includes.
+    aidl: {
+        local_include_dirs: ["telephony/java"],
+    },
     libs: ["framework-internal-utils"],
     installable: false,
     annotations_enabled: true,
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index 83195dc..0aa685d 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -25,9 +25,12 @@
 import com.android.internal.infra.AndroidFuture;
 
 import com.google.android.icing.proto.SchemaProto;
+import com.google.android.icing.proto.SearchResultProto;
+import com.google.android.icing.protobuf.InvalidProtocolBufferException;
 
 import java.util.List;
 import java.util.concurrent.Executor;
+import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
 /**
@@ -127,4 +130,94 @@
         // TODO(b/147614371) Fix error report for multiple documents.
         future.whenCompleteAsync((noop, err) -> callback.accept(err), executor);
     }
+
+    /**
+     * This method searches for documents based on a given query string. It also accepts
+     * specifications regarding how to search and format the results.
+     *
+     *<p>Currently we support following features in the raw query format:
+     * <ul>
+     *     <li>AND
+     *     AND joins (e.g. “match documents that have both the terms ‘dog’ and
+     *     ‘cat’”).
+     *     Example: hello world matches documents that have both ‘hello’ and ‘world’
+     *     <li>OR
+     *     OR joins (e.g. “match documents that have either the term ‘dog’ or
+     *     ‘cat’”).
+     *     Example: dog OR puppy
+     *     <li>Exclusion
+     *     Exclude a term (e.g. “match documents that do
+     *     not have the term ‘dog’”).
+     *     Example: -dog excludes the term ‘dog’
+     *     <li>Grouping terms
+     *     Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g.
+     *     “match documents that have either ‘dog’ or ‘puppy’, and either ‘cat’ or ‘kitten’”).
+     *     Example: (dog puppy) (cat kitten) two one group containing two terms.
+     *     <li>Property restricts
+     *      which properties of a document to specifically match terms in (e.g.
+     *     “match documents where the ‘subject’ property contains ‘important’”).
+     *     Example: subject:important matches documents with the term ‘important’ in the
+     *     ‘subject’ property
+     *     <li>Schema type restricts
+     *     This is similar to property restricts, but allows for restricts on top-level document
+     *     fields, such as schema_type. Clients should be able to limit their query to documents of
+     *     a certain schema_type (e.g. “match documents that are of the ‘Email’ schema_type”).
+     *     Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will match documents
+     *     that contain the query term ‘dog’ and are of either the ‘Email’ schema type or the
+     *     ‘Video’ schema type.
+     * </ul>
+     *
+     * <p> It is strongly recommended to use Jetpack APIs.
+     *
+     * @param queryExpression Query String to search.
+     * @param searchSpec Spec for setting filters, raw query etc.
+     * @param executor Executor on which to invoke the callback.
+     * @param callback  Callback to receive errors resulting from the query operation. If the
+     *                 operation succeeds, the callback will be invoked with {@code null}.
+     * @hide
+     */
+    @NonNull
+    public void query(
+            @NonNull String queryExpression,
+            @NonNull SearchSpec searchSpec,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull BiConsumer<? super SearchResults, ? super Throwable> callback) {
+        AndroidFuture<byte[]> future = new AndroidFuture<>();
+        future.whenCompleteAsync((searchResultBytes, err) -> {
+            if (err != null) {
+                callback.accept(null, err);
+                return;
+            }
+
+            if (searchResultBytes != null) {
+                SearchResultProto searchResultProto;
+                try {
+                    searchResultProto = SearchResultProto.parseFrom(searchResultBytes);
+                } catch (InvalidProtocolBufferException e) {
+                    callback.accept(null, e);
+                    return;
+                }
+                if (searchResultProto.hasError()) {
+                    // TODO(sidchhabra): Add better exception handling.
+                    callback.accept(
+                            null,
+                            new RuntimeException(searchResultProto.getError().getErrorMessage()));
+                    return;
+                }
+                SearchResults searchResults = new SearchResults(searchResultProto);
+                callback.accept(searchResults, null);
+                return;
+            }
+
+            // Nothing was supplied in the future at all
+            callback.accept(
+                    null, new IllegalStateException("Unknown failure occurred while querying"));
+        }, executor);
+
+        try {
+            mService.query(queryExpression, searchSpec.getProto().toByteArray(), future);
+        } catch (RemoteException e) {
+            future.completeExceptionally(e);
+        }
+    }
 }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index fc83d8c..22250f4 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -1,5 +1,5 @@
 /**
- * Copyright 2019, The Android Open Source Project
+ * Copyright 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.
@@ -29,4 +29,13 @@
      */
     void setSchema(in byte[] schemaProto, in AndroidFuture callback);
     void put(in byte[] documentBytes, in AndroidFuture callback);
+    /**
+     * Searches a document based on a given query string.
+     *
+     * @param queryExpression Query String to search.
+     * @param searchSpec Serialized SearchSpecProto.
+     * @param callback {@link AndroidFuture}. Will be completed with a serialized
+     *     {@link SearchResultsProto}, or completed exceptionally if query fails.
+     */
+     void query(in String queryExpression, in byte[] searchSpecBytes, in AndroidFuture callback);
 }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IllegalSearchSpecException.java b/apex/appsearch/framework/java/android/app/appsearch/IllegalSearchSpecException.java
new file mode 100644
index 0000000..0d029f0
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/IllegalSearchSpecException.java
@@ -0,0 +1,36 @@
+/*
+ * 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.app.appsearch;
+
+import android.annotation.NonNull;
+
+/**
+ * Indicates that a {@link android.app.appsearch.SearchResults} has logical inconsistencies such
+ * as unpopulated mandatory fields or illegal combinations of parameters.
+ *
+ * @hide
+ */
+public class IllegalSearchSpecException extends IllegalArgumentException {
+    /**
+     * Constructs a new {@link IllegalSearchSpecException}.
+     *
+     * @param message A developer-readable description of the issue with the bundle.
+     */
+    public IllegalSearchSpecException(@NonNull String message) {
+        super(message);
+    }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
new file mode 100644
index 0000000..ec4258d
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
@@ -0,0 +1,89 @@
+/*
+ * 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.app.appsearch;
+
+import android.annotation.NonNull;
+
+import com.google.android.icing.proto.SearchResultProto;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * SearchResults are a list of results that are returned from a query. Each result from this
+ * list contains a document and may contain other fields like snippets based on request.
+ * @hide
+ */
+public final class SearchResults {
+
+    private final SearchResultProto mSearchResultProto;
+
+    /** @hide */
+    public SearchResults(SearchResultProto searchResultProto) {
+        mSearchResultProto = searchResultProto;
+    }
+
+    /**
+     * This class represents the result obtained from the query. It will contain the document which
+     * which matched the specified query string and specifications.
+     * @hide
+     */
+    public static final class Result {
+        private final SearchResultProto.ResultProto mResultProto;
+
+        private Result(SearchResultProto.ResultProto resultProto) {
+            mResultProto = resultProto;
+        }
+
+        /**
+         * Contains the matching {@link AppSearch.Document}.
+         * @return Document object which matched the query.
+         * @hide
+         */
+        // TODO(sidchhabra): Switch to Document constructor that takes proto.
+        @NonNull
+        public AppSearch.Document getDocument() {
+            return AppSearch.Document.newBuilder(mResultProto.getDocument().getUri(),
+                    mResultProto.getDocument().getSchema())
+                    .setCreationTimestampSecs(mResultProto.getDocument().getCreationTimestampSecs())
+                    .setScore(mResultProto.getDocument().getScore())
+                    .build();
+        }
+
+        // TODO(sidchhabra): Add Getter for ResultReader for Snippet.
+    }
+
+    @Override
+    public String toString() {
+        return mSearchResultProto.toString();
+    }
+
+    /**
+     * Returns a {@link Result} iterator. Returns Empty Iterator if there are no matching results.
+     * @hide
+     */
+    @NonNull
+    public Iterator<Result> getResults() {
+        List<Result> results = new ArrayList<>();
+        // TODO(sidchhabra): Pass results using a RemoteStream.
+        for (SearchResultProto.ResultProto resultProto : mSearchResultProto.getResultsList()) {
+            results.add(new Result(resultProto));
+        }
+        return results.iterator();
+    }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java
new file mode 100644
index 0000000..5df7108
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java
@@ -0,0 +1,127 @@
+/*
+ * 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.app.appsearch;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import com.google.android.icing.proto.SearchSpecProto;
+import com.google.android.icing.proto.TermMatchType;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class represents the specification logic for AppSearch. It can be used to set the type of
+ * search, like prefix or exact only or apply filters to search for a specific schema type only etc.
+ * @hide
+ *
+ */
+// TODO(sidchhabra) : AddResultSpec fields for Snippets etc.
+public final class SearchSpec {
+
+    private final SearchSpecProto mSearchSpecProto;
+
+    private SearchSpec(SearchSpecProto searchSpecProto) {
+        mSearchSpecProto = searchSpecProto;
+    }
+
+    /** Creates a new {@link SearchSpec.Builder}. */
+    @NonNull
+    public static SearchSpec.Builder newBuilder() {
+        return new SearchSpec.Builder();
+    }
+
+    /** @hide */
+    @NonNull
+    SearchSpecProto getProto() {
+        return mSearchSpecProto;
+    }
+
+    /** Term Match Type for the query. */
+    // NOTE: The integer values of these constants must match the proto enum constants in
+    // {@link com.google.android.icing.proto.SearchSpecProto.termMatchType}
+    @IntDef(prefix = {"TERM_MATCH_TYPE_"}, value = {
+            TERM_MATCH_TYPE_EXACT_ONLY,
+            TERM_MATCH_TYPE_PREFIX
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TermMatchTypeCode {}
+
+    public static final int TERM_MATCH_TYPE_EXACT_ONLY = 1;
+    public static final int TERM_MATCH_TYPE_PREFIX = 2;
+
+    /** Builder for {@link SearchSpec objects}. */
+    public static final class Builder {
+
+        private final SearchSpecProto.Builder mBuilder = SearchSpecProto.newBuilder();
+
+        private Builder(){}
+
+        /**
+         * Indicates how the query terms should match {@link TermMatchTypeCode} in the index.
+         *
+         *   TermMatchType.Code=EXACT_ONLY
+         *   Query terms will only match exact tokens in the index.
+         *   Ex. A query term "foo" will only match indexed token "foo", and not "foot"
+         *   or "football"
+         *
+         *   TermMatchType.Code=PREFIX
+         *   Query terms will match indexed tokens when the query term is a prefix of
+         *   the token.
+         *   Ex. A query term "foo" will match indexed tokens like "foo", "foot", and
+         *   "football".
+         */
+        @NonNull
+        public Builder setTermMatchType(@TermMatchTypeCode int termMatchTypeCode) {
+            TermMatchType.Code termMatchTypeCodeProto =
+                    TermMatchType.Code.forNumber(termMatchTypeCode);
+            if (termMatchTypeCodeProto == null) {
+                throw new IllegalArgumentException("Invalid term match type: " + termMatchTypeCode);
+            }
+            mBuilder.setTermMatchType(termMatchTypeCodeProto);
+            return this;
+        }
+
+        /**
+         * Adds a Schema type filter to {@link SearchSpec} Entry.
+         * Only search for documents that have the specified schema types.
+         * If unset, the query will search over all schema types.
+         */
+        @NonNull
+        public Builder setSchemaTypes(@NonNull String... schemaTypes) {
+            for (String schemaType : schemaTypes) {
+                mBuilder.addSchemaTypeFilters(schemaType);
+            }
+            return this;
+        }
+
+        /**
+         * Constructs a new {@link SearchSpec} from the contents of this builder.
+         *
+         * <p>After calling this method, the builder must no longer be used.
+         */
+        @NonNull
+        public SearchSpec build() {
+            if (mBuilder.getTermMatchType() == TermMatchType.Code.UNKNOWN) {
+                throw new IllegalSearchSpecException("Missing termMatchType field.");
+            }
+            return new SearchSpec(mBuilder.build());
+        }
+    }
+
+}
diff --git a/apex/appsearch/service/Android.bp b/apex/appsearch/service/Android.bp
index 7327231..04f385e 100644
--- a/apex/appsearch/service/Android.bp
+++ b/apex/appsearch/service/Android.bp
@@ -20,6 +20,5 @@
         "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 042f051..f63abd9 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -15,13 +15,23 @@
  */
 package com.android.server.appsearch;
 
+import android.annotation.NonNull;
 import android.app.appsearch.IAppSearchManager;
 import android.content.Context;
+import android.os.Binder;
+import android.os.UserHandle;
 
 import com.android.internal.infra.AndroidFuture;
+import com.android.internal.util.Preconditions;
 import com.android.server.SystemService;
+import com.android.server.appsearch.impl.AppSearchImpl;
+import com.android.server.appsearch.impl.FakeIcing;
+import com.android.server.appsearch.impl.ImplInstanceManager;
 
 import com.google.android.icing.proto.SchemaProto;
+import com.google.android.icing.proto.SearchResultProto;
+import com.google.android.icing.proto.SearchSpecProto;
+import com.google.android.icing.protobuf.InvalidProtocolBufferException;
 
 /**
  * TODO(b/142567528): add comments when implement this class
@@ -30,8 +40,11 @@
 
     public AppSearchManagerService(Context context) {
         super(context);
+        mFakeIcing = new FakeIcing();
     }
 
+    private final FakeIcing mFakeIcing;
+
     @Override
     public void onStart() {
         publishBinderService(Context.APP_SEARCH_SERVICE, new Stub());
@@ -40,12 +53,20 @@
     private class Stub extends IAppSearchManager.Stub {
         @Override
         public void setSchema(byte[] schemaBytes, AndroidFuture callback) {
+            Preconditions.checkNotNull(schemaBytes);
+            Preconditions.checkNotNull(callback);
+            int callingUid = Binder.getCallingUidOrThrow();
+            int callingUserId = UserHandle.getUserId(callingUid);
+            long callingIdentity = Binder.clearCallingIdentity();
             try {
                 SchemaProto schema = SchemaProto.parseFrom(schemaBytes);
-                throw new UnsupportedOperationException("setSchema not yet implemented: " + schema);
-
+                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
+                impl.setSchema(callingUid, schema);
+                callback.complete(null);
             } catch (Throwable t) {
                 callback.completeExceptionally(t);
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
             }
         }
 
@@ -57,5 +78,22 @@
                 callback.completeExceptionally(t);
             }
         }
+        // TODO(sidchhabra):Init FakeIcing properly.
+        // TODO(sidchhabra): Do this in a threadpool.
+        @Override
+        public void query(@NonNull String queryExpression, @NonNull byte[] searchSpec,
+                AndroidFuture callback) {
+            Preconditions.checkNotNull(queryExpression);
+            Preconditions.checkNotNull(searchSpec);
+            SearchSpecProto searchSpecProto = null;
+            try {
+                searchSpecProto = SearchSpecProto.parseFrom(searchSpec);
+            } catch (InvalidProtocolBufferException e) {
+                throw new RuntimeException(e);
+            }
+            SearchResultProto searchResults =
+                    mFakeIcing.query(queryExpression);
+            callback.complete(searchResults.toByteArray());
+        }
     }
 }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/impl/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/impl/AppSearchImpl.java
new file mode 100644
index 0000000..7c97b0b
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/impl/AppSearchImpl.java
@@ -0,0 +1,110 @@
+/*
+ * 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.appsearch.impl;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.Context;
+
+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;
+
+/**
+ * Manages interaction with {@link FakeIcing} and other components to implement AppSearch
+ * functionality.
+ */
+public final class AppSearchImpl {
+    private final Context mContext;
+    private final @UserIdInt int mUserId;
+    private final FakeIcing mFakeIcing = new FakeIcing();
+
+    AppSearchImpl(@NonNull Context context, @UserIdInt int userId) {
+        mContext = context;
+        mUserId = userId;
+    }
+
+    /**
+     * Updates the AppSearch schema for this app.
+     *
+     * @param callingUid The uid of the app calling AppSearch.
+     * @param origSchema The schema to set for this app.
+     */
+    public void setSchema(int callingUid, @NonNull SchemaProto origSchema) {
+        // Rewrite schema type names to include the calling app's package and uid.
+        String typePrefix = getTypePrefix(callingUid);
+        SchemaProto.Builder schemaBuilder = origSchema.toBuilder();
+        rewriteSchemaTypes(typePrefix, schemaBuilder);
+
+        // TODO(b/145635424): Save in schema type map
+        // TODO(b/145635424): Apply the schema to Icing and report results
+    }
+
+    /**
+     * Rewrites all types mentioned in the given {@code schemaBuilder} to prepend
+     * {@code typePrefix}.
+     *
+     * @param typePrefix The prefix to add
+     * @param schemaBuilder The schema to mutate
+     */
+    @VisibleForTesting
+    void rewriteSchemaTypes(
+            @NonNull String typePrefix, @NonNull SchemaProto.Builder schemaBuilder) {
+        for (int typeIdx = 0; typeIdx < schemaBuilder.getTypesCount(); typeIdx++) {
+            SchemaTypeConfigProto.Builder typeConfigBuilder =
+                    schemaBuilder.getTypes(typeIdx).toBuilder();
+
+            // Rewrite SchemaProto.types.schema_type
+            String newSchemaType = typePrefix + typeConfigBuilder.getSchemaType();
+            typeConfigBuilder.setSchemaType(newSchemaType);
+
+            // Rewrite SchemaProto.types.properties.schema_type
+            for (int propertyIdx = 0;
+                    propertyIdx < typeConfigBuilder.getPropertiesCount();
+                    propertyIdx++) {
+                PropertyConfigProto.Builder propertyConfigBuilder =
+                        typeConfigBuilder.getProperties(propertyIdx).toBuilder();
+                if (!propertyConfigBuilder.getSchemaType().isEmpty()) {
+                    String newPropertySchemaType =
+                            typePrefix + propertyConfigBuilder.getSchemaType();
+                    propertyConfigBuilder.setSchemaType(newPropertySchemaType);
+                    typeConfigBuilder.setProperties(propertyIdx, propertyConfigBuilder);
+                }
+            }
+
+            schemaBuilder.setTypes(typeIdx, typeConfigBuilder);
+        }
+    }
+
+    /**
+     * Returns a type prefix in a format like {@code com.example.package@1000/} or
+     * {@code com.example.sharedname:5678@1000/}.
+     */
+    @NonNull
+    private String getTypePrefix(int callingUid) {
+        // For regular apps, this call will return the package name. If callingUid is an
+        // android:sharedUserId, this value may be another type of name and have a :uid suffix.
+        String callingUidName = mContext.getPackageManager().getNameForUid(callingUid);
+        if (callingUidName == null) {
+            // Not sure how this is possible --- maybe app was uninstalled?
+            throw new IllegalStateException("Failed to look up package name for uid " + callingUid);
+        }
+        return callingUidName + "@" + mUserId + "/";
+    }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/impl/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/impl/ImplInstanceManager.java
new file mode 100644
index 0000000..395e30e
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/impl/ImplInstanceManager.java
@@ -0,0 +1,56 @@
+/*
+ * 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.appsearch.impl;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.util.SparseArray;
+
+/**
+ * Manages the lifecycle of instances of {@link AppSearchImpl}.
+ *
+ * <p>These instances are managed per unique device-user.
+ */
+public final class ImplInstanceManager {
+    private static final SparseArray<AppSearchImpl> sInstances = new SparseArray<>();
+
+    /**
+     * Gets an instance of AppSearchImpl for the given user.
+     *
+     * <p>If no AppSearchImpl instance exists for this user, Icing will be initialized and one will
+     * be created.
+     *
+     * @param context The Android context
+     * @param userId The multi-user userId of the device user calling AppSearch
+     * @return An initialized {@link AppSearchImpl} for this user
+     */
+    @NonNull
+    public static AppSearchImpl getInstance(@NonNull Context context, @UserIdInt int userId) {
+        AppSearchImpl instance = sInstances.get(userId);
+        if (instance == null) {
+            synchronized (ImplInstanceManager.class) {
+                instance = sInstances.get(userId);
+                if (instance == null) {
+                    instance = new AppSearchImpl(context, userId);
+                    sInstances.put(userId, instance);
+                }
+            }
+        }
+        return instance;
+    }
+}
diff --git a/apex/permission/Android.bp b/apex/permission/Android.bp
index f232e62..d746ea6 100644
--- a/apex/permission/Android.bp
+++ b/apex/permission/Android.bp
@@ -22,6 +22,10 @@
     name: "com.android.permission-defaults",
     key: "com.android.permission.key",
     certificate: ":com.android.permission.certificate",
+    java_libs: [
+        "framework-permission",
+        "service-permission",
+    ],
     apps: ["PermissionController"],
 }
 
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
new file mode 100644
index 0000000..8b03da3
--- /dev/null
+++ b/apex/permission/framework/Android.bp
@@ -0,0 +1,60 @@
+// 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.
+
+filegroup {
+    name: "framework-permission-sources",
+    srcs: [
+        "java/**/*.java",
+        "java/**/*.aidl",
+    ],
+    path: "java",
+}
+
+java_library {
+    name: "framework-permission",
+    srcs: [
+        ":framework-permission-sources",
+    ],
+    sdk_version: "system_current",
+    apex_available: [
+        "com.android.permission",
+        "test_com.android.permission",
+    ],
+    hostdex: true,
+    installable: true,
+    visibility: [
+        "//frameworks/base/apex/permission:__subpackages__",
+    ],
+}
+
+droidstubs {
+    name: "framework-permission-stubs-sources",
+    srcs: [
+        ":framework-annotations",
+        ":framework-permission-sources",
+    ],
+    sdk_version: "system_current",
+    defaults: [
+        "framework-module-stubs-defaults-systemapi",
+    ],
+}
+
+java_library {
+    name: "framework-permission-stubs",
+    srcs: [
+        ":framework-permission-stubs-sources",
+    ],
+    sdk_version: "system_current",
+    installable: false,
+}
diff --git a/apex/permission/framework/java/android/permission/PermissionState.java b/apex/permission/framework/java/android/permission/PermissionState.java
new file mode 100644
index 0000000..e810db8
--- /dev/null
+++ b/apex/permission/framework/java/android/permission/PermissionState.java
@@ -0,0 +1,22 @@
+/*
+ * 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.permission;
+
+/**
+ * @hide
+ */
+public class PermissionState {}
diff --git a/apex/permission/service/Android.bp b/apex/permission/service/Android.bp
new file mode 100644
index 0000000..972b362
--- /dev/null
+++ b/apex/permission/service/Android.bp
@@ -0,0 +1,29 @@
+// 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.
+
+java_library {
+    name: "service-permission",
+    srcs: [
+        "java/**/*.java",
+    ],
+    sdk_version: "system_current",
+    libs: [
+        "framework-permission",
+    ],
+    apex_available: [
+        "com.android.permission",
+        "test_com.android.permission",
+    ],
+    installable: true,
+}
diff --git a/apex/permission/service/java/com/android/server/permission/RuntimePermissionPersistence.java b/apex/permission/service/java/com/android/server/permission/RuntimePermissionPersistence.java
new file mode 100644
index 0000000..a534e22
--- /dev/null
+++ b/apex/permission/service/java/com/android/server/permission/RuntimePermissionPersistence.java
@@ -0,0 +1,22 @@
+/*
+ * 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.permission;
+
+/**
+ * Persistence for runtime permissions.
+ */
+public class RuntimePermissionPersistence {}
diff --git a/apex/statsd/aidl/android/os/IStatsd.aidl b/apex/statsd/aidl/android/os/IStatsd.aidl
index c409f51..0ecf2f0 100644
--- a/apex/statsd/aidl/android/os/IStatsd.aidl
+++ b/apex/statsd/aidl/android/os/IStatsd.aidl
@@ -212,12 +212,17 @@
     *
     * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS
     */
-   oneway void unregisterPullerCallback(int atomTag, String packageName);
+    oneway void unregisterPullerCallback(int atomTag, String packageName);
 
-  /**
-   * Unregisters any pullAtomCallback for the given uid/atom.
-   */
-   oneway void unregisterPullAtomCallback(int uid, int atomTag);
+    /**
+     * Unregisters any pullAtomCallback for the given uid/atom.
+     */
+    oneway void unregisterPullAtomCallback(int uid, int atomTag);
+
+    /**
+     * Unregisters any pullAtomCallback for the given atom.
+     */
+    oneway void unregisterNativePullAtomCallback(int atomTag);
 
     /**
      * The install requires staging.
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 6444b4e..17573bb 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -24,11 +24,11 @@
 import static android.os.storage.VolumeInfo.TYPE_PUBLIC;
 
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
-import static com.android.server.stats.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
-import static com.android.server.stats.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
-import static com.android.server.stats.ProcfsMemoryUtil.forEachPid;
-import static com.android.server.stats.ProcfsMemoryUtil.readCmdlineFromProcfs;
-import static com.android.server.stats.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
+import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
+import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
+import static com.android.server.stats.pull.ProcfsMemoryUtil.forEachPid;
+import static com.android.server.stats.pull.ProcfsMemoryUtil.readCmdlineFromProcfs;
+import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -135,8 +135,8 @@
 import com.android.server.am.MemoryStatUtil.MemoryStat;
 import com.android.server.notification.NotificationManagerService;
 import com.android.server.role.RoleManagerInternal;
-import com.android.server.stats.IonMemoryUtil.IonAllocations;
-import com.android.server.stats.ProcfsMemoryUtil.MemorySnapshot;
+import com.android.server.stats.pull.IonMemoryUtil.IonAllocations;
+import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot;
 import com.android.server.storage.DiskStatsFileLogger;
 import com.android.server.storage.DiskStatsLoggingService;
 
@@ -714,111 +714,6 @@
         }
     }
 
-    /**
-     * Helper method to extract the Parcelable controller info from a
-     * SynchronousResultReceiver.
-     */
-    private static <T extends Parcelable> T awaitControllerInfo(
-            @Nullable SynchronousResultReceiver receiver) {
-        if (receiver == null) {
-            return null;
-        }
-
-        try {
-            final SynchronousResultReceiver.Result result =
-                    receiver.awaitResult(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS);
-            if (result.bundle != null) {
-                // This is the final destination for the Bundle.
-                result.bundle.setDefusable(true);
-
-                final T data = result.bundle.getParcelable(
-                        RESULT_RECEIVER_CONTROLLER_KEY);
-                if (data != null) {
-                    return data;
-                }
-            }
-            Slog.e(TAG, "no controller energy info supplied for " + receiver.getName());
-        } catch (TimeoutException e) {
-            Slog.w(TAG, "timeout reading " + receiver.getName() + " stats");
-        }
-        return null;
-    }
-
-    private void pullWifiActivityInfo(
-            int tagId, long elapsedNanos, long wallClockNanos,
-            List<StatsLogEventWrapper> pulledData) {
-        WifiManager wifiManager;
-        synchronized (this) {
-            if (mWifiManager == null) {
-                mWifiManager = mContext.getSystemService(WifiManager.class);
-            }
-            wifiManager = mWifiManager;
-        }
-        if (wifiManager == null) {
-            return;
-        }
-        long token = Binder.clearCallingIdentity();
-        try {
-            SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
-            wifiManager.getWifiActivityEnergyInfoAsync(
-                    new Executor() {
-                        @Override
-                        public void execute(Runnable runnable) {
-                            // run the listener on the binder thread, if it was run on the main
-                            // thread it would deadlock since we would be waiting on ourselves
-                            runnable.run();
-                        }
-                    },
-                    info -> {
-                        Bundle bundle = new Bundle();
-                        bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info);
-                        wifiReceiver.send(0, bundle);
-                    }
-            );
-            final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
-            if (wifiInfo == null) {
-                return;
-            }
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-            e.writeLong(wifiInfo.getTimeSinceBootMillis());
-            e.writeInt(wifiInfo.getStackState());
-            e.writeLong(wifiInfo.getControllerTxDurationMillis());
-            e.writeLong(wifiInfo.getControllerRxDurationMillis());
-            e.writeLong(wifiInfo.getControllerIdleDurationMillis());
-            e.writeLong(wifiInfo.getControllerEnergyUsedMicroJoules());
-            pulledData.add(e);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    private void pullModemActivityInfo(
-            int tagId, long elapsedNanos, long wallClockNanos,
-            List<StatsLogEventWrapper> pulledData) {
-        long token = Binder.clearCallingIdentity();
-        synchronized (this) {
-            if (mTelephony == null) {
-                mTelephony = mContext.getSystemService(TelephonyManager.class);
-            }
-        }
-        if (mTelephony != null) {
-            SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
-            mTelephony.requestModemActivityInfo(modemReceiver);
-            final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-            e.writeLong(modemInfo.getTimestamp());
-            e.writeLong(modemInfo.getSleepTimeMillis());
-            e.writeLong(modemInfo.getIdleTimeMillis());
-            e.writeLong(modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis());
-            e.writeLong(modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis());
-            e.writeLong(modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis());
-            e.writeLong(modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis());
-            e.writeLong(modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis());
-            e.writeLong(modemInfo.getReceiveTimeMillis());
-            pulledData.add(e);
-        }
-    }
-
     private void pullSystemElapsedRealtime(
             int tagId, long elapsedNanos, long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
@@ -827,207 +722,6 @@
         pulledData.add(e);
     }
 
-    private void pullProcessMemoryState(
-            int tagId, long elapsedNanos, long wallClockNanos,
-            List<StatsLogEventWrapper> pulledData) {
-        List<ProcessMemoryState> processMemoryStates =
-                LocalServices.getService(
-                        ActivityManagerInternal.class).getMemoryStateForProcesses();
-        for (ProcessMemoryState processMemoryState : processMemoryStates) {
-            final MemoryStat memoryStat = readMemoryStatFromFilesystem(processMemoryState.uid,
-                    processMemoryState.pid);
-            if (memoryStat == null) {
-                continue;
-            }
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-            e.writeInt(processMemoryState.uid);
-            e.writeString(processMemoryState.processName);
-            e.writeInt(processMemoryState.oomScore);
-            e.writeLong(memoryStat.pgfault);
-            e.writeLong(memoryStat.pgmajfault);
-            e.writeLong(memoryStat.rssInBytes);
-            e.writeLong(memoryStat.cacheInBytes);
-            e.writeLong(memoryStat.swapInBytes);
-            e.writeLong(-1);  // unused
-            e.writeLong(-1);  // unused
-            e.writeInt(-1);  // unsed
-            pulledData.add(e);
-        }
-    }
-
-    private void pullProcessMemoryHighWaterMark(
-            int tagId, long elapsedNanos, long wallClockNanos,
-            List<StatsLogEventWrapper> pulledData) {
-        List<ProcessMemoryState> managedProcessList =
-                LocalServices.getService(
-                        ActivityManagerInternal.class).getMemoryStateForProcesses();
-        for (ProcessMemoryState managedProcess : managedProcessList) {
-            final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid);
-            if (snapshot == null) {
-                continue;
-            }
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-            e.writeInt(managedProcess.uid);
-            e.writeString(managedProcess.processName);
-            // RSS high-water mark in bytes.
-            e.writeLong((long) snapshot.rssHighWaterMarkInKilobytes * 1024L);
-            e.writeInt(snapshot.rssHighWaterMarkInKilobytes);
-            pulledData.add(e);
-        }
-        forEachPid((pid, cmdLine) -> {
-            if (!MEMORY_INTERESTING_NATIVE_PROCESSES.contains(cmdLine)) {
-                return;
-            }
-            final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid);
-            if (snapshot == null) {
-                return;
-            }
-            // Sometimes we get here a process that is not included in the whitelist. It comes
-            // from forking the zygote for an app. We can ignore that sample because this process
-            // is collected by ProcessMemoryState.
-            if (isAppUid(snapshot.uid)) {
-                return;
-            }
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-            e.writeInt(snapshot.uid);
-            e.writeString(cmdLine);
-            // RSS high-water mark in bytes.
-            e.writeLong((long) snapshot.rssHighWaterMarkInKilobytes * 1024L);
-            e.writeInt(snapshot.rssHighWaterMarkInKilobytes);
-            pulledData.add(e);
-        });
-        // Invoke rss_hwm_reset binary to reset RSS HWM counters for all processes.
-        SystemProperties.set("sys.rss_hwm_reset.on", "1");
-    }
-
-    private void pullProcessMemorySnapshot(
-            int tagId, long elapsedNanos, long wallClockNanos,
-            List<StatsLogEventWrapper> pulledData) {
-        List<ProcessMemoryState> managedProcessList =
-                LocalServices.getService(
-                        ActivityManagerInternal.class).getMemoryStateForProcesses();
-        for (ProcessMemoryState managedProcess : managedProcessList) {
-            final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid);
-            if (snapshot == null) {
-                continue;
-            }
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-            e.writeInt(managedProcess.uid);
-            e.writeString(managedProcess.processName);
-            e.writeInt(managedProcess.pid);
-            e.writeInt(managedProcess.oomScore);
-            e.writeInt(snapshot.rssInKilobytes);
-            e.writeInt(snapshot.anonRssInKilobytes);
-            e.writeInt(snapshot.swapInKilobytes);
-            e.writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes);
-            pulledData.add(e);
-        }
-        forEachPid((pid, cmdLine) -> {
-            if (!MEMORY_INTERESTING_NATIVE_PROCESSES.contains(cmdLine)) {
-                return;
-            }
-            final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid);
-            if (snapshot == null) {
-                return;
-            }
-            // Sometimes we get here a process that is not included in the whitelist. It comes
-            // from forking the zygote for an app. We can ignore that sample because this process
-            // is collected by ProcessMemoryState.
-            if (isAppUid(snapshot.uid)) {
-                return;
-            }
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-            e.writeInt(snapshot.uid);
-            e.writeString(cmdLine);
-            e.writeInt(pid);
-            e.writeInt(-1001);  // Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.
-            e.writeInt(snapshot.rssInKilobytes);
-            e.writeInt(snapshot.anonRssInKilobytes);
-            e.writeInt(snapshot.swapInKilobytes);
-            e.writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes);
-            pulledData.add(e);
-        });
-    }
-
-    private static boolean isAppUid(int uid) {
-        return uid >= MIN_APP_UID;
-    }
-
-    private void pullSystemIonHeapSize(
-            int tagId, long elapsedNanos, long wallClockNanos,
-            List<StatsLogEventWrapper> pulledData) {
-        final long systemIonHeapSizeInBytes = readSystemIonHeapSizeFromDebugfs();
-        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-        e.writeLong(systemIonHeapSizeInBytes);
-        pulledData.add(e);
-    }
-
-    private void pullProcessSystemIonHeapSize(
-            int tagId, long elapsedNanos, long wallClockNanos,
-            List<StatsLogEventWrapper> pulledData) {
-        List<IonAllocations> result = readProcessSystemIonHeapSizesFromDebugfs();
-        for (IonAllocations allocations : result) {
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-            e.writeInt(getUidForPid(allocations.pid));
-            e.writeString(readCmdlineFromProcfs(allocations.pid));
-            e.writeInt((int) (allocations.totalSizeInBytes / 1024));
-            e.writeInt(allocations.count);
-            e.writeInt((int) (allocations.maxSizeInBytes / 1024));
-            pulledData.add(e);
-        }
-    }
-
-    private void pullBinderCallsStats(
-            int tagId, long elapsedNanos, long wallClockNanos,
-            List<StatsLogEventWrapper> pulledData) {
-        BinderCallsStatsService.Internal binderStats =
-                LocalServices.getService(BinderCallsStatsService.Internal.class);
-        if (binderStats == null) {
-            throw new IllegalStateException("binderStats is null");
-        }
-
-        List<ExportedCallStat> callStats = binderStats.getExportedCallStats();
-        binderStats.reset();
-        for (ExportedCallStat callStat : callStats) {
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-            e.writeInt(callStat.workSourceUid);
-            e.writeString(callStat.className);
-            e.writeString(callStat.methodName);
-            e.writeLong(callStat.callCount);
-            e.writeLong(callStat.exceptionCount);
-            e.writeLong(callStat.latencyMicros);
-            e.writeLong(callStat.maxLatencyMicros);
-            e.writeLong(callStat.cpuTimeMicros);
-            e.writeLong(callStat.maxCpuTimeMicros);
-            e.writeLong(callStat.maxReplySizeBytes);
-            e.writeLong(callStat.maxRequestSizeBytes);
-            e.writeLong(callStat.recordedCallCount);
-            e.writeInt(callStat.screenInteractive ? 1 : 0);
-            e.writeInt(callStat.callingUid);
-            pulledData.add(e);
-        }
-    }
-
-    private void pullBinderCallsStatsExceptions(
-            int tagId, long elapsedNanos, long wallClockNanos,
-            List<StatsLogEventWrapper> pulledData) {
-        BinderCallsStatsService.Internal binderStats =
-                LocalServices.getService(BinderCallsStatsService.Internal.class);
-        if (binderStats == null) {
-            throw new IllegalStateException("binderStats is null");
-        }
-
-        ArrayMap<String, Integer> exceptionStats = binderStats.getExportedExceptionStats();
-        // TODO: decouple binder calls exceptions with the rest of the binder calls data so that we
-        // can reset the exception stats.
-        for (Entry<String, Integer> entry : exceptionStats.entrySet()) {
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-            e.writeString(entry.getKey());
-            e.writeInt(entry.getValue());
-            pulledData.add(e);
-        }
-    }
-
     private void pullLooperStats(int tagId, long elapsedNanos, long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
         LooperStats looperStats = LocalServices.getService(LooperStats.class);
@@ -1404,78 +1098,6 @@
         }
     }
 
-    private BatteryStatsHelper getBatteryStatsHelper() {
-        if (mBatteryStatsHelper == null) {
-            final long callingToken = Binder.clearCallingIdentity();
-            try {
-                // clearCallingIdentity required for BatteryStatsHelper.checkWifiOnly().
-                mBatteryStatsHelper = new BatteryStatsHelper(mContext, false);
-            } finally {
-                Binder.restoreCallingIdentity(callingToken);
-            }
-            mBatteryStatsHelper.create((Bundle) null);
-        }
-        long currentTime = SystemClock.elapsedRealtime();
-        if (currentTime - mBatteryStatsHelperTimestampMs >= MAX_BATTERY_STATS_HELPER_FREQUENCY_MS) {
-            // Load BatteryStats and do all the calculations.
-            mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.USER_ALL);
-            // Calculations are done so we don't need to save the raw BatteryStats data in RAM.
-            mBatteryStatsHelper.clearStats();
-            mBatteryStatsHelperTimestampMs = currentTime;
-        }
-        return mBatteryStatsHelper;
-    }
-
-    private long milliAmpHrsToNanoAmpSecs(double mAh) {
-        final long MILLI_AMP_HR_TO_NANO_AMP_SECS = 1_000_000L * 3600L;
-        return (long) (mAh * MILLI_AMP_HR_TO_NANO_AMP_SECS + 0.5);
-    }
-
-    private void pullDeviceCalculatedPowerUse(int tagId,
-            long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
-        BatteryStatsHelper bsHelper = getBatteryStatsHelper();
-        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-        e.writeLong(milliAmpHrsToNanoAmpSecs(bsHelper.getComputedPower()));
-        pulledData.add(e);
-    }
-
-    private void pullDeviceCalculatedPowerBlameUid(int tagId,
-            long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
-        final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
-        if (sippers == null) {
-            return;
-        }
-        for (BatterySipper bs : sippers) {
-            if (bs.drainType != bs.drainType.APP) {
-                continue;
-            }
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-            e.writeInt(bs.uidObj.getUid());
-            e.writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah));
-            pulledData.add(e);
-        }
-    }
-
-    private void pullDeviceCalculatedPowerBlameOther(int tagId,
-            long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
-        final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
-        if (sippers == null) {
-            return;
-        }
-        for (BatterySipper bs : sippers) {
-            if (bs.drainType == bs.drainType.APP) {
-                continue; // This is a separate atom; see pullDeviceCalculatedPowerBlameUid().
-            }
-            if (bs.drainType == bs.drainType.USER) {
-                continue; // This is not supported. We purposefully calculate over USER_ALL.
-            }
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-            e.writeInt(bs.drainType.ordinal());
-            e.writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah));
-            pulledData.add(e);
-        }
-    }
-
     private void pullDiskIo(int tagId, long elapsedNanos, final long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
         mStoragedUidIoStatsReader.readAbsolute((uid, fgCharsRead, fgCharsWrite, fgBytesRead,
@@ -1574,49 +1196,6 @@
         }
     }
 
-    private void pullTemperature(int tagId, long elapsedNanos, long wallClockNanos,
-            List<StatsLogEventWrapper> pulledData) {
-        long callingToken = Binder.clearCallingIdentity();
-        try {
-            List<Temperature> temperatures = sThermalService.getCurrentTemperatures();
-            for (Temperature temp : temperatures) {
-                StatsLogEventWrapper e =
-                        new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-                e.writeInt(temp.getType());
-                e.writeString(temp.getName());
-                e.writeInt((int) (temp.getValue() * 10));
-                e.writeInt(temp.getStatus());
-                pulledData.add(e);
-            }
-        } catch (RemoteException e) {
-            // Should not happen.
-            Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures.");
-        } finally {
-            Binder.restoreCallingIdentity(callingToken);
-        }
-    }
-
-    private void pullCoolingDevices(int tagId, long elapsedNanos, long wallClockNanos,
-            List<StatsLogEventWrapper> pulledData) {
-        long callingToken = Binder.clearCallingIdentity();
-        try {
-            List<CoolingDevice> devices = sThermalService.getCurrentCoolingDevices();
-            for (CoolingDevice device : devices) {
-                StatsLogEventWrapper e =
-                        new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-                e.writeInt(device.getType());
-                e.writeString(device.getName());
-                e.writeInt((int) (device.getValue()));
-                pulledData.add(e);
-            }
-        } catch (RemoteException e) {
-            // Should not happen.
-            Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures.");
-        } finally {
-            Binder.restoreCallingIdentity(callingToken);
-        }
-    }
-
     private void pullDebugElapsedClock(int tagId,
             long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
         final long elapsedMillis = SystemClock.elapsedRealtime();
@@ -1995,56 +1574,11 @@
         long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
         switch (tagId) {
 
-            case StatsLog.WIFI_ACTIVITY_INFO: {
-                pullWifiActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
-            case StatsLog.MODEM_ACTIVITY_INFO: {
-                pullModemActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
             case StatsLog.SYSTEM_ELAPSED_REALTIME: {
                 pullSystemElapsedRealtime(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
 
-            case StatsLog.PROCESS_MEMORY_STATE: {
-                pullProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
-            case StatsLog.PROCESS_MEMORY_HIGH_WATER_MARK: {
-                pullProcessMemoryHighWaterMark(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
-            case StatsLog.PROCESS_MEMORY_SNAPSHOT: {
-                pullProcessMemorySnapshot(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
-            case StatsLog.SYSTEM_ION_HEAP_SIZE: {
-                pullSystemIonHeapSize(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
-            case StatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE: {
-                pullProcessSystemIonHeapSize(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
-            case StatsLog.BINDER_CALLS: {
-                pullBinderCallsStats(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
-            case StatsLog.BINDER_CALLS_EXCEPTIONS: {
-                pullBinderCallsStatsExceptions(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
             case StatsLog.LOOPER_STATS: {
                 pullLooperStats(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
@@ -2107,31 +1641,6 @@
                 break;
             }
 
-            case StatsLog.DEVICE_CALCULATED_POWER_USE: {
-                pullDeviceCalculatedPowerUse(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
-            case StatsLog.DEVICE_CALCULATED_POWER_BLAME_UID: {
-                pullDeviceCalculatedPowerBlameUid(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
-            case StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER: {
-                pullDeviceCalculatedPowerBlameOther(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
-            case StatsLog.TEMPERATURE: {
-                pullTemperature(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
-            case StatsLog.COOLING_DEVICE: {
-                pullCoolingDevices(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
-
             case StatsLog.DEBUG_ELAPSED_CLOCK: {
                 pullDebugElapsedClock(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
diff --git a/api/current.txt b/api/current.txt
index a3d8458..d9c305a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -17289,6 +17289,7 @@
     field public static final int LENS_OPTICAL_STABILIZATION_MODE_ON = 1; // 0x1
     field public static final int LENS_POSE_REFERENCE_GYROSCOPE = 1; // 0x1
     field public static final int LENS_POSE_REFERENCE_PRIMARY_CAMERA = 0; // 0x0
+    field public static final int LENS_POSE_REFERENCE_UNDEFINED = 2; // 0x2
     field public static final int LENS_STATE_MOVING = 1; // 0x1
     field public static final int LENS_STATE_STATIONARY = 0; // 0x0
     field public static final int LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE = 0; // 0x0
@@ -30654,6 +30655,7 @@
     method public boolean isP2pSupported();
     method public boolean isPreferredNetworkOffloadSupported();
     method @Deprecated public boolean isScanAlwaysAvailable();
+    method public boolean isStaApConcurrencySupported();
     method public boolean isTdlsSupported();
     method public boolean isWapiSupported();
     method public boolean isWifiEnabled();
@@ -30809,6 +30811,7 @@
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsAppInteractionRequired(boolean);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsEnhancedOpen(boolean);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsHiddenSsid(boolean);
+    method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsInitialAutoJoinEnabled(boolean);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsMetered(boolean);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsUserInteractionRequired(boolean);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPasspointConfig(@NonNull android.net.wifi.hotspot2.PasspointConfiguration);
@@ -39066,7 +39069,7 @@
     field public static final String COLUMN_MIME_TYPE = "mime_type";
     field public static final String COLUMN_SIZE = "_size";
     field public static final String COLUMN_SUMMARY = "summary";
-    field public static final int FLAG_DIR_BLOCKS_TREE = 32768; // 0x8000
+    field public static final int FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE = 32768; // 0x8000
     field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10
     field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20
     field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
@@ -39747,8 +39750,8 @@
     field public static final String EXTRA_INPUT_METHOD_ID = "input_method_id";
     field public static final String EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME = "android.provider.extra.NOTIFICATION_LISTENER_COMPONENT_NAME";
     field public static final String EXTRA_SUB_ID = "android.provider.extra.SUB_ID";
-    field public static final String EXTRA_WIFI_CONFIGURATION_LIST = "android.provider.extra.WIFI_CONFIGURATION_LIST";
-    field public static final String EXTRA_WIFI_CONFIGURATION_RESULT_LIST = "android.provider.extra.WIFI_CONFIGURATION_RESULT_LIST";
+    field public static final String EXTRA_WIFI_NETWORK_LIST = "android.provider.extra.WIFI_NETWORK_LIST";
+    field public static final String EXTRA_WIFI_NETWORK_RESULT_LIST = "android.provider.extra.WIFI_NETWORK_RESULT_LIST";
     field public static final String INTENT_CATEGORY_USAGE_ACCESS_CONFIG = "android.intent.category.USAGE_ACCESS_CONFIG";
     field public static final String METADATA_USAGE_ACCESS_REASON = "android.settings.metadata.USAGE_ACCESS_REASON";
   }
@@ -55694,7 +55697,7 @@
     method public int describeContents();
     method @NonNull public android.os.Bundle getExtras();
     method @NonNull public java.util.Collection<android.view.textclassifier.TextLinks.TextLink> getLinks();
-    method @NonNull public String getText();
+    method @NonNull public CharSequence getText();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int APPLY_STRATEGY_IGNORE = 0; // 0x0
     field public static final int APPLY_STRATEGY_REPLACE = 1; // 0x1
diff --git a/api/module-app-current.txt b/api/module-app-current.txt
index d802177..4307e67 100644
--- a/api/module-app-current.txt
+++ b/api/module-app-current.txt
@@ -1 +1,9 @@
 // Signature format: 2.0
+package android.app {
+
+  public final class NotificationChannel implements android.os.Parcelable {
+    method public void setBlockableSystem(boolean);
+  }
+
+}
+
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index c8253a0..1cb1c20 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -124,3 +124,11 @@
 
 }
 
+package android.util {
+
+  public final class Log {
+    method public static int logToRadioBuffer(int, @Nullable String, @Nullable String);
+  }
+
+}
+
diff --git a/api/system-current.txt b/api/system-current.txt
index d02bbfb..e532a3a 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -126,6 +126,7 @@
     field @Deprecated public static final String MODIFY_NETWORK_ACCOUNTING = "android.permission.MODIFY_NETWORK_ACCOUNTING";
     field public static final String MODIFY_PARENTAL_CONTROLS = "android.permission.MODIFY_PARENTAL_CONTROLS";
     field public static final String MODIFY_QUIET_MODE = "android.permission.MODIFY_QUIET_MODE";
+    field public static final String MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE = "android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE";
     field public static final String MOVE_PACKAGE = "android.permission.MOVE_PACKAGE";
     field public static final String NETWORK_AIRPLANE_MODE = "android.permission.NETWORK_AIRPLANE_MODE";
     field public static final String NETWORK_CARRIER_PROVISIONING = "android.permission.NETWORK_CARRIER_PROVISIONING";
@@ -242,8 +243,10 @@
   public static final class R.attr {
     field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
     field public static final int isVrOnly = 16844152; // 0x1010578
+    field public static final int minExtensionVersion = 16844306; // 0x1010612
     field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
     field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
+    field public static final int sdkVersion = 16844305; // 0x1010611
     field public static final int supportsAmbientMode = 16844173; // 0x101058d
     field public static final int userRestriction = 16844164; // 0x1010584
   }
@@ -6243,6 +6246,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveSoftApBackupData();
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
     method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean setSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
     method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
@@ -6592,6 +6596,7 @@
 
   public final class PasspointConfiguration implements android.os.Parcelable {
     method public boolean isAutoJoinEnabled();
+    method public boolean isMacRandomizationEnabled();
   }
 
   public abstract class ProvisioningCallback {
@@ -7713,7 +7718,10 @@
   public final class PermissionManager {
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public int getRuntimePermissionsVersion();
     method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions();
+    method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToEnabledImsServices(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToEnabledTelephonyDataServices(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToLuiApp(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void revokeDefaultPermissionsFromLuiApps(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, int, int);
@@ -8014,6 +8022,8 @@
     field public static final int COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE = 5; // 0x5
     field public static final int COLUMN_INDEX_XML_RES_RANK = 0; // 0x0
     field public static final int COLUMN_INDEX_XML_RES_RESID = 1; // 0x1
+    field public static final String DYNAMIC_INDEXABLES_RAW = "dynamic_indexables_raw";
+    field public static final String DYNAMIC_INDEXABLES_RAW_PATH = "settings/dynamic_indexables_raw";
     field public static final String INDEXABLES_RAW = "indexables_raw";
     field public static final String[] INDEXABLES_RAW_COLUMNS;
     field public static final String INDEXABLES_RAW_PATH = "settings/indexables_raw";
@@ -8071,6 +8081,7 @@
     method public String getType(android.net.Uri);
     method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
     method public android.database.Cursor query(android.net.Uri, String[], String, String[], String);
+    method @Nullable public android.database.Cursor queryDynamicRawData(@Nullable String[]);
     method public abstract android.database.Cursor queryNonIndexableKeys(String[]);
     method public abstract android.database.Cursor queryRawData(String[]);
     method @Nullable public android.database.Cursor querySliceUriPairs();
@@ -8164,6 +8175,10 @@
     field public static final int VOLUME_HUSH_VIBRATE = 1; // 0x1
   }
 
+  public static final class Settings.System extends android.provider.Settings.NameValueTable {
+    method @RequiresPermission(android.Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE) public static boolean putString(@NonNull android.content.ContentResolver, @NonNull String, @Nullable String, boolean);
+  }
+
   public static interface Telephony.CarrierColumns extends android.provider.BaseColumns {
     field @NonNull public static final android.net.Uri CONTENT_URI;
     field public static final String EXPIRATION_TIME = "expiration_time";
diff --git a/api/test-current.txt b/api/test-current.txt
index 6498e9a..28119e3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -73,6 +73,7 @@
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
     method public static void resumeAppSwitches() throws android.os.RemoteException;
     method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
+    method @RequiresPermission("android.permission.MANAGE_USERS") public boolean switchUser(@NonNull android.os.UserHandle);
     field public static final int PROCESS_CAPABILITY_ALL = 1; // 0x1
     field public static final int PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1; // 0x1
     field public static final int PROCESS_CAPABILITY_NONE = 0; // 0x0
@@ -756,6 +757,7 @@
     method public int getUserId();
     method public void setAutofillOptions(@Nullable android.content.AutofillOptions);
     method public void setContentCaptureOptions(@Nullable android.content.ContentCaptureOptions);
+    method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
     field public static final String BLOB_STORE_SERVICE = "blob_store";
     field public static final String BUGREPORT_SERVICE = "bugreport";
     field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
@@ -2512,10 +2514,12 @@
     method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static float getFloat(@NonNull String, @NonNull String, float);
     method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static int getInt(@NonNull String, @NonNull String, int);
     method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static long getLong(@NonNull String, @NonNull String, long);
+    method @NonNull @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static android.provider.DeviceConfig.Properties getProperties(@NonNull String, @NonNull java.lang.String...);
     method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getProperty(@NonNull String, @NonNull String);
     method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getString(@NonNull String, @NonNull String, @Nullable String);
     method public static void removeOnPropertiesChangedListener(@NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
     method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void resetToDefaults(int, @Nullable String);
+    method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperties(@NonNull android.provider.DeviceConfig.Properties) throws android.provider.DeviceConfig.BadConfigException;
     method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(@NonNull String, @NonNull String, @Nullable String, boolean);
     field public static final String NAMESPACE_ANDROID = "android";
     field public static final String NAMESPACE_AUTOFILL = "autofill";
@@ -2527,6 +2531,10 @@
     field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot";
   }
 
+  public static class DeviceConfig.BadConfigException extends java.lang.Exception {
+    ctor public DeviceConfig.BadConfigException();
+  }
+
   public static interface DeviceConfig.OnPropertiesChangedListener {
     method public void onPropertiesChanged(@NonNull android.provider.DeviceConfig.Properties);
   }
@@ -4672,6 +4680,18 @@
 
 package android.view.inputmethod {
 
+  public final class InlineSuggestion implements android.os.Parcelable {
+    method @NonNull public static android.view.inputmethod.InlineSuggestion newInlineSuggestion(@NonNull android.view.inputmethod.InlineSuggestionInfo);
+  }
+
+  public final class InlineSuggestionInfo implements android.os.Parcelable {
+    method @NonNull public static android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(@NonNull android.view.inline.InlinePresentationSpec, @NonNull String, @Nullable String[]);
+  }
+
+  public final class InlineSuggestionsResponse implements android.os.Parcelable {
+    method @NonNull public static android.view.inputmethod.InlineSuggestionsResponse newInlineSuggestionsResponse(@NonNull java.util.List<android.view.inputmethod.InlineSuggestion>);
+  }
+
   public final class InputMethodManager {
     method public int getDisplayId();
     method public boolean isInputMethodPickerShown();
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index 6d1f291..603f7a2 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -400,7 +400,7 @@
 GetterSetterNames: android.location.GnssClock#setTimeUncertaintyNanos(double):
     
 GetterSetterNames: android.location.GnssMeasurement#setBasebandCn0DbHz(double):
-
+    
 GetterSetterNames: android.location.GnssMeasurement#setCarrierFrequencyHz(float):
     
 GetterSetterNames: android.location.GnssMeasurement#setCodeType(String):
@@ -466,7 +466,7 @@
 KotlinOperator: android.os.WorkSource#get(int):
     
 KotlinOperator: android.util.SparseArrayMap#get(int, String):
-    Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+    
 
 
 ListenerInterface: android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener:
@@ -2059,6 +2059,18 @@
     
 MissingNullability: android.view.KeyEvent#actionToString(int):
     
+MissingNullability: android.view.SurfaceControlViewHost#SurfaceControlViewHost(android.content.Context, android.view.Display, android.view.SurfaceControl) parameter #0:
+    
+MissingNullability: android.view.SurfaceControlViewHost#SurfaceControlViewHost(android.content.Context, android.view.Display, android.view.SurfaceControl) parameter #1:
+    
+MissingNullability: android.view.SurfaceControlViewHost#SurfaceControlViewHost(android.content.Context, android.view.Display, android.view.SurfaceControl) parameter #2:
+    
+MissingNullability: android.view.SurfaceControlViewHost#addView(android.view.View, android.view.WindowManager.LayoutParams) parameter #0:
+    
+MissingNullability: android.view.SurfaceControlViewHost#addView(android.view.View, android.view.WindowManager.LayoutParams) parameter #1:
+    
+MissingNullability: android.view.SurfaceControlViewHost#relayout(android.view.WindowManager.LayoutParams) parameter #0:
+    
 MissingNullability: android.view.View#getTooltipView():
     
 MissingNullability: android.view.View#isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable) parameter #0:
@@ -2079,18 +2091,6 @@
     
 MissingNullability: android.view.WindowManager.LayoutParams#accessibilityTitle:
     
-MissingNullability: android.view.SurfaceControlViewHost#SurfaceControlViewHost(android.content.Context, android.view.Display, android.view.SurfaceControl) parameter #0:
-    
-MissingNullability: android.view.SurfaceControlViewHost#SurfaceControlViewHost(android.content.Context, android.view.Display, android.view.SurfaceControl) parameter #1:
-    
-MissingNullability: android.view.SurfaceControlViewHost#SurfaceControlViewHost(android.content.Context, android.view.Display, android.view.SurfaceControl) parameter #2:
-    
-MissingNullability: android.view.SurfaceControlViewHost#addView(android.view.View, android.view.WindowManager.LayoutParams) parameter #0:
-    
-MissingNullability: android.view.SurfaceControlViewHost#addView(android.view.View, android.view.WindowManager.LayoutParams) parameter #1:
-    
-MissingNullability: android.view.SurfaceControlViewHost#relayout(android.view.WindowManager.LayoutParams) parameter #0:
-    
 MissingNullability: android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener#onAccessibilityServicesStateChanged(android.view.accessibility.AccessibilityManager) parameter #0:
     
 MissingNullability: android.view.accessibility.AccessibilityNodeInfo#setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger) parameter #0:
@@ -2426,11 +2426,13 @@
 ProtectedMember: android.view.ViewGroup#resetResolvedDrawables():
     
 
-PublicTypedef: android.os.HwParcel.Status: Don't expose @IntDef: @Status must be hidden.
 
-PublicTypedef: android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability: Don't expose @IntDef: @MmTelCapability must be hidden.
-
-PublicTypedef: android.telephony.ims.feature.MmTelFeature.ProcessCallResult: Don't expose @IntDef: @ProcessCallResult must be hidden.
+PublicTypedef: android.os.HwParcel.Status:
+    
+PublicTypedef: android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability:
+    
+PublicTypedef: android.telephony.ims.feature.MmTelFeature.ProcessCallResult:
+    
 
 
 RawAidl: android.telephony.mbms.vendor.MbmsDownloadServiceBase:
@@ -2513,6 +2515,8 @@
     
 SamShouldBeLast: android.database.sqlite.SQLiteDirectCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]):
     
+SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, android.location.LocationListener, android.os.Looper):
+    SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestLocationUpdates) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, java.util.concurrent.Executor, android.location.LocationListener):
     
 SamShouldBeLast: android.os.BugreportManager#startBugreport(android.os.ParcelFileDescriptor, android.os.ParcelFileDescriptor, android.os.BugreportParams, java.util.concurrent.Executor, android.os.BugreportManager.BugreportCallback):
@@ -2593,6 +2597,8 @@
     
 
 
+UserHandle: android.app.ActivityManager#switchUser(android.os.UserHandle):
+    When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
 UserHandle: android.app.admin.DevicePolicyManager#getOwnerInstalledCaCerts(android.os.UserHandle):
     
 UserHandle: android.app.role.RoleManager#addOnRoleHoldersChangedListenerAsUser(java.util.concurrent.Executor, android.app.role.OnRoleHoldersChangedListener, android.os.UserHandle):
@@ -2607,8 +2613,12 @@
     
 UserHandle: android.app.role.RoleManager#removeRoleHolderAsUser(String, String, int, android.os.UserHandle, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Boolean>):
     
-UserHandle: android.companion.CompanionDeviceManager#isDeviceAssociated(String, android.net.MacAddress, android.os.UserHandle):
+UserHandle: android.app.usage.StorageStatsManager#queryCratesForPackage(java.util.UUID, String, android.os.UserHandle):
     When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
+UserHandle: android.app.usage.StorageStatsManager#queryCratesForUser(java.util.UUID, android.os.UserHandle):
+    When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
+UserHandle: android.companion.CompanionDeviceManager#isDeviceAssociated(String, android.net.MacAddress, android.os.UserHandle):
+    
 UserHandle: android.content.pm.PackageManager#getInstallReason(String, android.os.UserHandle):
     
 UserHandle: android.content.pm.PackageManager#getPermissionFlags(String, String, android.os.UserHandle):
diff --git a/cmds/incident_helper/Android.bp b/cmds/incident_helper/Android.bp
index d7b6d69..64f4c66 100644
--- a/cmds/incident_helper/Android.bp
+++ b/cmds/incident_helper/Android.bp
@@ -1,3 +1,28 @@
+// Copyright (C) 2017 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.
+
+java_binary {
+    name: "incident-helper-cmd",
+    wrapper: "incident_helper_cmd",
+    srcs: [
+        "java/**/*.java",
+    ],
+    proto: {
+        plugin: "javastream",
+    },
+}
+
 cc_defaults {
     name: "incident_helper_defaults",
 
diff --git a/cmds/incident_helper/incident_helper_cmd b/cmds/incident_helper/incident_helper_cmd
new file mode 100644
index 0000000..d45f7df
--- /dev/null
+++ b/cmds/incident_helper/incident_helper_cmd
@@ -0,0 +1,6 @@
+#!/system/bin/sh
+# Script to start "incident_helper_cmd" on the device
+#
+base=/system
+export CLASSPATH=$base/framework/incident-helper-cmd.jar
+exec app_process $base/bin com.android.commands.incident.IncidentHelper "$@"
diff --git a/cmds/incident_helper/java/com/android/commands/incident/ExecutionException.java b/cmds/incident_helper/java/com/android/commands/incident/ExecutionException.java
new file mode 100644
index 0000000..d97b17e
--- /dev/null
+++ b/cmds/incident_helper/java/com/android/commands/incident/ExecutionException.java
@@ -0,0 +1,40 @@
+/*
+ * 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.commands.incident;
+
+/**
+ * Thrown when there is an error executing a section.
+ */
+public class ExecutionException extends Exception {
+    /**
+     * Constructs a ExecutionException.
+     *
+     * @param msg the message
+     */
+    public ExecutionException(String msg) {
+        super(msg);
+    }
+
+    /**
+     * Constructs a ExecutionException from another exception.
+     *
+     * @param e the exception
+     */
+    public ExecutionException(Exception e) {
+        super(e);
+    }
+}
diff --git a/cmds/incident_helper/java/com/android/commands/incident/IncidentHelper.java b/cmds/incident_helper/java/com/android/commands/incident/IncidentHelper.java
new file mode 100644
index 0000000..e5874e0
--- /dev/null
+++ b/cmds/incident_helper/java/com/android/commands/incident/IncidentHelper.java
@@ -0,0 +1,124 @@
+/*
+ * 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.commands.incident;
+
+import android.util.Log;
+
+import com.android.commands.incident.sections.PersistLogSection;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * Helper command runner for incidentd to run customized command to gather data for a non-standard
+ * section.
+ */
+public class IncidentHelper {
+    private static final String TAG = "IncidentHelper";
+    private static boolean sLog = false;
+    private final List<String> mArgs;
+    private ListIterator<String> mArgsIterator;
+
+    private IncidentHelper(String[] args) {
+        mArgs = Collections.unmodifiableList(Arrays.asList(args));
+        mArgsIterator = mArgs.listIterator();
+    }
+
+    private static void showUsage(PrintStream out) {
+        out.println("This command is not designed to be run manually.");
+        out.println("Usage:");
+        out.println("  run [sectionName]");
+    }
+
+    private void run(String[] args) throws ExecutionException {
+        Section section = null;
+        List<String> sectionArgs = new ArrayList<>();
+        while (mArgsIterator.hasNext()) {
+            String arg = mArgsIterator.next();
+            if ("-l".equals(arg)) {
+                sLog = true;
+                Log.i(TAG, "Args: [" + String.join(",", args) + "]");
+            } else if ("run".equals(arg)) {
+                section = getSection(nextArgRequired());
+                mArgsIterator.forEachRemaining(sectionArgs::add);
+                break;
+            } else {
+                log(Log.WARN, TAG, "Error: Unknown argument: " + arg);
+                return;
+            }
+        }
+        section.run(System.in, System.out, sectionArgs);
+    }
+
+    private static Section getSection(String name) throws IllegalArgumentException {
+        if ("persisted_logs".equals(name)) {
+            return new PersistLogSection();
+        }
+        throw new IllegalArgumentException("Section not found: " + name);
+    }
+
+    private String nextArgRequired() {
+        if (!mArgsIterator.hasNext()) {
+            throw new IllegalArgumentException(
+                    "Arg required after \"" + mArgs.get(mArgsIterator.previousIndex()) + "\"");
+        }
+        return mArgsIterator.next();
+    }
+
+    /**
+     * Print the given message to stderr, also log it if asked to (set by -l cmd arg).
+     */
+    public static void log(int priority, String tag, String msg) {
+        System.err.println(tag + ": " + msg);
+        if (sLog) {
+            Log.println(priority, tag, msg);
+        }
+    }
+
+    /**
+     * Command-line entry point.
+     *
+     * @param args The command-line arguments
+     */
+    public static void main(String[] args) {
+        if (args.length == 0) {
+            showUsage(System.err);
+            System.exit(0);
+        }
+        IncidentHelper incidentHelper = new IncidentHelper(args);
+        try {
+            incidentHelper.run(args);
+        } catch (IllegalArgumentException e) {
+            showUsage(System.err);
+            System.err.println();
+            e.printStackTrace(System.err);
+            if (sLog) {
+                Log.e(TAG, "Error: ", e);
+            }
+        } catch (Exception e) {
+            e.printStackTrace(System.err);
+            if (sLog) {
+                Log.e(TAG, "Error: ", e);
+            }
+            System.exit(1);
+        }
+    }
+}
diff --git a/cmds/incident_helper/java/com/android/commands/incident/Section.java b/cmds/incident_helper/java/com/android/commands/incident/Section.java
new file mode 100644
index 0000000..1c8c657
--- /dev/null
+++ b/cmds/incident_helper/java/com/android/commands/incident/Section.java
@@ -0,0 +1,29 @@
+/*
+ * 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.commands.incident;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+
+/** Section interface used by {@link IncidentHelper}. */
+public interface Section {
+    /**
+     * Writes protobuf wire format to out, optionally reads data from in, with supplied args.
+     */
+    void run(InputStream in, OutputStream out, List<String> args) throws ExecutionException;
+}
diff --git a/cmds/incident_helper/java/com/android/commands/incident/sections/PersistLogSection.java b/cmds/incident_helper/java/com/android/commands/incident/sections/PersistLogSection.java
new file mode 100644
index 0000000..f9d2e79
--- /dev/null
+++ b/cmds/incident_helper/java/com/android/commands/incident/sections/PersistLogSection.java
@@ -0,0 +1,287 @@
+/*
+ * 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.commands.incident.sections;
+
+import android.util.Log;
+import android.util.PersistedLogProto;
+import android.util.TextLogEntry;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.commands.incident.ExecutionException;
+import com.android.commands.incident.IncidentHelper;
+import com.android.commands.incident.Section;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Comparator;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+/** PersistLogSection reads persisted logs and parses them into a PersistedLogProto. */
+public class PersistLogSection implements Section {
+    private static final String TAG = "IH_PersistLog";
+    private static final String LOG_DIR = "/data/misc/logd/";
+    // Persist log files are named logcat, logcat.001, logcat.002, logcat.003, ...
+    private static final Pattern LOG_FILE_RE = Pattern.compile("logcat(\\.\\d+)?");
+    private static final Pattern BUFFER_BEGIN_RE =
+            Pattern.compile("--------- (?:beginning of|switch to) (.*)");
+    private static final Map<String, Long> SECTION_NAME_TO_ID = new HashMap<>();
+    private static final Map<Character, Integer> LOG_PRIORITY_MAP = new HashMap<>();
+    private static final String DEFAULT_BUFFER = "main";
+
+    static {
+        SECTION_NAME_TO_ID.put("main", PersistedLogProto.MAIN_LOGS);
+        SECTION_NAME_TO_ID.put("radio", PersistedLogProto.RADIO_LOGS);
+        SECTION_NAME_TO_ID.put("events", PersistedLogProto.EVENTS_LOGS);
+        SECTION_NAME_TO_ID.put("system", PersistedLogProto.SYSTEM_LOGS);
+        SECTION_NAME_TO_ID.put("crash", PersistedLogProto.CRASH_LOGS);
+        SECTION_NAME_TO_ID.put("kernel", PersistedLogProto.KERNEL_LOGS);
+    }
+
+    static {
+        LOG_PRIORITY_MAP.put('V', TextLogEntry.LOG_VERBOSE);
+        LOG_PRIORITY_MAP.put('D', TextLogEntry.LOG_DEBUG);
+        LOG_PRIORITY_MAP.put('I', TextLogEntry.LOG_INFO);
+        LOG_PRIORITY_MAP.put('W', TextLogEntry.LOG_WARN);
+        LOG_PRIORITY_MAP.put('E', TextLogEntry.LOG_ERROR);
+        LOG_PRIORITY_MAP.put('F', TextLogEntry.LOG_FATAL);
+        LOG_PRIORITY_MAP.put('S', TextLogEntry.LOG_SILENT);
+    }
+
+    /**
+     * Caches dates at 00:00:00 to epoch second elapsed conversion. There are only a few different
+     * dates in persisted logs in one device, and constructing DateTime object is relatively
+     * expensive.
+     */
+    private Map<Integer, Long> mEpochTimeCache = new HashMap<>();
+    private ProtoOutputStream mProto;
+    private long mCurrFieldId;
+    private long mMaxBytes = Long.MAX_VALUE;
+
+    @Override
+    public void run(InputStream in, OutputStream out, List<String> args) throws ExecutionException {
+        parseArgs(args);
+        Path logDirPath = Paths.get(LOG_DIR);
+        if (!Files.exists(logDirPath)) {
+            IncidentHelper.log(Log.WARN, TAG, "Skip dump. " + logDirPath + " does not exist.");
+            return;
+        }
+        if (!Files.isReadable(logDirPath)) {
+            IncidentHelper.log(Log.WARN, TAG, "Skip dump. " + logDirPath + " is not readable.");
+            return;
+        }
+        mProto = new ProtoOutputStream(out);
+        setCurrentSection(DEFAULT_BUFFER);
+        final Matcher logFileRe = LOG_FILE_RE.matcher("");
+        // Need to process older log files first and write logs to proto in chronological order
+        // But we want to process only the latest ones if there is a size limit
+        try (Stream<File> stream = Files.list(logDirPath).map(Path::toFile)
+                .filter(f -> !f.isDirectory() && match(logFileRe, f.getName()) != null)
+                .sorted(Comparator.comparingLong(File::lastModified).reversed())) {
+            Iterator<File> iter = stream.iterator();
+            List<File> filesToProcess = new ArrayList<>();
+            long sumBytes = 0;
+            while (iter.hasNext()) {
+                File file = iter.next();
+                sumBytes += file.length();
+                if (sumBytes > mMaxBytes) {
+                    break;
+                }
+                filesToProcess.add(file);
+            }
+            IncidentHelper.log(Log.INFO, TAG, "Limit # log files to " + filesToProcess.size());
+            filesToProcess.stream()
+                    .sorted(Comparator.comparingLong(File::lastModified))
+                    .forEachOrdered(this::processFile);
+        } catch (IOException e) {
+            throw new ExecutionException(e);
+        } finally {
+            mProto.flush();
+        }
+        IncidentHelper.log(Log.DEBUG, TAG, "Bytes written: " + mProto.getBytes().length);
+    }
+
+    private void parseArgs(List<String> args) {
+        Iterator<String> iter = args.iterator();
+        while (iter.hasNext()) {
+            String arg = iter.next();
+            if ("--limit".equals(arg) && iter.hasNext()) {
+                String sizeStr = iter.next().toLowerCase();
+                if (sizeStr.endsWith("mb")) {
+                    mMaxBytes = Long.parseLong(sizeStr.replace("mb", "")) * 1024 * 1024;
+                } else if (sizeStr.endsWith("kb")) {
+                    mMaxBytes = Long.parseLong(sizeStr.replace("kb", "")) * 1024;
+                } else {
+                    mMaxBytes = Long.parseLong(sizeStr);
+                }
+            } else {
+                throw new IllegalArgumentException("Unknown argument: " + arg);
+            }
+        }
+    }
+
+    private void processFile(File file) {
+        final Matcher bufferBeginRe = BUFFER_BEGIN_RE.matcher("");
+        try (BufferedReader reader = Files.newBufferedReader(file.toPath(),
+                StandardCharsets.UTF_8)) {
+            String line;
+            Matcher m;
+            while ((line = reader.readLine()) != null) {
+                if ((m = match(bufferBeginRe, line)) != null) {
+                    setCurrentSection(m.group(1));
+                    continue;
+                }
+                parseLine(line);
+            }
+        } catch (IOException e) {
+            // Non-fatal error. We can skip and still process other files.
+            IncidentHelper.log(Log.WARN, TAG, "Error reading \"" + file + "\": " + e.getMessage());
+        }
+        IncidentHelper.log(Log.DEBUG, TAG, "Finished reading " + file);
+    }
+
+    private void setCurrentSection(String sectionName) {
+        Long sectionId = SECTION_NAME_TO_ID.get(sectionName);
+        if (sectionId == null) {
+            IncidentHelper.log(Log.WARN, TAG, "Section does not exist: " + sectionName);
+            sectionId = SECTION_NAME_TO_ID.get(DEFAULT_BUFFER);
+        }
+        mCurrFieldId = sectionId;
+    }
+
+    /**
+     * Parse a log line in the following format:
+     * 01-01 15:01:47.723501  2738  2895 I Exp_TAG: example log line
+     *
+     * It does not use RegExp for performance reasons. Using this RegExp "(\\d{2})-(\\d{2})\\s
+     * (\\d{2}):(\\d{2}):(\\d{2}).(\\d{6})\\s+(\\d+)\\s+(\\d+)\\s+(.)\\s+(.*?):\\s(.*)" is twice as
+     * slow as the current approach.
+     */
+    private void parseLine(String line) {
+        long token = mProto.start(mCurrFieldId);
+        try {
+            mProto.write(TextLogEntry.SEC, getEpochSec(line));
+            // Nanosec is 15th to 20th digits of "10-01 02:57:27.710652" times 1000
+            mProto.write(TextLogEntry.NANOSEC, parseInt(line, 15, 21) * 1000L);
+
+            int start = nextNonBlank(line, 21);
+            int end = line.indexOf(' ', start + 1);
+            mProto.write(TextLogEntry.PID, parseInt(line, start, end));
+
+            start = nextNonBlank(line, end);
+            end = line.indexOf(' ', start + 1);
+            mProto.write(TextLogEntry.TID, parseInt(line, start, end));
+
+            start = nextNonBlank(line, end);
+            char priority = line.charAt(start);
+            mProto.write(TextLogEntry.PRIORITY,
+                    LOG_PRIORITY_MAP.getOrDefault(priority, TextLogEntry.LOG_DEFAULT));
+
+            start = nextNonBlank(line, start + 1);
+            end = line.indexOf(": ", start);
+            mProto.write(TextLogEntry.TAG, line.substring(start, end).trim());
+            mProto.write(TextLogEntry.LOG, line.substring(Math.min(end + 2, line.length())));
+        } catch (RuntimeException e) {
+            // Error reporting is likely piped to /dev/null. Inserting it into the proto to make
+            // it more useful.
+            mProto.write(TextLogEntry.SEC, System.currentTimeMillis() / 1000);
+            mProto.write(TextLogEntry.PRIORITY, TextLogEntry.LOG_ERROR);
+            mProto.write(TextLogEntry.TAG, TAG);
+            mProto.write(TextLogEntry.LOG,
+                    "Error parsing \"" + line + "\"" + ": " + e.getMessage());
+        }
+        mProto.end(token);
+    }
+
+    // ============== Below are util methods to parse log lines ==============
+
+    private static int nextNonBlank(String line, int start) {
+        for (int i = start; i < line.length(); i++) {
+            if (line.charAt(i) != ' ') {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Gets the epoch second from the line string. Line starts with a fixed-length timestamp like
+     * "10-01 02:57:27.710652"
+     */
+    private long getEpochSec(String line) {
+        int month = getDigit(line, 0) * 10 + getDigit(line, 1);
+        int day = getDigit(line, 3) * 10 + getDigit(line, 4);
+
+        int mmdd = month * 100 + day;
+        long epochSecBase = mEpochTimeCache.computeIfAbsent(mmdd, (key) -> {
+            final GregorianCalendar calendar = new GregorianCalendar();
+            calendar.set(Calendar.MONTH, (month + 12 - 1) % 12);
+            calendar.set(Calendar.DAY_OF_MONTH, day);
+            calendar.set(Calendar.HOUR_OF_DAY, 0);
+            calendar.set(Calendar.MINUTE, 0);
+            calendar.set(Calendar.SECOND, 0);
+            calendar.set(Calendar.MILLISECOND, 0);
+            // Date in log entries can never be in the future. If it happens, it means we are off
+            // by one year.
+            if (calendar.getTimeInMillis() > System.currentTimeMillis()) {
+                calendar.roll(Calendar.YEAR, /*amount=*/-1);
+            }
+            return calendar.getTimeInMillis() / 1000;
+        });
+
+        int hh = getDigit(line, 6) * 10 + getDigit(line, 7);
+        int mm = getDigit(line, 9) * 10 + getDigit(line, 10);
+        int ss = getDigit(line, 12) * 10 + getDigit(line, 13);
+        return epochSecBase + hh * 3600 + mm * 60 + ss;
+    }
+
+    private static int parseInt(String line, /*inclusive*/ int start, /*exclusive*/ int end) {
+        int num = 0;
+        for (int i = start; i < end; i++) {
+            num = num * 10 + getDigit(line, i);
+        }
+        return num;
+    }
+
+    private static int getDigit(String str, int pos) {
+        int digit = str.charAt(pos) - '0';
+        if (digit < 0 || digit > 9) {
+            throw new NumberFormatException("'" + str.charAt(pos) + "' is not a digit.");
+        }
+        return digit;
+    }
+
+    private static Matcher match(Matcher matcher, String text) {
+        matcher.reset(text);
+        return matcher.matches() ? matcher : null;
+    }
+}
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 1ca19c34..ada2f2d 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1312,6 +1312,13 @@
     return Status::ok();
 }
 
+Status StatsService::unregisterNativePullAtomCallback(int32_t atomTag) {
+    VLOG("StatsService::unregisterNativePullAtomCallback called.");
+    int32_t uid = IPCThreadState::self()->getCallingUid();
+    mPullerManager->UnregisterPullAtomCallback(uid, atomTag);
+    return Status::ok();
+}
+
 Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& trainNameIn,
                                                     const int64_t trainVersionCodeIn,
                                                     const int options,
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index c9a9072..7990e5e 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -203,6 +203,11 @@
     virtual Status unregisterPullAtomCallback(int32_t uid, int32_t atomTag) override;
 
     /**
+     * Binder call to unregister any existing callback for the given atom and calling uid.
+     */
+    virtual Status unregisterNativePullAtomCallback(int32_t atomTag) override;
+
+    /**
      * Binder call to log BinaryPushStateChanged atom.
      */
     virtual Status sendBinaryPushStateChangedAtom(
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index cbece78..4372e22 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -53,6 +53,7 @@
 import "frameworks/base/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto";
 import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto";
 import "frameworks/base/core/proto/android/stats/style/style_enums.proto";
+import "frameworks/base/core/proto/android/stats/sysui/notification_enums.proto";
 import "frameworks/base/core/proto/android/telecomm/enums.proto";
 import "frameworks/base/core/proto/android/telephony/enums.proto";
 import "frameworks/base/core/proto/android/view/enums.proto";
@@ -324,8 +325,6 @@
             228 [(allow_from_any_uid) = true];
         PerfettoUploaded perfetto_uploaded = 229 [(module) = "perfetto"];
         VmsClientConnectionStateChanged vms_client_connection_state_changed = 230;
-        GpsLocationStatusReported gps_location_status_reported = 231;
-        GpsTimeToFirstFixReported gps_time_to_first_fix_reported = 232;
         MediaProviderScanEvent media_provider_scan_event = 233 [(module) = "mediaprovider"];
         MediaProviderDeletionEvent media_provider_deletion_event = 234 [(module) = "mediaprovider"];
         MediaProviderPermissionEvent media_provider_permission_event =
@@ -339,10 +338,13 @@
         BootTimeEventUtcTime boot_time_event_utc_time_reported = 241;
         BootTimeEventErrorCode boot_time_event_error_code_reported = 242;
         UserspaceRebootReported userspace_reboot_reported = 243;
+        NotificationReported notification_reported = 244;
+        NotificationPanelReported notification_panel_reported = 245;
+        NotificationChannelModified notification_panel_modified = 246;
     }
 
     // Pulled events will start at field 10000.
-    // Next: 10070
+    // Next: 10071
     oneof pulled {
         WifiBytesTransfer wifi_bytes_transfer = 10000;
         WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -399,7 +401,7 @@
         ExternalStorageInfo external_storage_info = 10053;
         GpuStatsGlobalInfo gpu_stats_global_info = 10054;
         GpuStatsAppInfo gpu_stats_app_info = 10055;
-        SystemIonHeapSize system_ion_heap_size = 10056;
+        SystemIonHeapSize system_ion_heap_size = 10056 [deprecated = true];
         AppsOnExternalStorageInfo apps_on_external_storage_info = 10057;
         FaceSettings face_settings = 10058;
         CoolingDevice cooling_device = 10059;
@@ -413,6 +415,7 @@
         DangerousPermissionStateSampled dangerous_permission_state_sampled = 10067;
         GraphicsStats graphics_stats = 10068;
         RuntimeAppOpsAccess runtime_app_ops_access = 10069;
+        IonHeapSize ion_heap_size = 10070;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -737,27 +740,6 @@
     optional android.server.location.GpsSignalQualityEnum level = 1;
 }
 
-/**
- * Gps location status report
- *
- * Logged from:
- *   /frameworks/base/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
- */
-message GpsLocationStatusReported {
-    // Boolean stating if location was acquired
-    optional bool location_success = 1;
-}
-
-/**
- * Gps log time to first fix report
- *
- * Logged from:
- *   /frameworks/base/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
- */
-message GpsTimeToFirstFixReported {
-    // int32 reporting the time to first fix in milliseconds
-    optional int32 time_to_first_fix_millis = 1;
-}
 
 /**
  * Logs when a sync manager sync state changes.
@@ -3309,6 +3291,10 @@
  * this button" or "this dialog was displayed".
  * Keep the UI event stream clean: don't use for system or background events.
  * Log using the UiEventLogger wrapper - don't write with the StatsLog API directly.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/
+ *   frameworks/base/packages/SystemUI/src/com/android/systemui/
  */
 message UiEventReported {
     // The event_id.
@@ -3320,6 +3306,122 @@
 }
 
 /**
+ * Reports a notification was created or updated.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/notification/
+ */
+message NotificationReported {
+    // The event_id (as for UiEventReported).
+    optional int32 event_id = 1;
+    // The notifying app's uid and package.
+    optional int32 uid = 2 [(is_uid) = true];
+    optional string package_name = 3;
+    // A small system-assigned identifier for the notification.
+    // Locally probably-unique, but expect collisions across users and/or days.
+    optional int32 instance_id = 4;
+    // The app-assigned notification ID and tag
+    optional int32 notification_id = 5;
+    optional string notification_tag = 6;
+    optional string channel_id = 7;  // App-assigned channel ID
+
+    // Grouping information
+    optional string group_id = 8;  // Group the notification currently belongs to
+    optional int32 group_instance_id = 9;  // Instance_id of the group-summary notification
+    optional bool is_group_summary = 10;  // Tags the group-summary notification
+
+    // Attributes
+    optional string category = 11;   // App-assigned notification category (API-defined strings)
+    optional int32 style = 12;       // App-assigned notification style
+    optional int32 num_people = 13;  // Number of Person records attached to the notification
+
+    // Ordering, importance and interruptiveness
+
+    optional int32 position = 14;    // Position in NotificationManager's list
+
+    optional android.stats.sysui.NotificationImportance importance = 15;
+    optional int32 alerting = 16;    // Bitfield, 1=buzz 2=beep 4=blink
+
+    enum NotificationImportanceExplanation {
+        IMPORTANCE_EXPLANATION_UNKNOWN = 0;
+        IMPORTANCE_EXPLANATION_APP = 1;     // App-specified channel importance.
+        IMPORTANCE_EXPLANATION_USER = 2;    // User-specified channel importance.
+        IMPORTANCE_EXPLANATION_ASST = 3;    // Notification Assistant override.
+        IMPORTANCE_EXPLANATION_SYSTEM = 4;  // System override.
+        // Like _APP, but based on pre-channels priority signal.
+        IMPORTANCE_EXPLANATION_APP_PRE_CHANNELS = 5;
+    }
+
+    optional NotificationImportanceExplanation importance_source = 17;
+    optional android.stats.sysui.NotificationImportance importance_initial = 18;
+    optional NotificationImportanceExplanation importance_initial_source = 19;
+    optional android.stats.sysui.NotificationImportance importance_asst = 20;
+    optional int32 assistant_hash = 21;
+    optional float assistant_ranking_score = 22;
+}
+
+message Notification {
+    // The notifying app's uid and package.
+    optional int32 uid = 1 [(is_uid) = true];
+    optional string package_name = 2;
+    // A small system-assigned identifier for the notification.
+    optional int32 instance_id = 3;
+
+    // Grouping information.
+    optional int32 group_instance_id = 4;
+    optional bool is_group_summary = 5;
+
+    // The section of the shade that the notification is in.
+    // See NotificationSectionsManager.PriorityBucket.
+    enum NotificationSection {
+        SECTION_UNKNOWN = 0;
+        SECTION_PEOPLE = 1;
+        SECTION_ALERTING = 2;
+        SECTION_SILENT = 3;
+    }
+    optional NotificationSection section = 6;
+}
+
+message NotificationList {
+    repeated Notification notifications = 1;  // An ordered sequence of notifications.
+}
+
+/**
+ * Reports a notification panel was displayed, e.g. from the lockscreen or status bar.
+ *
+ * Logged from:
+ *   frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/
+ */
+message NotificationPanelReported {
+    // The event_id (as for UiEventReported).
+    optional int32 event_id = 1;
+    optional int32 num_notifications = 2;
+    // The notifications in the panel, in the order that they appear there.
+    optional NotificationList notifications = 3 [(log_mode) = MODE_BYTES];
+}
+
+/**
+ * Reports a notification channel, or channel group, was created, updated, or deleted.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/notification/
+ */
+message NotificationChannelModified {
+    // The event_id (as for UiEventReported).
+    optional int32 event_id = 1;
+    // The notifying app's uid and package.
+    optional int32 uid = 2 [(is_uid) = true];
+    optional string package_name = 3;
+    // App-assigned notification channel ID or channel-group ID
+    optional string channel_id = 4;
+    // Previous importance setting, if applicable
+    optional android.stats.sysui.NotificationImportance old_importance = 5;
+    // New importance setting
+    optional android.stats.sysui.NotificationImportance importance = 6;
+}
+
+
+/**
  * Logs when a biometric acquire event occurs.
  *
  * Logged from:
@@ -3507,12 +3609,14 @@
         INSTALL_FAILURE_DOWNLOAD = 23;
         INSTALL_FAILURE_STATE_MISMATCH = 24;
         INSTALL_FAILURE_COMMIT = 25;
+        REBOOT_TRIGGERED = 26;
     }
     optional State state = 6;
     // Possible experiment ids for monitoring this push.
     optional TrainExperimentIds experiment_ids = 7 [(log_mode) = MODE_BYTES];
     // user id
     optional int32 user_id = 8;
+    optional int32 reason = 9;
 }
 
 /* Test atom, is not logged anywhere */
@@ -6804,6 +6908,7 @@
         INSTALL_FAILURE_DOWNLOAD = 23;
         INSTALL_FAILURE_STATE_MISMATCH = 24;
         INSTALL_FAILURE_COMMIT = 25;
+        REBOOT_TRIGGERED = 26;
     }
     optional Status status = 4;
 }
@@ -6946,11 +7051,27 @@
  * Pulled from StatsCompanionService.
  */
 message SystemIonHeapSize {
+    // Deprecated due to limited support of ion stats in debugfs.
+    // Use `IonHeapSize` instead.
+    option deprecated = true;
+
     // Size of the system ion heap in bytes.
+    // Read from debugfs.
     optional int64 size_in_bytes = 1;
 }
 
 /*
+ * Logs the total size of the ion heap.
+ *
+ * Pulled from StatsCompanionService.
+ */
+message IonHeapSize {
+    // Total size of all ion heaps in kilobytes.
+    // Read from: /sys/kernel/ion/total_heaps_kb.
+    optional int32 total_size_kb = 1;
+}
+
+/*
  * Logs the per-process size of the system ion heap.
  *
  * Pulled from StatsCompanionService.
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp
index 0e6b677..e5a83a2 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.cpp
+++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp
@@ -42,7 +42,7 @@
 }
 
 bool StatsCallbackPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
-    VLOG("StatsCallbackPuller called for tag %d", mTagId)
+    VLOG("StatsCallbackPuller called for tag %d", mTagId);
     if(mCallback == nullptr) {
         ALOGW("No callback registered");
         return false;
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index b5920cb..d5cda85 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -68,14 +68,6 @@
         {{.atomTag = android::util::ON_DEVICE_POWER_MEASUREMENT},
          {.puller = new PowerStatsPuller()}},
 
-        // wifi_activity_energy_info
-        {{.atomTag = android::util::WIFI_ACTIVITY_INFO},
-         {.puller = new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
-
-        // modem_activity_info
-        {{.atomTag = android::util::MODEM_ACTIVITY_INFO},
-         {.puller = new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
-
         // system_elapsed_realtime
         {{.atomTag = android::util::SYSTEM_ELAPSED_REALTIME},
          {.coolDownNs = NS_PER_SEC,
@@ -103,45 +95,6 @@
         {{.atomTag = android::util::BATTERY_CYCLE_COUNT},
          {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_CYCLE_COUNT)}},
 
-        // process_memory_state
-        {{.atomTag = android::util::PROCESS_MEMORY_STATE},
-         {.additiveFields = {4, 5, 6, 7, 8},
-          .puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
-
-        // process_memory_high_water_mark
-        {{.atomTag = android::util::PROCESS_MEMORY_HIGH_WATER_MARK},
-         {.puller =
-                  new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
-
-        // process_memory_snapshot
-        {{.atomTag = android::util::PROCESS_MEMORY_SNAPSHOT},
-         {.puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_SNAPSHOT)}},
-
-        // system_ion_heap_size
-        {{.atomTag = android::util::SYSTEM_ION_HEAP_SIZE},
-         {.puller = new StatsCompanionServicePuller(android::util::SYSTEM_ION_HEAP_SIZE)}},
-
-        // process_system_ion_heap_size
-        {{.atomTag = android::util::PROCESS_SYSTEM_ION_HEAP_SIZE},
-         {.puller = new StatsCompanionServicePuller(android::util::PROCESS_SYSTEM_ION_HEAP_SIZE)}},
-
-        // temperature
-        {{.atomTag = android::util::TEMPERATURE},
-         {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}},
-
-        // cooling_device
-        {{.atomTag = android::util::COOLING_DEVICE},
-         {.puller = new StatsCompanionServicePuller(android::util::COOLING_DEVICE)}},
-
-        // binder_calls
-        {{.atomTag = android::util::BINDER_CALLS},
-         {.additiveFields = {4, 5, 6, 8, 12},
-          .puller = new StatsCompanionServicePuller(android::util::BINDER_CALLS)}},
-
-        // binder_calls_exceptions
-        {{.atomTag = android::util::BINDER_CALLS_EXCEPTIONS},
-         {.puller = new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}},
-
         // looper_stats
         {{.atomTag = android::util::LOOPER_STATS},
          {.additiveFields = {5, 6, 7, 8, 9},
@@ -193,20 +146,6 @@
          {.additiveFields = {7, 9, 11, 13, 15, 17, 19, 21},
           .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
 
-        // DeviceCalculatedPowerUse.
-        {{.atomTag = android::util::DEVICE_CALCULATED_POWER_USE},
-         {.puller = new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}},
-
-        // DeviceCalculatedPowerBlameUid.
-        {{.atomTag = android::util::DEVICE_CALCULATED_POWER_BLAME_UID},
-         {.puller = new StatsCompanionServicePuller(
-                  android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}},
-
-        // DeviceCalculatedPowerBlameOther.
-        {{.atomTag = android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER},
-         {.puller = new StatsCompanionServicePuller(
-                  android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
-
         // DebugElapsedClock.
         {{.atomTag = android::util::DEBUG_ELAPSED_CLOCK},
          {.additiveFields = {1, 2, 3, 4},
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 070a4f8..d952be5 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2539,7 +2539,8 @@
         mCalled = true;
 
         if (mAutoFillResetNeeded) {
-            getAutofillManager().onInvisibleForAutofill();
+            // If stopped without changing the configurations, the response should expire.
+            getAutofillManager().onInvisibleForAutofill(!mChangingConfigurations);
         } else if (mIntent != null
                 && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)
                 && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY)) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index c3b07c8..2010cc98 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4064,6 +4064,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public boolean switchUser(@NonNull UserHandle user) {
         if (user == null) {
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 70262b0..0c5e67c 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -464,6 +464,9 @@
                     || appDir.equals(instrumentedAppDir)) {
                 outZipPaths.clear();
                 outZipPaths.add(instrumentationAppDir);
+                if (!instrumentationAppDir.equals(instrumentedAppDir)) {
+                    outZipPaths.add(instrumentedAppDir);
+                }
 
                 // Only add splits if the app did not request isolated split loading.
                 if (!aInfo.requestsIsolatedSplitLoading()) {
@@ -472,7 +475,6 @@
                     }
 
                     if (!instrumentationAppDir.equals(instrumentedAppDir)) {
-                        outZipPaths.add(instrumentedAppDir);
                         if (instrumentedSplitAppDirs != null) {
                             Collections.addAll(outZipPaths, instrumentedSplitAppDirs);
                         }
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index bdc7b99..5a4622e 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -15,6 +15,8 @@
  */
 package android.app;
 
+import static android.annotation.SystemApi.Client.MODULE_APPS;
+
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
@@ -105,6 +107,7 @@
     private static final String ATT_ORIG_IMP = "orig_imp";
     private static final String ATT_PARENT_CHANNEL = "parent";
     private static final String ATT_CONVERSATION_ID = "conv_id";
+    private static final String ATT_DEMOTE = "dem";
     private static final String DELIMITER = ",";
 
     /**
@@ -194,6 +197,7 @@
     private boolean mImportanceLockedDefaultApp;
     private String mParentId = null;
     private String mConversationId = null;
+    private boolean mDemoted = false;
 
     /**
      * Creates a notification channel.
@@ -260,6 +264,7 @@
         mOriginalImportance = in.readInt();
         mParentId = in.readString();
         mConversationId = in.readString();
+        mDemoted = in.readBoolean();
     }
 
     @Override
@@ -317,6 +322,7 @@
         dest.writeInt(mOriginalImportance);
         dest.writeString(mParentId);
         dest.writeString(mConversationId);
+        dest.writeBoolean(mDemoted);
     }
 
     /**
@@ -350,9 +356,13 @@
     }
 
     /**
+     * Allows users to block notifications sent through this channel, if this channel belongs to
+     * a package that is signed with the system signature. If the channel does not belong to a
+     * package that is signed with the system signature, this method does nothing.
+     * @param blockableSystem if {@code true}, allows users to block notifications on this channel.
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi(client = MODULE_APPS)
     @TestApi
     public void setBlockableSystem(boolean blockableSystem) {
         mBlockableSystem = blockableSystem;
@@ -386,8 +396,6 @@
         return input;
     }
 
-    // Modifiable by apps on channel creation.
-
     /**
      * @hide
      */
@@ -395,6 +403,8 @@
         mId = id;
     }
 
+    // Modifiable by apps on channel creation.
+
     /**
      * Sets what group this channel belongs to.
      *
@@ -767,6 +777,20 @@
     }
 
     /**
+     * @hide
+     */
+    public void setDemoted(boolean demoted) {
+        mDemoted = demoted;
+    }
+
+    /**
+     * @hide
+     */
+    public boolean isDemoted() {
+        return mDemoted;
+    }
+
+    /**
      * Returns whether the user has chosen the importance of this channel, either to affirm the
      * initial selection from the app, or changed it to be higher or lower.
      * @see #getImportance()
@@ -829,6 +853,7 @@
         setOriginalImportance(safeInt(parser, ATT_ORIG_IMP, DEFAULT_IMPORTANCE));
         setConversationId(parser.getAttributeValue(null, ATT_PARENT_CHANNEL),
                 parser.getAttributeValue(null, ATT_CONVERSATION_ID));
+        setDemoted(safeBool(parser, ATT_DEMOTE, false));
     }
 
     @Nullable
@@ -959,6 +984,9 @@
         if (getConversationId() != null) {
             out.attribute(null, ATT_CONVERSATION_ID, getConversationId());
         }
+        if (isDemoted()) {
+            out.attribute(null, ATT_DEMOTE, Boolean.toString(isDemoted()));
+        }
 
         // mImportanceLockedDefaultApp and mImportanceLockedByOEM have a different source of
         // truth and so aren't written to this xml file
@@ -1118,7 +1146,8 @@
                 && mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp
                 && mOriginalImportance == that.mOriginalImportance
                 && Objects.equals(getParentChannelId(), that.getParentChannelId())
-                && Objects.equals(getConversationId(), that.getConversationId());
+                && Objects.equals(getConversationId(), that.getConversationId())
+                && isDemoted() == that.isDemoted();
     }
 
     @Override
@@ -1129,7 +1158,7 @@
                 isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(),
                 getAudioAttributes(), isBlockableSystem(), mAllowBubbles,
                 mImportanceLockedByOEM, mImportanceLockedDefaultApp, mOriginalImportance,
-                mParentId, mConversationId);
+                mParentId, mConversationId, mDemoted);
         result = 31 * result + Arrays.hashCode(mVibration);
         return result;
     }
@@ -1176,7 +1205,8 @@
                 + ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp
                 + ", mOriginalImp=" + mOriginalImportance
                 + ", mParent=" + mParentId
-                + ", mConversationId=" + mConversationId;
+                + ", mConversationId=" + mConversationId
+                + ", mDemoted=" + mDemoted;
     }
 
     /** @hide */
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index d23754e..7ab85a4 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -1208,8 +1208,7 @@
                 WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
                 ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
                 if (r != null) {
-                    applyConfigurationToResourcesLocked(config, compat, tmpConfig,
-                            defaultDisplayMetrics, key, r);
+                    applyConfigurationToResourcesLocked(config, compat, tmpConfig, key, r);
                 } else {
                     mResourceImpls.removeAt(i);
                 }
@@ -1224,8 +1223,7 @@
                 }
 
                 applyConfigurationToResourcesLocked(config, compat, tmpConfig,
-                        defaultDisplayMetrics, resourcesWithLoaders.resourcesKey(),
-                        resources.getImpl());
+                        resourcesWithLoaders.resourcesKey(), resources.getImpl());
             }
 
             return changes != 0;
@@ -1236,40 +1234,33 @@
 
     private void applyConfigurationToResourcesLocked(@NonNull Configuration config,
             @Nullable CompatibilityInfo compat, Configuration tmpConfig,
-            DisplayMetrics defaultDisplayMetrics, ResourcesKey key, ResourcesImpl resourcesImpl) {
+            ResourcesKey key, ResourcesImpl resourcesImpl) {
         if (DEBUG || DEBUG_CONFIGURATION) {
             Slog.v(TAG, "Changing resources "
                     + resourcesImpl + " config to: " + config);
         }
         int displayId = key.mDisplayId;
-        boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
-        DisplayMetrics dm = defaultDisplayMetrics;
         final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
-        if (!isDefaultDisplay || hasOverrideConfiguration) {
-            tmpConfig.setTo(config);
+        tmpConfig.setTo(config);
 
-            // Get new DisplayMetrics based on the DisplayAdjustments given
-            // to the ResourcesImpl. Update a copy if the CompatibilityInfo
-            // changed, because the ResourcesImpl object will handle the
-            // update internally.
-            DisplayAdjustments daj = resourcesImpl.getDisplayAdjustments();
-            if (compat != null) {
-                daj = new DisplayAdjustments(daj);
-                daj.setCompatibilityInfo(compat);
-            }
-            dm = getDisplayMetrics(displayId, daj);
-
-            if (!isDefaultDisplay) {
-                applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
-            }
-
-            if (hasOverrideConfiguration) {
-                tmpConfig.updateFrom(key.mOverrideConfiguration);
-            }
-            resourcesImpl.updateConfiguration(tmpConfig, dm, compat);
-        } else {
-            resourcesImpl.updateConfiguration(config, dm, compat);
+        // Get new DisplayMetrics based on the DisplayAdjustments given to the ResourcesImpl. Update
+        // a copy if the CompatibilityInfo changed, because the ResourcesImpl object will handle the
+        // update internally.
+        DisplayAdjustments daj = resourcesImpl.getDisplayAdjustments();
+        if (compat != null) {
+            daj = new DisplayAdjustments(daj);
+            daj.setCompatibilityInfo(compat);
         }
+        daj.setConfiguration(config);
+        DisplayMetrics dm = getDisplayMetrics(displayId, daj);
+        if (displayId != Display.DEFAULT_DISPLAY) {
+            applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
+        }
+
+        if (hasOverrideConfiguration) {
+            tmpConfig.updateFrom(key.mOverrideConfiguration);
+        }
+        resourcesImpl.updateConfiguration(tmpConfig, dm, compat);
     }
 
     /**
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 6df92a7..a34be5c 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -75,9 +75,15 @@
 
         mLifecycleSequence.clear();
         if (finish >= start) {
-            // just go there
-            for (int i = start + 1; i <= finish; i++) {
-                mLifecycleSequence.add(i);
+            if (start == ON_START && finish == ON_STOP) {
+                // A case when we from start to stop state soon, we don't need to go
+                // through the resumed, paused state.
+                mLifecycleSequence.add(ON_STOP);
+            } else {
+                // just go there
+                for (int i = start + 1; i <= finish; i++) {
+                    mLifecycleSequence.add(i);
+                }
             }
         } else { // finish < start, can't just cycle down
             if (start == ON_PAUSE && finish == ON_RESUME) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 3860508..679de8a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1827,6 +1827,7 @@
      */
     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
     @SystemApi
+    @TestApi
     public void startActivityAsUser(@RequiresPermission @NonNull Intent intent,
             @NonNull UserHandle user) {
         throw new RuntimeException("Not implemented. Must override in a subclass.");
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 5ca50c2..1f502a1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3379,6 +3379,10 @@
      * etc. This library is versioned and backwards compatible. Clients
      * should check its version via {@link android.ext.services.Version
      * #getVersionCode()} and avoid calling APIs added in later versions.
+     * <p>
+     * This shared library no longer exists since Android R.
+     *
+     * @see #getServicesSystemSharedLibraryPackageName()
      *
      * @hide
      */
@@ -4745,6 +4749,9 @@
 
     /**
      * Get the name of the package hosting the services shared library.
+     * <p>
+     * Note that this package is no longer a shared library since Android R. It is now a package
+     * that hosts for a bunch of updatable services that the system binds to.
      *
      * @return The library host package.
      *
diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java
index a001ada..38d3137 100644
--- a/core/java/android/content/pm/parsing/ApkParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkParseUtils.java
@@ -65,6 +65,7 @@
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.Trace;
+import android.os.ext.SdkExtensions;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -1615,11 +1616,72 @@
                 );
             }
 
+            int type;
+            final int innerDepth = parser.getDepth();
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                    continue;
+                }
+                if (parser.getName().equals("extension-sdk")) {
+                    final ParseResult result =
+                            parseExtensionSdk(parseInput, parsingPackage, res, parser);
+                    if (!result.isSuccess()) {
+                        return result;
+                    }
+                } else {
+                    Slog.w(TAG, "Unknown element under <uses-sdk>: " + parser.getName()
+                            + " at " + parsingPackage.getBaseCodePath() + " "
+                            + parser.getPositionDescription());
+                }
+                XmlUtils.skipCurrentTag(parser);
+            }
+
             parsingPackage.setMinSdkVersion(minSdkVersion)
                     .setTargetSdkVersion(targetSdkVersion);
         }
+        return parseInput.success(parsingPackage);
+    }
 
-        XmlUtils.skipCurrentTag(parser);
+    private static ParseResult parseExtensionSdk(
+            ParseInput parseInput,
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser
+    ) throws IOException, XmlPullParserException {
+        TypedArray sa = res.obtainAttributes(parser,
+                com.android.internal.R.styleable.AndroidManifestExtensionSdk);
+        int sdkVersion = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestExtensionSdk_sdkVersion, -1);
+        int minVersion = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestExtensionSdk_minExtensionVersion,
+                -1);
+        sa.recycle();
+
+        if (sdkVersion < 0) {
+            return parseInput.error(
+                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                "<extension-sdk> must specify an sdkVersion >= 0");
+        }
+        if (minVersion < 0) {
+            return parseInput.error(
+                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                "<extension-sdk> must specify minExtensionVersion >= 0");
+        }
+
+        try {
+            int version = SdkExtensions.getExtensionVersion(sdkVersion);
+            if (version < minVersion) {
+                return parseInput.error(
+                        PackageManager.INSTALL_FAILED_OLDER_SDK,
+                        "Package requires " + sdkVersion + " extension version " + minVersion
+                                + " which exceeds device version " + version);
+            }
+        } catch (RuntimeException e) {
+            return parseInput.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "Specified sdkVersion " + sdkVersion + " is not valid");
+        }
         return parseInput.success(parsingPackage);
     }
 
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index a45648f..7bddc1d 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1539,10 +1539,15 @@
      * <p><code>p' = Rp</code></p>
      * <p>where <code>p</code> is in the device sensor coordinate system, and
      *  <code>p'</code> is in the camera-oriented coordinate system.</p>
+     * <p>If {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is UNDEFINED, the quaternion rotation cannot
+     *  be accurately represented by the camera device, and will be represented by
+     *  default values matching its default facing.</p>
      * <p><b>Units</b>:
      * Quaternion coefficients</p>
      * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
      * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
+     *
+     * @see CameraCharacteristics#LENS_POSE_REFERENCE
      */
     @PublicKey
     @NonNull
@@ -1577,6 +1582,8 @@
      * <p>When {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is GYROSCOPE, then this position is relative to
      * the center of the primary gyroscope on the device. The axis definitions are the same as
      * with PRIMARY_CAMERA.</p>
+     * <p>When {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is UNDEFINED, this position cannot be accurately
+     * represented by the camera device, and will be represented as <code>(0, 0, 0)</code>.</p>
      * <p><b>Units</b>: Meters</p>
      * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
      * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
@@ -1714,20 +1721,24 @@
             new Key<float[]>("android.lens.radialDistortion", float[].class);
 
     /**
-     * <p>The origin for {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}.</p>
+     * <p>The origin for {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}, and the accuracy of
+     * {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation} and {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}.</p>
      * <p>Different calibration methods and use cases can produce better or worse results
      * depending on the selected coordinate origin.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #LENS_POSE_REFERENCE_PRIMARY_CAMERA PRIMARY_CAMERA}</li>
      *   <li>{@link #LENS_POSE_REFERENCE_GYROSCOPE GYROSCOPE}</li>
+     *   <li>{@link #LENS_POSE_REFERENCE_UNDEFINED UNDEFINED}</li>
      * </ul></p>
      * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
      * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
      *
+     * @see CameraCharacteristics#LENS_POSE_ROTATION
      * @see CameraCharacteristics#LENS_POSE_TRANSLATION
      * @see #LENS_POSE_REFERENCE_PRIMARY_CAMERA
      * @see #LENS_POSE_REFERENCE_GYROSCOPE
+     * @see #LENS_POSE_REFERENCE_UNDEFINED
      */
     @PublicKey
     @NonNull
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index ec13a36..2377ccd 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -366,6 +366,20 @@
      */
     public static final int LENS_POSE_REFERENCE_GYROSCOPE = 1;
 
+    /**
+     * <p>The camera device cannot represent the values of {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}
+     * and {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} accurately enough. One such example is a camera device
+     * on the cover of a foldable phone: in order to measure the pose translation and rotation,
+     * some kind of hinge position sensor would be needed.</p>
+     * <p>The value of {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation} must be all zeros, and
+     * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} must be values matching its default facing.</p>
+     *
+     * @see CameraCharacteristics#LENS_POSE_ROTATION
+     * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+     * @see CameraCharacteristics#LENS_POSE_REFERENCE
+     */
+    public static final int LENS_POSE_REFERENCE_UNDEFINED = 2;
+
     //
     // Enumeration values for CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
     //
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 9b305b32..6f0d135 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -3027,10 +3027,15 @@
      * <p><code>p' = Rp</code></p>
      * <p>where <code>p</code> is in the device sensor coordinate system, and
      *  <code>p'</code> is in the camera-oriented coordinate system.</p>
+     * <p>If {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is UNDEFINED, the quaternion rotation cannot
+     *  be accurately represented by the camera device, and will be represented by
+     *  default values matching its default facing.</p>
      * <p><b>Units</b>:
      * Quaternion coefficients</p>
      * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
      * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
+     *
+     * @see CameraCharacteristics#LENS_POSE_REFERENCE
      */
     @PublicKey
     @NonNull
@@ -3065,6 +3070,8 @@
      * <p>When {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is GYROSCOPE, then this position is relative to
      * the center of the primary gyroscope on the device. The axis definitions are the same as
      * with PRIMARY_CAMERA.</p>
+     * <p>When {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is UNDEFINED, this position cannot be accurately
+     * represented by the camera device, and will be represented as <code>(0, 0, 0)</code>.</p>
      * <p><b>Units</b>: Meters</p>
      * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
      * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 799dff9..fb5f136 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -26,6 +26,7 @@
 import android.app.KeyguardManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Point;
 import android.media.projection.MediaProjection;
 import android.os.Handler;
@@ -400,10 +401,10 @@
         if (display == null) {
             // TODO: We cannot currently provide any override configurations for metrics on displays
             // other than the display the context is associated with.
-            final Context context = mContext.getDisplayId() == displayId
-                    ? mContext : mContext.getApplicationContext();
+            final Resources resources = mContext.getDisplayId() == displayId
+                    ? mContext.getResources() : null;
 
-            display = mGlobal.getCompatibleDisplay(displayId, context.getResources());
+            display = mGlobal.getCompatibleDisplay(displayId, resources);
             if (display != null) {
                 mDisplays.put(displayId, display);
             }
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index 921f0f2..5cb3361 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -256,9 +256,13 @@
                 mService.send(msg);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Unable to get status from installation service");
-                mExecutor.execute(() -> {
+                if (mExecutor != null) {
+                    mExecutor.execute(() -> {
+                        mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e);
+                    });
+                } else {
                     mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e);
-                });
+                }
             }
         }
 
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 2583292..5a1ba7f 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -160,6 +160,7 @@
      * Grant default permissions to currently active LUI app
      * @param packageName The package name for the LUI app
      * @param user The user handle
+     * @param executor The executor for the callback
      * @param callback The callback provided by caller to be notified when grant completes
      * @hide
      */
@@ -181,6 +182,7 @@
      * Revoke default permissions to currently active LUI app
      * @param packageNames The package names for the LUI apps
      * @param user The user handle
+     * @param executor The executor for the callback
      * @param callback The callback provided by caller to be notified when grant completes
      * @hide
      */
@@ -198,6 +200,72 @@
         }
     }
 
+    /**
+     * Grant default permissions to currently active Ims services
+     * @param packageNames The package names for the Ims services
+     * @param user The user handle
+     * @param executor The executor for the callback
+     * @param callback The callback provided by caller to be notified when grant completes
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS)
+    public void grantDefaultPermissionsToEnabledImsServices(
+            @NonNull String[] packageNames, @NonNull UserHandle user,
+            @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+        try {
+            mPermissionManager.grantDefaultPermissionsToEnabledImsServices(
+                    packageNames, user.getIdentifier());
+            executor.execute(() -> callback.accept(true));
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Grant default permissions to currently enabled telephony data services
+     * @param packageNames The package name for the services
+     * @param user The user handle
+     * @param executor The executor for the callback
+     * @param callback The callback provided by caller to be notified when grant completes
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS)
+    public void grantDefaultPermissionsToEnabledTelephonyDataServices(
+            @NonNull String[] packageNames, @NonNull UserHandle user,
+            @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+        try {
+            mPermissionManager.grantDefaultPermissionsToEnabledTelephonyDataServices(
+                    packageNames, user.getIdentifier());
+            executor.execute(() -> callback.accept(true));
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Revoke default permissions to currently active telephony data services
+     * @param packageNames The package name for the services
+     * @param user The user handle
+     * @param executor The executor for the callback
+     * @param callback The callback provided by caller to be notified when revoke completes
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS)
+    public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(
+            @NonNull String[] packageNames, @NonNull UserHandle user,
+            @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+        try {
+            mPermissionManager.revokeDefaultPermissionsFromDisabledTelephonyDataServices(
+                    packageNames, user.getIdentifier());
+            executor.execute(() -> callback.accept(true));
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
     private List<SplitPermissionInfo> splitPermissionInfoListToNonParcelableList(
             List<SplitPermissionInfoParcelable> parcelableList) {
         final int size = parcelableList.size();
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 6650cf2..53f4615 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -424,6 +424,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     @NonNull
     @RequiresPermission(READ_DEVICE_CONFIG)
     public static Properties getProperties(@NonNull String namespace, @NonNull String ... names) {
@@ -593,6 +594,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     @RequiresPermission(WRITE_DEVICE_CONFIG)
     public static boolean setProperties(@NonNull Properties properties) throws BadConfigException {
         ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
@@ -817,6 +819,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public static class BadConfigException extends Exception {}
 
     /**
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index ef8a286..1453608 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -363,7 +363,7 @@
          * <p>
          * Type: INTEGER (int)
          *
-         * @see #FLAG_DIR_BLOCKS_TREE
+         * @see #FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE
          * @see #FLAG_DIR_PREFERS_GRID
          * @see #FLAG_DIR_PREFERS_LAST_MODIFIED
          * @see #FLAG_DIR_SUPPORTS_CREATE
@@ -567,7 +567,7 @@
          * @see Intent#ACTION_OPEN_DOCUMENT_TREE
          * @see #COLUMN_FLAGS
          */
-        public static final int FLAG_DIR_BLOCKS_TREE = 1 << 15;
+        public static final int FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE = 1 << 15;
     }
 
     /**
diff --git a/core/java/android/provider/SearchIndexablesContract.java b/core/java/android/provider/SearchIndexablesContract.java
index 298628e..8fc13b7 100644
--- a/core/java/android/provider/SearchIndexablesContract.java
+++ b/core/java/android/provider/SearchIndexablesContract.java
@@ -98,16 +98,14 @@
 
 
     /**
-     * Dynamic indexable raw data names.
-     *
-     * @hide
+     * The raw data name of dynamic index. This is used to compose the index path of provider
+     * for dynamic index.
      */
     public static final String DYNAMIC_INDEXABLES_RAW = "dynamic_indexables_raw";
 
     /**
-     * ContentProvider path for dynamic indexable raw data.
-     *
-     * @hide
+     * ContentProvider path for dynamic index. This is used to get the raw data of dynamic index
+     * from provider.
      */
     public static final String DYNAMIC_INDEXABLES_RAW_PATH =
             SETTINGS + "/" + DYNAMIC_INDEXABLES_RAW;
diff --git a/core/java/android/provider/SearchIndexablesProvider.java b/core/java/android/provider/SearchIndexablesProvider.java
index 68284b4..f4d0cb4 100644
--- a/core/java/android/provider/SearchIndexablesProvider.java
+++ b/core/java/android/provider/SearchIndexablesProvider.java
@@ -204,11 +204,9 @@
      * @param projection list of {@link android.provider.SearchIndexablesContract.RawData} columns
      *                   to put into the cursor. If {@code null} all supported columns should be
      *                   included.
-     *
-     * @hide
      */
     @Nullable
-    public Cursor queryDynamicRawData(String[] projection) {
+    public Cursor queryDynamicRawData(@Nullable String[] projection) {
         // By default no-op;
         return null;
     }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4f84183..1d759af 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -95,6 +95,7 @@
  * The Settings provider contains global system-level device preferences.
  */
 public final class Settings {
+    private static final boolean DEFAULT_OVERRIDEABLE_BY_RESTORE = false;
 
     // Intent actions for Settings
 
@@ -2149,6 +2150,11 @@
      */
     public static final String CALL_METHOD_FLAGS_KEY = "_flags";
 
+    /**
+     * @hide - String argument extra to the fast-path call()-based requests
+     */
+    public static final String CALL_METHOD_OVERRIDEABLE_BY_RESTORE_KEY = "_overrideable_by_restore";
+
     /** @hide - Private call() method to write to 'system' table */
     public static final String CALL_METHOD_PUT_SYSTEM = "PUT_system";
 
@@ -2517,7 +2523,8 @@
         }
 
         public boolean putStringForUser(ContentResolver cr, String name, String value,
-                String tag, boolean makeDefault, final int userHandle) {
+                String tag, boolean makeDefault, final int userHandle,
+                boolean overrideableByRestore) {
             try {
                 Bundle arg = new Bundle();
                 arg.putString(Settings.NameValueTable.VALUE, value);
@@ -2528,6 +2535,9 @@
                 if (makeDefault) {
                     arg.putBoolean(CALL_METHOD_MAKE_DEFAULT_KEY, true);
                 }
+                if (overrideableByRestore) {
+                    arg.putBoolean(CALL_METHOD_OVERRIDEABLE_BY_RESTORE_KEY, true);
+                }
                 IContentProvider cp = mProviderHolder.getProvider(cr);
                 cp.call(cr.getPackageName(), cr.getFeatureId(),
                         mProviderHolder.mUri.getAuthority(), mCallSetCommand, name, arg);
@@ -2736,6 +2746,8 @@
 
         public ArrayMap<String, String> getStringsForPrefix(ContentResolver cr, String prefix,
                 List<String> names) {
+            String namespace = prefix.substring(0, prefix.length() - 1);
+            DeviceConfig.enforceReadPermission(ActivityThread.currentApplication(), namespace);
             ArrayMap<String, String> keyValues = new ArrayMap<>();
             int currentGeneration = -1;
 
@@ -3078,10 +3090,36 @@
             return putStringForUser(resolver, name, value, resolver.getUserId());
         }
 
+        /**
+         * Store a name/value pair into the database. Values written by this method will be
+         * overridden if a restore happens in the future.
+         *
+         * @param resolver to access the database with
+         * @param name to store
+         * @param value to associate with the name
+         *
+         * @return true if the value was set, false on database errors
+         *
+         * @hide
+         */
+        @RequiresPermission(Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE)
+        @SystemApi
+        public static boolean putString(@NonNull ContentResolver resolver,
+                @NonNull String name, @Nullable String value, boolean overrideableByRestore) {
+            return putStringForUser(resolver, name, value, resolver.getUserId(),
+                   overrideableByRestore);
+        }
+
         /** @hide */
         @UnsupportedAppUsage
         public static boolean putStringForUser(ContentResolver resolver, String name, String value,
                 int userHandle) {
+            return putStringForUser(resolver, name, value, userHandle,
+                    DEFAULT_OVERRIDEABLE_BY_RESTORE);
+        }
+
+        private static boolean putStringForUser(ContentResolver resolver, String name, String value,
+                int userHandle, boolean overrideableByRestore) {
             if (MOVED_TO_SECURE.contains(name)) {
                 Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"
                         + " to android.provider.Settings.Secure, value is unchanged.");
@@ -3092,7 +3130,8 @@
                         + " to android.provider.Settings.Global, value is unchanged.");
                 return false;
             }
-            return sNameValueCache.putStringForUser(resolver, name, value, null, false, userHandle);
+            return sNameValueCache.putStringForUser(resolver, name, value, null, false, userHandle,
+                    overrideableByRestore);
         }
 
         /**
@@ -3416,7 +3455,7 @@
                     // need to store the adjusted configuration as the initial settings.
                     Settings.System.putStringForUser(
                             cr, SYSTEM_LOCALES, outConfig.getLocales().toLanguageTags(),
-                            userHandle);
+                            userHandle, DEFAULT_OVERRIDEABLE_BY_RESTORE);
                 }
             }
         }
@@ -3449,7 +3488,8 @@
                 int userHandle) {
             return Settings.System.putFloatForUser(cr, FONT_SCALE, config.fontScale, userHandle) &&
                     Settings.System.putStringForUser(
-                            cr, SYSTEM_LOCALES, config.getLocales().toLanguageTags(), userHandle);
+                            cr, SYSTEM_LOCALES, config.getLocales().toLanguageTags(), userHandle,
+                            DEFAULT_OVERRIDEABLE_BY_RESTORE);
         }
 
         /** @hide */
@@ -5251,6 +5291,24 @@
         }
 
         /**
+         * Store a name/value pair into the database. Values written by this method will be
+         * overridden if a restore happens in the future.
+         *
+         * @param resolver to access the database with
+         * @param name to store
+         * @param value to associate with the name
+         * @return true if the value was set, false on database errors
+         *
+         * @hide
+         */
+        @RequiresPermission(Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE)
+        public static boolean putString(ContentResolver resolver, String name,
+                String value, boolean overrideableByRestore) {
+            return putStringForUser(resolver, name, value, /* tag */ null, /* makeDefault */ false,
+                    resolver.getUserId(), overrideableByRestore);
+        }
+
+        /**
          * Store a name/value pair into the database.
          * @param resolver to access the database with
          * @param name to store
@@ -5265,22 +5323,23 @@
         @UnsupportedAppUsage
         public static boolean putStringForUser(ContentResolver resolver, String name, String value,
                 int userHandle) {
-            return putStringForUser(resolver, name, value, null, false, userHandle);
+            return putStringForUser(resolver, name, value, null, false, userHandle,
+                    DEFAULT_OVERRIDEABLE_BY_RESTORE);
         }
 
         /** @hide */
         @UnsupportedAppUsage
         public static boolean putStringForUser(@NonNull ContentResolver resolver,
                 @NonNull String name, @Nullable String value, @Nullable String tag,
-                boolean makeDefault, @UserIdInt int userHandle) {
+                boolean makeDefault, @UserIdInt int userHandle, boolean overrideableByRestore) {
             if (MOVED_TO_GLOBAL.contains(name)) {
                 Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Secure"
                         + " to android.provider.Settings.Global");
                 return Global.putStringForUser(resolver, name, value,
-                        tag, makeDefault, userHandle);
+                        tag, makeDefault, userHandle, DEFAULT_OVERRIDEABLE_BY_RESTORE);
             }
             return sNameValueCache.putStringForUser(resolver, name, value, tag,
-                    makeDefault, userHandle);
+                    makeDefault, userHandle, overrideableByRestore);
         }
 
         /**
@@ -5329,7 +5388,7 @@
                 @NonNull String name, @Nullable String value, @Nullable String tag,
                 boolean makeDefault) {
             return putStringForUser(resolver, name, value, tag, makeDefault,
-                    resolver.getUserId());
+                    resolver.getUserId(), DEFAULT_OVERRIDEABLE_BY_RESTORE);
         }
 
         /**
@@ -12940,7 +12999,29 @@
          */
         public static boolean putString(ContentResolver resolver,
                 String name, String value) {
-            return putStringForUser(resolver, name, value, null, false, resolver.getUserId());
+            return putStringForUser(resolver, name, value, null, false, resolver.getUserId(),
+                    DEFAULT_OVERRIDEABLE_BY_RESTORE);
+        }
+
+        /**
+         * Store a name/value pair into the database.
+         *
+         * @param resolver to access the database with
+         * @param name to store
+         * @param value to associate with the name
+         * @param tag to associated with the setting.
+         * @param makeDefault whether to make the value the default one.
+         * @param overrideableByRestore whether restore can override this value
+         * @return true if the value was set, false on database errors
+         *
+         * @hide
+         */
+        @RequiresPermission(Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE)
+        public static boolean putString(@NonNull ContentResolver resolver,
+                @NonNull String name, @Nullable String value, @Nullable String tag,
+                boolean makeDefault, boolean overrideableByRestore) {
+            return putStringForUser(resolver, name, value, tag, makeDefault,
+                    resolver.getUserId(), overrideableByRestore);
         }
 
         /**
@@ -12989,7 +13070,7 @@
                 @NonNull String name, @Nullable String value, @Nullable String tag,
                 boolean makeDefault) {
             return putStringForUser(resolver, name, value, tag, makeDefault,
-                    resolver.getUserId());
+                    resolver.getUserId(), DEFAULT_OVERRIDEABLE_BY_RESTORE);
         }
 
         /**
@@ -13051,13 +13132,14 @@
         @UnsupportedAppUsage
         public static boolean putStringForUser(ContentResolver resolver,
                 String name, String value, int userHandle) {
-            return putStringForUser(resolver, name, value, null, false, userHandle);
+            return putStringForUser(resolver, name, value, null, false, userHandle,
+                    DEFAULT_OVERRIDEABLE_BY_RESTORE);
         }
 
         /** @hide */
         public static boolean putStringForUser(@NonNull ContentResolver resolver,
                 @NonNull String name, @Nullable String value, @Nullable String tag,
-                boolean makeDefault, @UserIdInt int userHandle) {
+                boolean makeDefault, @UserIdInt int userHandle, boolean overrideableByRestore) {
             if (LOCAL_LOGV) {
                 Log.v(TAG, "Global.putString(name=" + name + ", value=" + value
                         + " for " + userHandle);
@@ -13067,10 +13149,10 @@
                 Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Global"
                         + " to android.provider.Settings.Secure, value is unchanged.");
                 return Secure.putStringForUser(resolver, name, value, tag,
-                        makeDefault, userHandle);
+                        makeDefault, userHandle, overrideableByRestore);
             }
             return sNameValueCache.putStringForUser(resolver, name, value, tag,
-                    makeDefault, userHandle);
+                    makeDefault, userHandle, overrideableByRestore);
         }
 
         /**
@@ -13937,7 +14019,8 @@
         static boolean putString(@NonNull ContentResolver resolver, @NonNull String namespace,
                 @NonNull String name, @Nullable String value, boolean makeDefault) {
             return sNameValueCache.putStringForUser(resolver, createCompositeName(namespace, name),
-                    value, null, makeDefault, resolver.getUserId());
+                    value, null, makeDefault, resolver.getUserId(),
+                    DEFAULT_OVERRIDEABLE_BY_RESTORE);
         }
 
         /**
@@ -14277,46 +14360,74 @@
 
     /**
      * Activity Action: Show setting page to process the addition of Wi-Fi networks to the user's
-     * saved network list. The app should send a new intent with an extra that holds a maximum of
-     * five {@link android.net.wifi.WifiConfiguration} that specify credentials for the networks to
-     * be added to the user's database. The Intent should be sent via the {@link
-     * android.app.Activity#startActivityForResult(Intent, int)} API.
+     * saved network list. The app should send a new intent with an extra that holds a maximum
+     * of five {@link android.net.wifi.WifiNetworkSuggestion} that specify credentials for the
+     * networks to be added to the user's database. The Intent should be sent via the
+     * {@link android.app.Activity#startActivityForResult(Intent, int)} API.
      * <p>
      * Note: The app sending the Intent to add the credentials doesn't get any ownership over the
      * newly added network(s). For the Wi-Fi stack, these networks will look like the user
      * manually added them from the Settings UI.
      * <p>
-     * Input: The app should put parcelable array list to
-     * {@link android.net.wifi.WifiConfiguration} into the
-     * {@link #EXTRA_WIFI_CONFIGURATION_LIST} extra.
+     * Input: The app should put parcelable array list of
+     * {@link android.net.wifi.WifiNetworkSuggestion} into the {@link #EXTRA_WIFI_NETWORK_LIST}
+     * extra.
      * <p>
      * Output: After {@link android.app.Activity#startActivityForResult(Intent, int)}, the
      * callback {@link android.app.Activity#onActivityResult(int, int, Intent)} will have a
      * result code {@link android.app.Activity#RESULT_OK} to indicate user pressed the save
      * button to save the networks or {@link android.app.Activity#RESULT_CANCELED} to indicate
      * that the user rejected the request. Additionally, an integer array list, stored in
-     * {@link #EXTRA_WIFI_CONFIGURATION_RESULT_LIST}, will indicate the process result of
-     * each network.
+     * {@link #EXTRA_WIFI_NETWORK_RESULT_LIST}, will indicate the process result of each network.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_WIFI_ADD_NETWORKS =
             "android.settings.WIFI_ADD_NETWORKS";
 
     /**
-     * A bundle extra of {@link #ACTION_WIFI_ADD_NETWORKS} intent action that indicates all the
-     * {@link android.net.wifi.WifiConfiguration} that would be saved.
+     * A bundle extra of {@link #ACTION_WIFI_ADD_NETWORKS} intent action that indicates the list
+     * of the {@link android.net.wifi.WifiNetworkSuggestion} elements. The maximum count of the
+     * {@link android.net.wifi.WifiNetworkSuggestion} elements in the list will be five.
+     * <p>
+     * For example:
+     * To provide credentials for one open and one WPA2 networks:
+     *
+     * <pre>{@code
+     * final WifiNetworkSuggestion suggestion1 =
+     *       new WifiNetworkSuggestion.Builder()
+     *       .setSsid("test111111")
+     *       .build();
+     * final WifiNetworkSuggestion suggestion2 =
+     *       new WifiNetworkSuggestion.Builder()
+     *       .setSsid("test222222")
+     *       .setWpa2Passphrase("test123456")
+     *       .build();
+     * final List<WifiNetworkSuggestion> suggestionsList = new ArrayList<>;
+     * suggestionsList.add(suggestion1);
+     * suggestionsList.add(suggestion2);
+     * Bundle bundle = new Bundle();
+     * bundle.putParcelableArrayList(Settings.EXTRA_WIFI_NETWORK_LIST,(ArrayList<? extends
+     * Parcelable>) suggestionsList);
+     * final Intent intent = new Intent(Settings.ACTION_WIFI_ADD_NETWORKS);
+     * intent.putExtras(bundle);
+     * startActivityForResult(intent, 0);
+     * }</pre>
      */
-    public static final String EXTRA_WIFI_CONFIGURATION_LIST =
-            "android.provider.extra.WIFI_CONFIGURATION_LIST";
+    public static final String EXTRA_WIFI_NETWORK_LIST =
+            "android.provider.extra.WIFI_NETWORK_LIST";
 
     /**
      * A bundle extra of the result of {@link #ACTION_WIFI_ADD_NETWORKS} intent action that
-     * indicates the action result of the saved {@link android.net.wifi.WifiConfiguration}. It's
-     * value of AddWifiResult interface, and will be 1:1 mapping to the element in {@link
-     * #EXTRA_WIFI_CONFIGURATION_LIST}.
+     * indicates the action result of the saved {@link android.net.wifi.WifiNetworkSuggestion}.
+     * Its value is a list of integers, and all the elements will be 1:1 mapping to the elements
+     * in {@link #EXTRA_WIFI_NETWORK_LIST}, if user press cancel to cancel the add networks
+     * request, then its value will be null.
+     * <p>
+     * Note: The integer value will be one of the {@link #ADD_WIFI_RESULT_SUCCESS},
+     * {@link #ADD_WIFI_RESULT_ADD_OR_UPDATE_FAILED}, or {@link #ADD_WIFI_RESULT_ALREADY_EXISTS}}.
      */
-    public static final String EXTRA_WIFI_CONFIGURATION_RESULT_LIST =
-            "android.provider.extra.WIFI_CONFIGURATION_RESULT_LIST";
+    public static final String EXTRA_WIFI_NETWORK_RESULT_LIST =
+            "android.provider.extra.WIFI_NETWORK_RESULT_LIST";
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
diff --git a/core/java/android/service/watchdog/ExplicitHealthCheckService.java b/core/java/android/service/watchdog/ExplicitHealthCheckService.java
index 619c507..9950143 100644
--- a/core/java/android/service/watchdog/ExplicitHealthCheckService.java
+++ b/core/java/android/service/watchdog/ExplicitHealthCheckService.java
@@ -24,6 +24,7 @@
 import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -48,7 +49,8 @@
  * <p>To extend this class, you must declare the service in your manifest file with the
  * {@link android.Manifest.permission#BIND_EXPLICIT_HEALTH_CHECK_SERVICE} permission,
  * and include an intent filter with the {@link #SERVICE_INTERFACE} action. In adddition,
- * your implementation must live in {@link PackageManger#SYSTEM_SHARED_LIBRARY_SERVICES}.
+ * your implementation must live in
+ * {@link PackageManager#getServicesSystemSharedLibraryPackageName()}.
  * For example:</p>
  * <pre>
  *     &lt;service android:name=".FooExplicitHealthCheckService"
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index c9d3b92..a4fe6aa 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -985,7 +985,7 @@
      *        TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused.
      */
     public void onRegistrationFailed(@NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn,
-            @NetworkRegistrationInfo.Domain int domain, int causeCode, int additionalCauseCode) {
+            int domain, int causeCode, int additionalCauseCode) {
         // default implementation empty
     }
 
@@ -1277,7 +1277,7 @@
         }
 
         public void onRegistrationFailed(@NonNull CellIdentity cellIdentity,
-                @NonNull String chosenPlmn, @NetworkRegistrationInfo.Domain int domain,
+                @NonNull String chosenPlmn, int domain,
                 int causeCode, int additionalCauseCode) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 4dffa62..e25826c 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -701,7 +701,7 @@
      */
     public void notifyRegistrationFailed(int slotIndex, int subId,
             @NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn,
-            @NetworkRegistrationInfo.Domain int domain, int causeCode, int additionalCauseCode) {
+            int domain, int causeCode, int additionalCauseCode) {
         try {
             sRegistry.notifyRegistrationFailed(slotIndex, subId, cellIdentity,
                     chosenPlmn, domain, causeCode, additionalCauseCode);
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 9d22d30..eb4af1c 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -59,10 +59,9 @@
         DEFAULT_FLAGS.put("settings_wifi_details_datausage_header", "false");
         DEFAULT_FLAGS.put("settings_skip_direction_mutable", "true");
         DEFAULT_FLAGS.put(SETTINGS_WIFITRACKER2, "false");
-        DEFAULT_FLAGS.put("settings_work_profile", "true");
         DEFAULT_FLAGS.put("settings_controller_loading_enhancement", "false");
         DEFAULT_FLAGS.put("settings_conditionals", "false");
-        DEFAULT_FLAGS.put(NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
+        DEFAULT_FLAGS.put(NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true");
     }
 
     /**
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index f324113..9921bf0 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.DeadSystemException;
 
@@ -400,7 +401,7 @@
      * @param message The message you would like logged.
      * @hide
      */
-    // @SystemApi(client= SystemApi.Client.MODULE_LIBRARIES) // TODO Uncomment once http://ag/9956147 is in.
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static int logToRadioBuffer(@Level int priority, @Nullable String tag,
             @Nullable String message) {
         return println_native(LOG_ID_RADIO, priority, tag, message);
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 178b3c0..904c510 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -454,7 +454,7 @@
         mResources = res;
         mDisplayAdjustments = mResources != null
             ? new DisplayAdjustments(mResources.getConfiguration())
-            : daj != null ? new DisplayAdjustments(daj) : null;
+            : daj != null ? new DisplayAdjustments(daj) : new DisplayAdjustments();
         mIsValid = true;
 
         // Cache properties that cannot change as long as the display is valid.
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
new file mode 100644
index 0000000..5c494c1
--- /dev/null
+++ b/core/java/android/view/ImeFocusController.java
@@ -0,0 +1,234 @@
+/*
+ * 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.view;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.UiThread;
+import android.util.Log;
+import android.view.inputmethod.InputMethodManager;
+
+import com.android.internal.inputmethod.InputMethodDebug;
+import com.android.internal.inputmethod.StartInputFlags;
+import com.android.internal.inputmethod.StartInputReason;
+
+/**
+ * Responsible for IME focus handling inside {@link ViewRootImpl}.
+ * @hide
+ */
+public final class ImeFocusController {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "ImeFocusController";
+
+    private final ViewRootImpl mViewRootImpl;
+    private boolean mHasImeFocus = false;
+    private View mServedView;
+    private View mNextServedView;
+
+    @UiThread
+    ImeFocusController(@NonNull ViewRootImpl viewRootImpl) {
+        mViewRootImpl = viewRootImpl;
+    }
+
+    private InputMethodManagerDelegate getImmDelegate() {
+        return mViewRootImpl.mContext.getSystemService(InputMethodManager.class).getDelegate();
+    }
+
+    @UiThread
+    void onTraversal(boolean hasWindowFocus, WindowManager.LayoutParams windowAttribute) {
+        final boolean hasImeFocus = updateImeFocusable(windowAttribute, false /* force */);
+        if (!hasWindowFocus || isInLocalFocusMode(windowAttribute)) {
+            return;
+        }
+        if (hasImeFocus == mHasImeFocus) {
+            return;
+        }
+        mHasImeFocus = hasImeFocus;
+        if (mHasImeFocus) {
+            onPreWindowFocus(true /* hasWindowFocus */, windowAttribute);
+            onPostWindowFocus(mViewRootImpl.mView.findFocus(), true /* hasWindowFocus */,
+                    windowAttribute);
+        }
+    }
+
+    @UiThread
+    void onPreWindowFocus(boolean hasWindowFocus, WindowManager.LayoutParams windowAttribute) {
+        if (!mHasImeFocus || isInLocalFocusMode(windowAttribute)) {
+            return;
+        }
+        if (hasWindowFocus) {
+            getImmDelegate().setCurrentRootView(mViewRootImpl);
+        }
+    }
+
+    @UiThread
+    boolean updateImeFocusable(WindowManager.LayoutParams windowAttribute, boolean force) {
+        final boolean hasImeFocus = WindowManager.LayoutParams.mayUseInputMethod(
+                windowAttribute.flags);
+        if (force) {
+            mHasImeFocus = hasImeFocus;
+        }
+        return hasImeFocus;
+    }
+
+    @UiThread
+    void onPostWindowFocus(View focusedView, boolean hasWindowFocus,
+            WindowManager.LayoutParams windowAttribute) {
+        if (!hasWindowFocus || !mHasImeFocus || isInLocalFocusMode(windowAttribute)) {
+            return;
+        }
+        if (DEBUG) {
+            Log.v(TAG, "onWindowFocus: " + focusedView
+                    + " softInputMode=" + InputMethodDebug.softInputModeToString(
+                    windowAttribute.softInputMode));
+        }
+
+        boolean forceFocus = false;
+        if (getImmDelegate().isRestartOnNextWindowFocus(true /* reset */)) {
+            if (DEBUG) Log.v(TAG, "Restarting due to isRestartOnNextWindowFocus as true");
+            forceFocus = true;
+        }
+        // Update mNextServedView when focusedView changed.
+        final View viewForWindowFocus = focusedView != null ? focusedView : mViewRootImpl.mView;
+        onViewFocusChanged(viewForWindowFocus, true);
+
+        getImmDelegate().startInputAsyncOnWindowFocusGain(viewForWindowFocus,
+                windowAttribute.softInputMode, windowAttribute.flags, forceFocus);
+    }
+
+    public boolean checkFocus(boolean forceNewFocus, boolean startInput) {
+        if (!getImmDelegate().isCurrentRootView(mViewRootImpl)
+                || (mServedView == mNextServedView && !forceNewFocus)) {
+            return false;
+        }
+        if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
+                + " next=" + mNextServedView
+                + " force=" + forceNewFocus
+                + " package="
+                + (mServedView != null ? mServedView.getContext().getPackageName() : "<none>"));
+
+        // Close the connection when no next served view coming.
+        if (mNextServedView == null) {
+            getImmDelegate().finishInput();
+            getImmDelegate().closeCurrentIme();
+            return false;
+        }
+        mServedView = mNextServedView;
+        getImmDelegate().finishComposingText();
+
+        if (startInput) {
+            getImmDelegate().startInput(StartInputReason.CHECK_FOCUS, null, 0, 0, 0);
+        }
+        return true;
+    }
+
+    @UiThread
+    void onViewFocusChanged(View view, boolean hasFocus) {
+        if (view == null || view.isTemporarilyDetached()) {
+            return;
+        }
+        if (!getImmDelegate().isCurrentRootView(view.getViewRootImpl())) {
+            return;
+        }
+        if (mServedView == view || !view.hasImeFocus() || !view.hasWindowFocus()) {
+            return;
+        }
+        mNextServedView = hasFocus ? view : null;
+        mViewRootImpl.dispatchCheckFocus();
+    }
+
+    @UiThread
+    void onViewDetachedFromWindow(View view) {
+        if (!getImmDelegate().isCurrentRootView(view.getViewRootImpl())) {
+            return;
+        }
+        if (mServedView == view) {
+            mNextServedView = null;
+            mViewRootImpl.dispatchCheckFocus();
+        }
+    }
+
+    @UiThread
+    void onWindowDismissed() {
+        if (!getImmDelegate().isCurrentRootView(mViewRootImpl)) {
+            return;
+        }
+        if (mServedView != null) {
+            getImmDelegate().finishInput();
+        }
+        getImmDelegate().setCurrentRootView(null);
+        mHasImeFocus = false;
+    }
+
+    /**
+     * @param windowAttribute {@link WindowManager.LayoutParams} to be checked.
+     * @return Whether the window is in local focus mode or not.
+     */
+    @AnyThread
+    private static boolean isInLocalFocusMode(WindowManager.LayoutParams windowAttribute) {
+        return (windowAttribute.flags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0;
+    }
+
+    int onProcessImeInputStage(Object token, InputEvent event,
+            WindowManager.LayoutParams windowAttribute,
+            InputMethodManager.FinishedInputEventCallback callback) {
+        if (!mHasImeFocus || isInLocalFocusMode(windowAttribute)) {
+            return InputMethodManager.DISPATCH_NOT_HANDLED;
+        }
+        final InputMethodManager imm =
+                mViewRootImpl.mContext.getSystemService(InputMethodManager.class);
+        if (imm == null) {
+            return InputMethodManager.DISPATCH_NOT_HANDLED;
+        }
+        return imm.dispatchInputEvent(event, token, callback, mViewRootImpl.mHandler);
+    }
+
+    /**
+     * A delegate implementing some basic {@link InputMethodManager} APIs.
+     * @hide
+     */
+    public interface InputMethodManagerDelegate {
+        boolean startInput(@StartInputReason int startInputReason, View focusedView,
+                @StartInputFlags int startInputFlags,
+                @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags);
+        void startInputAsyncOnWindowFocusGain(View rootView,
+                @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags,
+                boolean forceNewFocus);
+        void finishInput();
+        void closeCurrentIme();
+        void finishComposingText();
+        void setCurrentRootView(ViewRootImpl rootView);
+        boolean isCurrentRootView(ViewRootImpl rootView);
+        boolean isRestartOnNextWindowFocus(boolean reset);
+    }
+
+    public View getServedView() {
+        return mServedView;
+    }
+
+    public View getNextServedView() {
+        return mNextServedView;
+    }
+
+    public void setServedView(View view) {
+        mServedView = view;
+    }
+
+    public void setNextServedView(View view) {
+        mNextServedView = view;
+    }
+}
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 6589e75..69d0105 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -34,6 +34,7 @@
 import android.util.SparseSetArray;
 import android.view.InsetsController.LayoutInsetsDuringAnimation;
 import android.view.InsetsState.InternalInsetsSide;
+import android.view.InsetsState.InternalInsetsType;
 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsAnimationCallback.AnimationBounds;
@@ -92,6 +93,7 @@
         mController = controller;
         mInitialInsetsState = new InsetsState(state, true /* copySources */);
         mCurrentInsets = getInsetsFromState(mInitialInsetsState, frame, null /* typeSideMap */);
+        mPendingInsets = mCurrentInsets;
         mHiddenInsets = calculateInsets(mInitialInsetsState, frame, controls, false /* shown */,
                 null /* typeSideMap */);
         mShownInsets = calculateInsets(mInitialInsetsState, frame, controls, true /* shown */,
@@ -131,6 +133,10 @@
         return mTypes;
     }
 
+    boolean controlsInternalType(@InternalInsetsType int type) {
+        return InsetsState.toInternalType(mTypes).contains(type);
+    }
+
     @Override
     public void setInsetsAndAlpha(Insets insets, float alpha, float fraction) {
         if (mFinished) {
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 775490c..e2739c4 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -28,6 +28,7 @@
 import android.annotation.NonNull;
 import android.graphics.Insets;
 import android.graphics.Rect;
+import android.net.InvalidPacketException.ErrorCode;
 import android.os.RemoteException;
 import android.util.ArraySet;
 import android.util.Log;
@@ -61,15 +62,9 @@
 
     private static final int ANIMATION_DURATION_SHOW_MS = 275;
     private static final int ANIMATION_DURATION_HIDE_MS = 340;
-    private static final int DIRECTION_NONE = 0;
-    private static final int DIRECTION_SHOW = 1;
-    private static final int DIRECTION_HIDE = 2;
 
     static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
 
-    @IntDef ({DIRECTION_NONE, DIRECTION_SHOW, DIRECTION_HIDE})
-    private @interface AnimationDirection{}
-
     /**
      * Layout mode during insets animation: The views should be laid out as if the changing inset
      * types are fully shown. Before starting the animation, {@link View#onApplyWindowInsets} will
@@ -101,6 +96,28 @@
     @interface LayoutInsetsDuringAnimation {
     }
 
+    /** Not running an animation. */
+    @VisibleForTesting
+    public static final int ANIMATION_TYPE_NONE = -1;
+
+    /** Running animation will show insets */
+    @VisibleForTesting
+    public static final int ANIMATION_TYPE_SHOW = 0;
+
+    /** Running animation will hide insets */
+    @VisibleForTesting
+    public static final int ANIMATION_TYPE_HIDE = 1;
+
+    /** Running animation is controlled by user via {@link #controlWindowInsetsAnimation} */
+    @VisibleForTesting
+    public static final int ANIMATION_TYPE_USER = 2;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {ANIMATION_TYPE_NONE, ANIMATION_TYPE_SHOW, ANIMATION_TYPE_HIDE,
+            ANIMATION_TYPE_USER})
+    @interface AnimationType {
+    }
+
     /**
      * Translation animation evaluator.
      */
@@ -145,7 +162,6 @@
         public void onReady(WindowInsetsAnimationController controller, int types) {
             mController = controller;
 
-            mAnimationDirection = mShow ? DIRECTION_SHOW : DIRECTION_HIDE;
             mAnimator = ObjectAnimator.ofObject(
                     controller,
                     new InsetsProperty(),
@@ -176,7 +192,6 @@
         }
 
         private void onAnimationFinish() {
-            mAnimationDirection = DIRECTION_NONE;
             mController.finish(mShow);
         }
 
@@ -193,6 +208,20 @@
         }
     }
 
+    /**
+     * Represents a running animation
+     */
+    private static class RunningAnimation {
+
+        RunningAnimation(InsetsAnimationControlImpl control, int type) {
+            this.control = control;
+            this.type = type;
+        }
+
+        final InsetsAnimationControlImpl control;
+        final @AnimationType int type;
+    }
+
     private final String TAG = "InsetsControllerImpl";
 
     private final InsetsState mState = new InsetsState();
@@ -203,7 +232,7 @@
     private final ViewRootImpl mViewRoot;
 
     private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
-    private final ArrayList<InsetsAnimationControlImpl> mAnimationControls = new ArrayList<>();
+    private final ArrayList<RunningAnimation> mRunningAnimations = new ArrayList<>();
     private final ArrayList<InsetsAnimationControlImpl> mTmpFinishedControls = new ArrayList<>();
     private WindowInsets mLastInsets;
 
@@ -213,7 +242,6 @@
 
     private final Rect mLastLegacyContentInsets = new Rect();
     private final Rect mLastLegacyStableInsets = new Rect();
-    private @AnimationDirection int mAnimationDirection;
 
     private int mPendingTypesToShow;
 
@@ -226,7 +254,7 @@
         mViewRoot = viewRoot;
         mAnimCallback = () -> {
             mAnimCallbackScheduled = false;
-            if (mAnimationControls.isEmpty()) {
+            if (mRunningAnimations.isEmpty()) {
                 return;
             }
             if (mViewRoot.mView == null) {
@@ -236,9 +264,9 @@
 
             mTmpFinishedControls.clear();
             InsetsState state = new InsetsState(mState, true /* copySources */);
-            for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
-                InsetsAnimationControlImpl control = mAnimationControls.get(i);
-                if (mAnimationControls.get(i).applyChangeInsets(state)) {
+            for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
+                InsetsAnimationControlImpl control = mRunningAnimations.get(i).control;
+                if (control.applyChangeInsets(state)) {
                     mTmpFinishedControls.add(control);
                 }
             }
@@ -349,18 +377,13 @@
         int typesReady = 0;
         final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
         for (int i = internalTypes.size() - 1; i >= 0; i--) {
-            InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
-            if (mAnimationDirection == DIRECTION_HIDE) {
-                // Only one animator (with multiple InsetsType) can run at a time.
-                // previous one should be cancelled for simplicity.
-                cancelExistingAnimation();
-            } else if (consumer.isRequestedVisible()
-                    && (mAnimationDirection == DIRECTION_NONE
-                    || mAnimationDirection == DIRECTION_HIDE)) {
+            @InternalInsetsType int internalType = internalTypes.valueAt(i);
+            @AnimationType int animationType = getAnimationType(internalType);
+            InsetsSourceConsumer consumer = getSourceConsumer(internalType);
+            if (mState.getSource(internalType).isVisible() && animationType == ANIMATION_TYPE_NONE
+                    || animationType == ANIMATION_TYPE_SHOW) {
                 // no-op: already shown or animating in (because window visibility is
                 // applied before starting animation).
-                // TODO: When we have more than one types: handle specific case when
-                // show animation is going on, but the current type is not becoming visible.
                 continue;
             }
             typesReady |= InsetsState.toPublicType(consumer.getType());
@@ -377,12 +400,11 @@
         int typesReady = 0;
         final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
         for (int i = internalTypes.size() - 1; i >= 0; i--) {
-            InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
-            if (mAnimationDirection == DIRECTION_SHOW) {
-                cancelExistingAnimation();
-            } else if (!consumer.isRequestedVisible()
-                    && (mAnimationDirection == DIRECTION_NONE
-                    || mAnimationDirection == DIRECTION_HIDE)) {
+            @InternalInsetsType int internalType = internalTypes.valueAt(i);
+            @AnimationType int animationType = getAnimationType(internalType);
+            InsetsSourceConsumer consumer = getSourceConsumer(internalType);
+            if (!mState.getSource(internalType).isVisible() && animationType == ANIMATION_TYPE_NONE
+                    || animationType == ANIMATION_TYPE_HIDE) {
                 // no-op: already hidden or animating out.
                 continue;
             }
@@ -394,11 +416,13 @@
     @Override
     public void controlWindowInsetsAnimation(@InsetsType int types, long durationMs,
             WindowInsetsAnimationControlListener listener) {
-        controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs);
+        controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs,
+                ANIMATION_TYPE_USER);
     }
 
     private void controlWindowInsetsAnimation(@InsetsType int types,
-            WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs) {
+            WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs,
+            @AnimationType int animationType) {
         // If the frame of our window doesn't span the entire display, the control API makes very
         // little sense, as we don't deal with negative insets. So just cancel immediately.
         if (!mState.getDisplayFrame().equals(mFrame)) {
@@ -406,12 +430,12 @@
             return;
         }
         controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, false /* fade */,
-                getLayoutInsetsDuringAnimationMode(types));
+                animationType, getLayoutInsetsDuringAnimationMode(types));
     }
 
     private void controlAnimationUnchecked(@InsetsType int types,
             WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
-            long durationMs, boolean fade,
+            long durationMs, boolean fade, @AnimationType int animationType,
             @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
         if (types == 0) {
             // nothing to animate.
@@ -444,7 +468,7 @@
         final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls,
                 frame, mState, listener, typesReady, this, durationMs, fade,
                 layoutInsetsDuringAnimation);
-        mAnimationControls.add(controller);
+        mRunningAnimations.add(new RunningAnimation(controller, animationType));
     }
 
     /**
@@ -523,10 +547,10 @@
     }
 
     private void cancelExistingControllers(@InsetsType int types) {
-        for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
-            InsetsAnimationControlImpl control = mAnimationControls.get(i);
+        for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
+            InsetsAnimationControlImpl control = mRunningAnimations.get(i).control;
             if ((control.getTypes() & types) != 0) {
-                cancelAnimation(control);
+                cancelAnimation(control, true /* invokeCallback */);
             }
         }
     }
@@ -534,7 +558,7 @@
     @VisibleForTesting
     @Override
     public void notifyFinished(InsetsAnimationControlImpl controller, boolean shown) {
-        mAnimationControls.remove(controller);
+        cancelAnimation(controller, false /* invokeCallback */);
         if (shown) {
             showDirectly(controller.getTypes());
         } else {
@@ -554,17 +578,24 @@
     }
 
     void notifyControlRevoked(InsetsSourceConsumer consumer) {
-        for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
-            InsetsAnimationControlImpl control = mAnimationControls.get(i);
+        for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
+            InsetsAnimationControlImpl control = mRunningAnimations.get(i).control;
             if ((control.getTypes() & toPublicType(consumer.getType())) != 0) {
-                cancelAnimation(control);
+                cancelAnimation(control, true /* invokeCallback */);
             }
         }
     }
 
-    private void cancelAnimation(InsetsAnimationControlImpl control) {
-        control.onCancelled();
-        mAnimationControls.remove(control);
+    private void cancelAnimation(InsetsAnimationControlImpl control, boolean invokeCallback) {
+        if (invokeCallback) {
+            control.onCancelled();
+        }
+        for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
+            if (mRunningAnimations.get(i).control == control) {
+                mRunningAnimations.remove(i);
+                break;
+            }
+        }
     }
 
     private void applyLocalVisibilityOverride() {
@@ -622,8 +653,15 @@
         }
     }
 
-    boolean isAnimating() {
-        return mAnimationDirection != DIRECTION_NONE;
+    @VisibleForTesting
+    public @AnimationType int getAnimationType(@InternalInsetsType int type) {
+        for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
+            InsetsAnimationControlImpl control = mRunningAnimations.get(i).control;
+            if (control.controlsInternalType(type)) {
+                return mRunningAnimations.get(i).type;
+            }
+        }
+        return ANIMATION_TYPE_NONE;
     }
 
     private InsetsSourceConsumer createConsumerOfType(int type) {
@@ -665,8 +703,8 @@
         // and hidden state insets are correct.
         controlAnimationUnchecked(
                 types, listener, mState.getDisplayFrame(), fromIme, listener.getDurationMs(),
-                true /* fade */, show
-                        ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
+                true /* fade */, show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE,
+                show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
                         : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN);
     }
 
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index b2a5d91..8a1b45a 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import static android.view.InsetsController.ANIMATION_TYPE_NONE;
+
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.view.InsetsState.InternalInsetsType;
@@ -172,7 +174,7 @@
 
     private void applyHiddenToControl() {
         if (mSourceControl == null || mSourceControl.getLeash() == null
-                || mController.isAnimating()) {
+                || mController.getAnimationType(mType) != ANIMATION_TYPE_NONE) {
             return;
         }
 
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index ff8455a..38416ee 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -122,6 +122,8 @@
     private static native void nativeSetColor(long transactionObj, long nativeObject, float[] color);
     private static native void nativeSetFlags(long transactionObj, long nativeObject,
             int flags, int mask);
+    private static native void nativeSetFrameRateSelectionPriority(long transactionObj,
+            long nativeObject, int priority);
     private static native void nativeSetWindowCrop(long transactionObj, long nativeObject,
             int l, int t, int r, int b);
     private static native void nativeSetCornerRadius(long transactionObj, long nativeObject,
@@ -157,7 +159,6 @@
     private static native DisplayedContentSample nativeGetDisplayedContentSample(
             IBinder displayToken, long numFrames, long timestamp);
     private static native int nativeGetActiveConfig(IBinder displayToken);
-    private static native boolean nativeSetActiveConfig(IBinder displayToken, int id);
     private static native boolean nativeSetDesiredDisplayConfigSpecs(IBinder displayToken,
             SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs);
     private static native SurfaceControl.DesiredDisplayConfigSpecs
@@ -1480,16 +1481,6 @@
 
 
     /**
-     * @hide
-     */
-    public static boolean setActiveConfig(IBinder displayToken, int id) {
-        if (displayToken == null) {
-            throw new IllegalArgumentException("displayToken must not be null");
-        }
-        return nativeSetActiveConfig(displayToken, id);
-    }
-
-    /**
      * Contains information about desired display configuration.
      *
      * @hide
@@ -2245,6 +2236,19 @@
         }
 
         /**
+         * This information is passed to SurfaceFlinger to decide which window should have a
+         * priority when deciding about the refresh rate of the display. All windows have the
+         * lowest priority by default.
+         * @hide
+         */
+        @NonNull
+        public Transaction setFrameRateSelectionPriority(@NonNull SurfaceControl sc, int priority) {
+            sc.checkNotReleased();
+            nativeSetFrameRateSelectionPriority(mNativeObject, sc.mNativeObject, priority);
+            return this;
+        }
+
+        /**
          * Request that a given surface and it's sub-tree be shown.
          *
          * @param sc The surface to show.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 13d609b..6724e9d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -130,7 +130,6 @@
 import android.view.contentcapture.ContentCaptureSession;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
 import android.view.inspector.InspectableProperty;
 import android.view.inspector.InspectableProperty.EnumEntry;
 import android.view.inspector.InspectableProperty.FlagEntry;
@@ -7942,12 +7941,12 @@
             if (isPressed()) {
                 setPressed(false);
             }
-            if (mAttachInfo != null && mAttachInfo.mHasWindowFocus) {
-                notifyFocusChangeToInputMethodManager(false /* hasFocus */);
+            if (hasWindowFocus()) {
+                notifyFocusChangeToImeFocusController(false /* hasFocus */);
             }
             onFocusLost();
-        } else if (mAttachInfo != null && mAttachInfo.mHasWindowFocus) {
-            notifyFocusChangeToInputMethodManager(true /* hasFocus */);
+        } else if (hasWindowFocus()) {
+            notifyFocusChangeToImeFocusController(true /* hasFocus */);
         }
 
         invalidate(true);
@@ -7964,23 +7963,15 @@
     }
 
     /**
-     * Notify {@link InputMethodManager} about the focus change of the {@link View}.
-     *
-     * <p>Does nothing when {@link InputMethodManager} is not available.</p>
+     * Notify {@link ImeFocusController} about the focus change of the {@link View}.
      *
      * @param hasFocus {@code true} when the {@link View} is being focused.
      */
-    private void notifyFocusChangeToInputMethodManager(boolean hasFocus) {
-        final InputMethodManager imm =
-                getContext().getSystemService(InputMethodManager.class);
-        if (imm == null) {
+    private void notifyFocusChangeToImeFocusController(boolean hasFocus) {
+        if (mAttachInfo == null) {
             return;
         }
-        if (hasFocus) {
-            imm.focusIn(this);
-        } else {
-            imm.focusOut(this);
-        }
+        mAttachInfo.mViewRootImpl.getImeFocusController().onViewFocusChanged(this, hasFocus);
     }
 
     /** @hide */
@@ -12611,11 +12602,10 @@
                 return findViewInsideOutShouldExist(root, mNextFocusForwardId);
             case FOCUS_BACKWARD: {
                 if (mID == View.NO_ID) return null;
-                final int id = mID;
                 return root.findViewByPredicateInsideOut(this, new Predicate<View>() {
                     @Override
                     public boolean test(View t) {
-                        return t.mNextFocusForwardId == id;
+                        return t.findViewById(t.mNextFocusForwardId) == View.this;
                     }
                 });
             }
@@ -13918,7 +13908,7 @@
         mPrivateFlags3 &= ~PFLAG3_TEMPORARY_DETACH;
         onFinishTemporaryDetach();
         if (hasWindowFocus() && hasFocus()) {
-            notifyFocusChangeToInputMethodManager(true /* hasFocus */);
+            notifyFocusChangeToImeFocusController(true /* hasFocus */);
         }
         notifyEnterOrExitForAutoFillIfNeeded(true);
         notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
@@ -14326,13 +14316,13 @@
             }
             mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
             if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
-                notifyFocusChangeToInputMethodManager(false /* hasFocus */);
+                notifyFocusChangeToImeFocusController(false /* hasFocus */);
             }
             removeLongPressCallback();
             removeTapCallback();
             onFocusLost();
         } else if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
-            notifyFocusChangeToInputMethodManager(true /* hasFocus */);
+            notifyFocusChangeToImeFocusController(true /* hasFocus */);
         }
 
         refreshDrawableState();
@@ -14349,6 +14339,14 @@
     }
 
     /**
+     * @return {@code true} if this view is in a window that currently has IME focusable state.
+     * @hide
+     */
+    public boolean hasImeFocus() {
+        return mAttachInfo != null && mAttachInfo.mHasImeFocus;
+    }
+
+    /**
      * Dispatch a view visibility change down the view hierarchy.
      * ViewGroups should override to route to their children.
      * @param changedView The view whose visibility changed. Could be 'this' or
@@ -19644,7 +19642,7 @@
         rebuildOutline();
 
         if (isFocused()) {
-            notifyFocusChangeToInputMethodManager(true /* hasFocus */);
+            notifyFocusChangeToImeFocusController(true /* hasFocus */);
         }
     }
 
@@ -20227,9 +20225,8 @@
         onDetachedFromWindow();
         onDetachedFromWindowInternal();
 
-        InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
-        if (imm != null) {
-            imm.onViewDetachedFromWindow(this);
+        if (info != null) {
+            info.mViewRootImpl.getImeFocusController().onViewDetachedFromWindow(this);
         }
 
         ListenerInfo li = mListenerInfo;
@@ -28565,6 +28562,11 @@
         boolean mHasWindowFocus;
 
         /**
+         * Indicates whether the view's window has IME focused.
+         */
+        boolean mHasImeFocus;
+
+        /**
          * The current visibility of the window.
          */
         int mWindowVisibility;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2ef944f..17b945b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -453,7 +453,6 @@
     boolean mReportNextDraw;
     boolean mFullRedrawNeeded;
     boolean mNewSurfaceNeeded;
-    boolean mLastWasImTarget;
     boolean mForceNextWindowRelayout;
     CountDownLatch mWindowDrawCountDown;
 
@@ -619,6 +618,16 @@
             InputEventConsistencyVerifier.isInstrumentationEnabled() ?
                     new InputEventConsistencyVerifier(this, 0) : null;
 
+    private final ImeFocusController mImeFocusController;
+
+    /**
+     * @return {@link ImeFocusController} for this instance.
+     */
+    @NonNull
+    public ImeFocusController getImeFocusController() {
+        return mImeFocusController;
+    }
+
     private final InsetsController mInsetsController = new InsetsController(this);
 
     private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker();
@@ -704,6 +713,7 @@
         }
 
         loadSystemProperties();
+        mImeFocusController = new ImeFocusController(this);
     }
 
     public static void addFirstDrawHandler(Runnable callback) {
@@ -1054,11 +1064,6 @@
         }
     }
 
-    /** Whether the window is in local focus mode or not */
-    private boolean isInLocalFocusMode() {
-        return (mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0;
-    }
-
     @UnsupportedAppUsage
     public int getWindowFlags() {
         return mWindowAttributes.flags;
@@ -2892,19 +2897,7 @@
         mViewVisibility = viewVisibility;
         mHadWindowFocus = hasWindowFocus;
 
-        if (hasWindowFocus && !isInLocalFocusMode()) {
-            final boolean imTarget = WindowManager.LayoutParams
-                    .mayUseInputMethod(mWindowAttributes.flags);
-            if (imTarget != mLastWasImTarget) {
-                mLastWasImTarget = imTarget;
-                InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
-                if (imm != null && imTarget) {
-                    imm.onPreWindowFocus(mView, hasWindowFocus);
-                    imm.onPostWindowFocus(mView, mView.findFocus(),
-                            mWindowAttributes.softInputMode, mWindowAttributes.flags);
-                }
-            }
-        }
+        mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes);
 
         // Remember if we must report the next draw.
         if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
@@ -3072,14 +3065,10 @@
             }
 
             mAttachInfo.mHasWindowFocus = hasWindowFocus;
+            mAttachInfo.mHasImeFocus = mImeFocusController.updateImeFocusable(
+                    mWindowAttributes, true /* force */);
+            mImeFocusController.onPreWindowFocus(hasWindowFocus, mWindowAttributes);
 
-            mLastWasImTarget = WindowManager.LayoutParams
-                    .mayUseInputMethod(mWindowAttributes.flags);
-
-            InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
-            if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
-                imm.onPreWindowFocus(mView, hasWindowFocus);
-            }
             if (mView != null) {
                 mAttachInfo.mKeyDispatchState.reset();
                 mView.dispatchWindowFocusChanged(hasWindowFocus);
@@ -3091,11 +3080,10 @@
 
             // Note: must be done after the focus change callbacks,
             // so all of the view state is set up correctly.
+            mImeFocusController.onPostWindowFocus(mView.findFocus(), hasWindowFocus,
+                    mWindowAttributes);
+
             if (hasWindowFocus) {
-                if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
-                    imm.onPostWindowFocus(mView, mView.findFocus(),
-                            mWindowAttributes.softInputMode, mWindowAttributes.flags);
-                }
                 // Clear the forward bit.  We can just do this directly, since
                 // the window manager doesn't care about it.
                 mWindowAttributes.softInputMode &=
@@ -4891,10 +4879,7 @@
                     enqueueInputEvent(event, null, 0, true);
                 } break;
                 case MSG_CHECK_FOCUS: {
-                    InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
-                    if (imm != null) {
-                        imm.checkFocus();
-                    }
+                    getImeFocusController().checkFocus(false, true);
                 } break;
                 case MSG_CLOSE_SYSTEM_DIALOGS: {
                     if (mView != null) {
@@ -5458,23 +5443,20 @@
 
         @Override
         protected int onProcess(QueuedInputEvent q) {
-            if (mLastWasImTarget && !isInLocalFocusMode()) {
-                InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
-                if (imm != null) {
-                    final InputEvent event = q.mEvent;
-                    if (DEBUG_IMF) Log.v(mTag, "Sending input event to IME: " + event);
-                    int result = imm.dispatchInputEvent(event, q, this, mHandler);
-                    if (result == InputMethodManager.DISPATCH_HANDLED) {
-                        return FINISH_HANDLED;
-                    } else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
-                        // The IME could not handle it, so skip along to the next InputStage
-                        return FORWARD;
-                    } else {
-                        return DEFER; // callback will be invoked later
-                    }
-                }
+            final int result = mImeFocusController.onProcessImeInputStage(
+                    q, q.mEvent, mWindowAttributes, this);
+            switch (result) {
+                case InputMethodManager.DISPATCH_IN_PROGRESS:
+                    // callback will be invoked later
+                    return DEFER;
+                case InputMethodManager.DISPATCH_NOT_HANDLED:
+                    // The IME could not handle it, so skip along to the next InputStage
+                    return FORWARD;
+                case InputMethodManager.DISPATCH_HANDLED:
+                    return FINISH_HANDLED;
+                default:
+                    throw new IllegalStateException("Unexpected result=" + result);
             }
-            return FORWARD;
         }
 
         @Override
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 7d5564e..ccfbd7e 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -487,11 +487,8 @@
         ViewRootImpl root = mRoots.get(index);
         View view = root.getView();
 
-        if (view != null) {
-            InputMethodManager imm = view.getContext().getSystemService(InputMethodManager.class);
-            if (imm != null) {
-                imm.windowDismissed(mViews.get(index).getWindowToken());
-            }
+        if (root != null) {
+            root.getImeFocusController().onWindowDismissed();
         }
         boolean deferred = root.die(immediate);
         if (view != null) {
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 3dfeffb..9cbba87 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -1444,6 +1444,29 @@
         return null;
     }
 
+    /**
+     *
+     * Sets an {@link IWindowMagnificationConnection} that manipulates window magnification.
+     *
+     * @param connection The connection that manipulates window magnification.
+     * @hide
+     */
+    public void setWindowMagnificationConnection(@Nullable
+            IWindowMagnificationConnection connection) {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+        }
+        try {
+            service.setWindowMagnificationConnection(connection);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error setting window magnfication connection", re);
+        }
+    }
+
     private IAccessibilityManager getServiceLocked() {
         if (mService == null) {
             tryConnectToServiceLocked(null);
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index fcaaa2e..7f8fdf8 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -25,6 +25,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
 import android.view.accessibility.IAccessibilityManagerClient;
+import android.view.accessibility.IWindowMagnificationConnection;
 import android.view.IWindow;
 
 /**
@@ -86,4 +87,5 @@
 
     oneway void registerSystemAction(in RemoteAction action, int actionId);
     oneway void unregisterSystemAction(int actionId);
+    oneway void setWindowMagnificationConnection(in IWindowMagnificationConnection connection);
 }
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
new file mode 100644
index 0000000..0b45c6b
--- /dev/null
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
@@ -0,0 +1,75 @@
+/*
+ * 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.view.accessibility;
+
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.view.accessibility.IWindowMagnificationConnectionCallback;
+
+/**
+ * Interface for interaction between {@link AccessibilityManagerService}
+ * and {@link WindowMagnification} in SystemUI.
+ *
+ * @hide
+ */
+oneway interface IWindowMagnificationConnection {
+
+    /**
+     * Enables window magnification on specifed display with specified center and scale.
+     *
+     * @param displayId The logical display id.
+     * @param scale magnification scale.
+     * @param centerX the screen-relative X coordinate around which to center,
+     *                or {@link Float#NaN} to leave unchanged.
+     * @param centerY the screen-relative Y coordinate around which to center,
+     *                or {@link Float#NaN} to leave unchanged.
+     */
+    void enableWindowMagnification(int displayId, float scale, float centerX, float centerY);
+
+    /**
+     * Sets the scale of the window magnifier on specifed display.
+     *
+     * @param displayId The logical display id.
+     * @param scale magnification scale.
+     */
+    void setScale(int displayId, float scale);
+
+     /**
+     * Disables window magnification on specifed display.
+     *
+     * @param displayId The logical display id.
+     */
+    void disableWindowMagnification(int displayId);
+
+    /**
+     * Moves the window magnifier on the specifed display.
+     *
+     * @param offsetX the amount in pixels to offset the window magnifier in the X direction, in
+     *                current screen pixels.
+     * @param offsetY the amount in pixels to offset the window magnifier in the Y direction, in
+     *                current screen pixels.
+     */
+    void moveWindowMagnifier(int displayId, float offsetX, float offsetY);
+
+    /**
+     * Sets {@link IWindowMagnificationConnectionCallback} to receive the request or the callback.
+     *
+     *
+     * @param callback the interface to be called.
+     */
+    void setConnectionCallback(in IWindowMagnificationConnectionCallback callback);
+}
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
new file mode 100644
index 0000000..7327bb5
--- /dev/null
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.view.accessibility;
+
+import android.graphics.Rect;
+
+/**
+ * interface to notify the change of the window magnifier bounds and request to change
+ * the magnification mode.
+ *
+ * @hide
+ */
+ oneway interface IWindowMagnificationConnectionCallback {
+
+    /**
+     * Called when the bounds of the window magnifier is changed.
+     *
+     * @param displayId The logical display id.
+     * @param bounds The window magnifier bounds in screen coordinates.
+     */
+    void onWindowMagnifierBoundsChanged(int display, in Rect bounds);
+    /**
+     * Changes the magnification mode on specified display. It is invoked by System UI when the
+     *  switch button is toggled.
+     *
+     * @param displayId The logical display id.
+     * @param magnificationMode new magnification mode.
+     */
+    void onChangeMagnificationMode(int display, int magnificationMode);
+}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 9c04b39..c159f89 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -230,6 +230,7 @@
     /** @hide */ public static final int ACTION_VIEW_ENTERED =  2;
     /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
     /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
+    /** @hide */ public static final int ACTION_RESPONSE_EXPIRED = 5;
 
     /** @hide */ public static final int NO_LOGGING = 0;
     /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
@@ -776,11 +777,19 @@
      *
      * @see AutofillClient#autofillClientIsVisibleForAutofill()
      *
+     * @param isExpiredResponse The response has expired or not
+     *
      * {@hide}
      */
-    public void onInvisibleForAutofill() {
+    public void onInvisibleForAutofill(boolean isExpiredResponse) {
         synchronized (mLock) {
             mOnInvisibleCalled = true;
+
+            if (isExpiredResponse) {
+                // Notify service the response has expired.
+                updateSessionLocked(/* id= */ null, /* bounds= */ null, /* value= */ null,
+                        ACTION_RESPONSE_EXPIRED, /* flags= */ 0);
+            }
         }
     }
 
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index ae2fb8e..d5d631a 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -758,8 +758,9 @@
             Context context;
             if (mTargetView != null) {
                 context = mTargetView.getContext();
-            } else if (mIMM.mServedView != null) {
-                context = mIMM.mServedView.getContext();
+            } else if (mIMM.mCurRootView != null) {
+                final View servedView = mIMM.mCurRootView.getImeFocusController().getServedView();
+                context = servedView != null ? servedView.getContext() : null;
             } else {
                 context = null;
             }
diff --git a/core/java/android/view/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java
index c10144e..a32ea4b 100644
--- a/core/java/android/view/inputmethod/InlineSuggestion.java
+++ b/core/java/android/view/inputmethod/InlineSuggestion.java
@@ -19,6 +19,7 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.content.Context;
 import android.os.AsyncTask;
 import android.os.Parcelable;
@@ -61,6 +62,20 @@
     private final @Nullable IInlineContentProvider mContentProvider;
 
     /**
+     * Creates a new {@link InlineSuggestion}, for testing purpose.
+     *
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public static InlineSuggestion newInlineSuggestion(@NonNull InlineSuggestionInfo info) {
+        return new InlineSuggestion(info, null);
+    }
+
+
+
+
+    /**
      * Inflates a view with the content of this suggestion at a specific size.
      * The size must be between the {@link InlinePresentationSpec#getMinSize() min size}
      * and the {@link InlinePresentationSpec#getMaxSize() max size} of the presentation
@@ -271,10 +286,10 @@
     };
 
     @DataClass.Generated(
-            time = 1575933636929L,
+            time = 1578972138081L,
             codegenVersion = "1.0.14",
             sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestion.java",
-            inputSignatures = "private static final  java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\npublic  void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.view.View>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
+            inputSignatures = "private static final  java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic  void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.view.View>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/inputmethod/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
index 07fce31..195b63a 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.os.Parcelable;
 import android.view.inline.InlinePresentationSpec;
 
@@ -53,6 +54,19 @@
     /** Hints for the type of data being suggested. */
     private final @Nullable String[] mAutofillHints;
 
+    /**
+     * Creates a new {@link InlineSuggestionInfo}, for testing purpose.
+     *
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public static InlineSuggestionInfo newInlineSuggestionInfo(
+            @NonNull InlinePresentationSpec presentationSpec,
+            @NonNull @Source String source,
+            @Nullable String[] autofillHints) {
+        return new InlineSuggestionInfo(presentationSpec, source, autofillHints);
+    }
 
 
 
@@ -247,10 +261,10 @@
     };
 
     @DataClass.Generated(
-            time = 1574406074120L,
+            time = 1578972121865L,
             codegenVersion = "1.0.14",
             sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionInfo.java",
-            inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mPresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
+            inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mPresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.view.inline.InlinePresentationSpec,java.lang.String,java.lang.String[])\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsResponse.java b/core/java/android/view/inputmethod/InlineSuggestionsResponse.java
index 924a5ee..be833df 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsResponse.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsResponse.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.os.Parcelable;
 
 import com.android.internal.util.DataClass;
@@ -33,6 +34,18 @@
 public final class InlineSuggestionsResponse implements Parcelable {
     private final @NonNull List<InlineSuggestion> mInlineSuggestions;
 
+    /**
+     * Creates a new {@link InlineSuggestionsResponse}, for testing purpose.
+     *
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public static InlineSuggestionsResponse newInlineSuggestionsResponse(
+            @NonNull List<InlineSuggestion> inlineSuggestions) {
+        return new InlineSuggestionsResponse(inlineSuggestions);
+    }
+
 
 
     // Code below generated by codegen v1.0.14.
@@ -151,10 +164,10 @@
     };
 
     @DataClass.Generated(
-            time = 1574406147911L,
+            time = 1578972149519L,
             codegenVersion = "1.0.14",
             sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsResponse.java",
-            inputSignatures = "private final @android.annotation.NonNull java.util.List<android.view.inputmethod.InlineSuggestion> mInlineSuggestions\nclass InlineSuggestionsResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
+            inputSignatures = "private final @android.annotation.NonNull java.util.List<android.view.inputmethod.InlineSuggestion> mInlineSuggestions\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionsResponse newInlineSuggestionsResponse(java.util.List<android.view.inputmethod.InlineSuggestion>)\nclass InlineSuggestionsResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index f3007a7..904e736 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -64,6 +64,7 @@
 import android.view.InputEvent;
 import android.view.InputEventSender;
 import android.view.KeyEvent;
+import android.view.ImeFocusController;
 import android.view.View;
 import android.view.ViewRootImpl;
 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
@@ -364,10 +365,10 @@
     boolean mActive = false;
 
     /**
-     * {@code true} if next {@link #onPostWindowFocus(View, View, int, int)} needs to
+     * {@code true} if next {@link ImeFocusController#onPostWindowFocus} needs to
      * restart input.
      */
-    boolean mRestartOnNextWindowFocus = true;
+    private boolean mRestartOnNextWindowFocus = true;
 
     /**
      * As reported by IME through InputConnection.
@@ -380,22 +381,8 @@
      * This is the root view of the overall window that currently has input
      * method focus.
      */
-    @UnsupportedAppUsage
-    View mCurRootView;
-    /**
-     * This is the view that should currently be served by an input method,
-     * regardless of the state of setting that up.
-     */
-    // See comment to mH field in regard to @UnsupportedAppUsage
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
-    View mServedView;
-    /**
-     * This is then next view that will be served by the input method, when
-     * we get around to updating things.
-     */
-    // See comment to mH field in regard to @UnsupportedAppUsage
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
-    View mNextServedView;
+    @GuardedBy("mH")
+    ViewRootImpl mCurRootView;
     /**
      * This is set when we are in the process of connecting, to determine
      * when we have actually finished.
@@ -489,6 +476,8 @@
     final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20);
     final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20);
 
+    final DelegateImpl mDelegate = new DelegateImpl();
+
     // -----------------------------------------------------------
 
     static final int MSG_DUMP = 1;
@@ -564,6 +553,178 @@
         return servedView.hasWindowFocus() || isAutofillUIShowing(servedView);
     }
 
+    private final class DelegateImpl implements
+            ImeFocusController.InputMethodManagerDelegate {
+        /**
+         * Used by {@link ImeFocusController} to start input connection.
+         */
+        @Override
+        public boolean startInput(@StartInputReason int startInputReason, View focusedView,
+                @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
+                int windowFlags) {
+            synchronized (mH) {
+                mCurrentTextBoxAttribute = null;
+                mCompletions = null;
+                mServedConnecting = true;
+                if (getServedViewLocked() != null && !getServedViewLocked().onCheckIsTextEditor()) {
+                    // servedView has changed and it's not editable.
+                    maybeCallServedViewChangedLocked(null);
+                }
+            }
+            return startInputInner(startInputReason,
+                    focusedView != null ? focusedView.getWindowToken() : null, startInputFlags,
+                    softInputMode, windowFlags);
+        }
+
+        /**
+         * Used by {@link ImeFocusController} to finish input connection.
+         */
+        @Override
+        public void finishInput() {
+            synchronized (mH) {
+                finishInputLocked();
+            }
+        }
+
+        /**
+         * Used by {@link ImeFocusController} to hide current input method editor.
+         */
+        @Override
+        public void closeCurrentIme() {
+            closeCurrentInput();
+        }
+
+        /**
+         * For {@link ImeFocusController} to start input asynchronously when focus gain.
+         */
+        @Override
+        public void startInputAsyncOnWindowFocusGain(View focusedView,
+                @SoftInputModeFlags int softInputMode, int windowFlags, boolean forceNewFocus) {
+            final boolean forceNewFocus1 = forceNewFocus;
+            final int startInputFlags = getStartInputFlags(focusedView, 0);
+
+            if (mWindowFocusGainFuture != null) {
+                mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */);
+            }
+            mWindowFocusGainFuture = mStartInputWorker.submit(() -> {
+                synchronized (mH) {
+                    if (mCurRootView == null) {
+                        return;
+                    }
+                    if (mCurRootView.getImeFocusController().checkFocus(forceNewFocus1, false)) {
+                        // We need to restart input on the current focus view.  This
+                        // should be done in conjunction with telling the system service
+                        // about the window gaining focus, to help make the transition
+                        // smooth.
+                        if (startInput(StartInputReason.WINDOW_FOCUS_GAIN,
+                                focusedView, startInputFlags, softInputMode, windowFlags)) {
+                            return;
+                        }
+                    }
+
+                    // For some reason we didn't do a startInput + windowFocusGain, so
+                    // we'll just do a window focus gain and call it a day.
+                    try {
+                        if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
+                        mService.startInputOrWindowGainedFocus(
+                                StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
+                                focusedView.getWindowToken(), startInputFlags, softInputMode,
+                                windowFlags,
+                                null, null, 0 /* missingMethodFlags */,
+                                mCurRootView.mContext.getApplicationInfo().targetSdkVersion);
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                }
+            });
+        }
+
+        /**
+         * Used by {@link ImeFocusController} to finish current composing text.
+         */
+        @Override
+        public void finishComposingText() {
+            if (mServedInputConnectionWrapper != null) {
+                mServedInputConnectionWrapper.finishComposingText();
+            }
+        }
+
+        /**
+         * Used for {@link ImeFocusController} to set the current focused root view.
+         */
+        @Override
+        public void setCurrentRootView(ViewRootImpl rootView) {
+            // If the mCurRootView is losing window focus, release the strong reference to it
+            // so as not to prevent it from being garbage-collected.
+            if (mWindowFocusGainFuture != null) {
+                mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */);
+                mWindowFocusGainFuture = null;
+            }
+            synchronized (mH) {
+                mCurRootView = rootView;
+            }
+        }
+
+        /**
+         * Used for {@link ImeFocusController} to return if the root view from the
+         * controller is this {@link InputMethodManager} currently focused.
+         * TODO: Address event-order problem when get current root view in multi-threads.
+         */
+        @Override
+        public boolean isCurrentRootView(ViewRootImpl rootView) {
+            synchronized (mH) {
+                return mCurRootView == rootView;
+            }
+        }
+
+        /**
+         * For {@link ImeFocusController#checkFocus} if needed to force check new focus.
+         */
+        @Override
+        public boolean isRestartOnNextWindowFocus(boolean reset) {
+            final boolean result = mRestartOnNextWindowFocus;
+            if (reset) {
+                mRestartOnNextWindowFocus = false;
+            }
+            return result;
+        }
+    }
+
+    /** @hide */
+    public DelegateImpl getDelegate() {
+        return mDelegate;
+    }
+
+    private View getServedViewLocked() {
+        return mCurRootView != null ? mCurRootView.getImeFocusController().getServedView() : null;
+    }
+
+    private View getNextServedViewLocked() {
+        return mCurRootView != null ? mCurRootView.getImeFocusController().getNextServedView()
+                : null;
+    }
+
+    private void setServedViewLocked(View view) {
+        if (mCurRootView != null) {
+            mCurRootView.getImeFocusController().setServedView(view);
+        }
+    }
+
+    private void setNextServedViewLocked(View view) {
+        if (mCurRootView != null) {
+            mCurRootView.getImeFocusController().setNextServedView(view);
+        }
+    }
+
+    /**
+     * Returns {@code true} when the given view has been served by Input Method.
+     */
+    private boolean hasServedByInputMethodLocked(View view) {
+        final View servedView = getServedViewLocked();
+        return (servedView == view
+                || (servedView != null && servedView.checkInputConnectionProxy(view)));
+    }
+
     class H extends Handler {
         H(Looper looper) {
             super(looper, null, true);
@@ -629,7 +790,8 @@
                         clearBindingLocked();
                         // If we were actively using the last input method, then
                         // we would like to re-connect to the next input method.
-                        if (mServedView != null && mServedView.isFocused()) {
+                        final View servedView = getServedViewLocked();
+                        if (servedView != null && servedView.isFocused()) {
                             mServedConnecting = true;
                         }
                         startInput = mActive;
@@ -664,11 +826,16 @@
                     }
                     // Check focus again in case that "onWindowFocus" is called before
                     // handling this message.
-                    if (mServedView != null && canStartInput(mServedView)) {
-                        if (checkFocusNoStartInput(mRestartOnNextWindowFocus)) {
+                    final View servedView;
+                    synchronized (mH) {
+                        servedView = getServedViewLocked();
+                    }
+                    if (servedView != null && canStartInput(servedView)) {
+                        if (mCurRootView != null && mCurRootView.getImeFocusController()
+                                .checkFocus(mRestartOnNextWindowFocus, false)) {
                             final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS
                                     : StartInputReason.DEACTIVATED_BY_IMMS;
-                            startInputInner(reason, null, 0, 0, 0);
+                            mDelegate.startInput(reason, null, 0, 0, 0);
                         }
                     }
                     return;
@@ -1212,10 +1379,7 @@
 
         checkFocus();
         synchronized (mH) {
-            return (mServedView == view
-                    || (mServedView != null
-                            && mServedView.checkInputConnectionProxy(view)))
-                    && mCurrentTextBoxAttribute != null;
+            return hasServedByInputMethodLocked(view) && mCurrentTextBoxAttribute != null;
         }
     }
 
@@ -1225,7 +1389,7 @@
     public boolean isActive() {
         checkFocus();
         synchronized (mH) {
-            return mServedView != null && mCurrentTextBoxAttribute != null;
+            return getServedViewLocked() != null && mCurrentTextBoxAttribute != null;
         }
     }
 
@@ -1286,11 +1450,14 @@
      */
     @UnsupportedAppUsage
     void finishInputLocked() {
-        mNextServedView = null;
         mActivityViewToScreenMatrix = null;
-        if (mServedView != null) {
-            if (DEBUG) Log.v(TAG, "FINISH INPUT: mServedView=" + dumpViewInfo(mServedView));
-            mServedView = null;
+        setNextServedViewLocked(null);
+        if (getServedViewLocked() != null) {
+            if (DEBUG) {
+                Log.v(TAG, "FINISH INPUT: mServedView="
+                        + dumpViewInfo(getServedViewLocked()));
+            }
+            setServedViewLocked(null);
             mCompletions = null;
             mServedConnecting = false;
             clearConnectionLocked();
@@ -1307,8 +1474,7 @@
 
         checkFocus();
         synchronized (mH) {
-            if (mServedView != view && (mServedView == null
-                            || !mServedView.checkInputConnectionProxy(view))) {
+            if (!hasServedByInputMethodLocked(view)) {
                 return;
             }
 
@@ -1332,8 +1498,7 @@
 
         checkFocus();
         synchronized (mH) {
-            if (mServedView != view && (mServedView == null
-                    || !mServedView.checkInputConnectionProxy(view))) {
+            if (!hasServedByInputMethodLocked(view)) {
                 return;
             }
 
@@ -1447,8 +1612,7 @@
 
         checkFocus();
         synchronized (mH) {
-            if (mServedView != view && (mServedView == null
-                    || !mServedView.checkInputConnectionProxy(view))) {
+            if (!hasServedByInputMethodLocked(view)) {
                 return false;
             }
 
@@ -1539,7 +1703,8 @@
             ResultReceiver resultReceiver) {
         checkFocus();
         synchronized (mH) {
-            if (mServedView == null || mServedView.getWindowToken() != windowToken) {
+            final View servedView = getServedViewLocked();
+            if (servedView == null || servedView.getWindowToken() != windowToken) {
                 return false;
             }
 
@@ -1566,7 +1731,8 @@
      **/
     public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) {
         synchronized (mH) {
-            if (mServedView == null || mServedView.getWindowToken() != windowToken) {
+            final View servedView = getServedViewLocked();
+            if (servedView == null || servedView.getWindowToken() != windowToken) {
                 return;
             }
             if (mCurMethod != null) {
@@ -1617,8 +1783,7 @@
 
         checkFocus();
         synchronized (mH) {
-            if (mServedView != view && (mServedView == null
-                    || !mServedView.checkInputConnectionProxy(view))) {
+            if (!hasServedByInputMethodLocked(view)) {
                 return;
             }
 
@@ -1645,7 +1810,7 @@
 
         final View view;
         synchronized (mH) {
-            view = mServedView;
+            view = getServedViewLocked();
 
             // Make sure we have a window token for the served view.
             if (DEBUG) {
@@ -1664,10 +1829,7 @@
                 Log.e(TAG, "ABORT input: ServedView must be attached to a Window");
                 return false;
             }
-            startInputFlags |= StartInputFlags.VIEW_HAS_FOCUS;
-            if (view.onCheckIsTextEditor()) {
-                startInputFlags |= StartInputFlags.IS_TEXT_EDITOR;
-            }
+            startInputFlags = getStartInputFlags(view, startInputFlags);
             softInputMode = view.getViewRootImpl().mWindowAttributes.softInputMode;
             windowFlags = view.getViewRootImpl().mWindowAttributes.flags;
         }
@@ -1690,7 +1852,7 @@
             // The view is running on a different thread than our own, so
             // we need to reschedule our work for over there.
             if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
-            vh.post(() -> startInputInner(startInputReason, null, 0, 0, 0));
+            vh.post(() -> mDelegate.startInput(startInputReason, null, 0, 0, 0));
             return false;
         }
 
@@ -1709,11 +1871,12 @@
         synchronized (mH) {
             // Now that we are locked again, validate that our state hasn't
             // changed.
-            if (mServedView != view || !mServedConnecting) {
+            final View servedView = getServedViewLocked();
+            if (servedView != view || !mServedConnecting) {
                 // Something else happened, so abort.
                 if (DEBUG) Log.v(TAG,
                         "Starting input: finished by someone else. view=" + dumpViewInfo(view)
-                        + " mServedView=" + dumpViewInfo(mServedView)
+                        + " servedView=" + dumpViewInfo(servedView)
                         + " mServedConnecting=" + mServedConnecting);
                 return false;
             }
@@ -1804,101 +1967,12 @@
         return true;
     }
 
-    /**
-     * When the focused window is dismissed, this method is called to finish the
-     * input method started before.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public void windowDismissed(IBinder appWindowToken) {
-        checkFocus();
-        synchronized (mH) {
-            if (mServedView != null &&
-                    mServedView.getWindowToken() == appWindowToken) {
-                finishInputLocked();
-            }
-            if (mCurRootView != null &&
-                    mCurRootView.getWindowToken() == appWindowToken) {
-                mCurRootView = null;
-            }
+    private int getStartInputFlags(View focusedView, int startInputFlags) {
+        startInputFlags |= StartInputFlags.VIEW_HAS_FOCUS;
+        if (focusedView.onCheckIsTextEditor()) {
+            startInputFlags |= StartInputFlags.IS_TEXT_EDITOR;
         }
-    }
-
-    /**
-     * Call this when a view receives focus.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public void focusIn(View view) {
-        synchronized (mH) {
-            focusInLocked(view);
-        }
-    }
-
-    void focusInLocked(View view) {
-        if (DEBUG) Log.v(TAG, "focusIn: " + dumpViewInfo(view));
-
-        if (view != null && view.isTemporarilyDetached()) {
-            // This is a request from a view that is temporarily detached from a window.
-            if (DEBUG) Log.v(TAG, "Temporarily detached view, ignoring");
-            return;
-        }
-
-        if (mCurRootView != view.getRootView()) {
-            // This is a request from a window that isn't in the window with
-            // IME focus, so ignore it.
-            if (DEBUG) Log.v(TAG, "Not IME target window, ignoring");
-            return;
-        }
-
-        mNextServedView = view;
-        scheduleCheckFocusLocked(view);
-    }
-
-    /**
-     * Call this when a view loses focus.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public void focusOut(View view) {
-        synchronized (mH) {
-            if (DEBUG) Log.v(TAG, "focusOut: view=" + dumpViewInfo(view)
-                    + " mServedView=" + dumpViewInfo(mServedView));
-            if (mServedView != view) {
-                // The following code would auto-hide the IME if we end up
-                // with no more views with focus.  This can happen, however,
-                // whenever we go into touch mode, so it ends up hiding
-                // at times when we don't really want it to.  For now it
-                // seems better to just turn it all off.
-                // TODO: Check view.isTemporarilyDetached() when re-enable the following code.
-                if (false && canStartInput(view)) {
-                    mNextServedView = null;
-                    scheduleCheckFocusLocked(view);
-                }
-            }
-        }
-    }
-
-    /**
-     * Call this when a view is being detached from a {@link android.view.Window}.
-     * @hide
-     */
-    public void onViewDetachedFromWindow(View view) {
-        synchronized (mH) {
-            if (DEBUG) Log.v(TAG, "onViewDetachedFromWindow: view=" + dumpViewInfo(view)
-                    + " mServedView=" + dumpViewInfo(mServedView));
-            if (mServedView == view) {
-                mNextServedView = null;
-                scheduleCheckFocusLocked(view);
-            }
-        }
-    }
-
-    static void scheduleCheckFocusLocked(View view) {
-        ViewRootImpl viewRootImpl = view.getViewRootImpl();
-        if (viewRootImpl != null) {
-            viewRootImpl.dispatchCheckFocus();
-        }
+        return startInputFlags;
     }
 
     /**
@@ -1906,54 +1980,12 @@
      */
     @UnsupportedAppUsage
     public void checkFocus() {
-        if (checkFocusNoStartInput(false)) {
-            startInputInner(StartInputReason.CHECK_FOCUS, null, 0, 0, 0);
-        }
-    }
-
-    private boolean checkFocusNoStartInput(boolean forceNewFocus) {
-        // This is called a lot, so short-circuit before locking.
-        if (mServedView == mNextServedView && !forceNewFocus) {
-            return false;
-        }
-
-        final ControlledInputConnectionWrapper ic;
         synchronized (mH) {
-            if (mServedView == mNextServedView && !forceNewFocus) {
-                return false;
-            }
-            if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
-                    + " next=" + mNextServedView
-                    + " forceNewFocus=" + forceNewFocus
-                    + " package="
-                    + (mServedView != null ? mServedView.getContext().getPackageName() : "<none>"));
-
-            if (mNextServedView == null) {
-                finishInputLocked();
-                // In this case, we used to have a focused view on the window,
-                // but no longer do.  We should make sure the input method is
-                // no longer shown, since it serves no purpose.
-                closeCurrentInput();
-                return false;
-            }
-
-            ic = mServedInputConnectionWrapper;
-
-            mServedView = mNextServedView;
-            mCurrentTextBoxAttribute = null;
-            mCompletions = null;
-            mServedConnecting = true;
-            // servedView has changed and it's not editable.
-            if (!mServedView.onCheckIsTextEditor()) {
-                maybeCallServedViewChangedLocked(null);
+            if (mCurRootView != null) {
+                mCurRootView.getImeFocusController().checkFocus(false /* forceNewFocus */,
+                        true /* startInput */);
             }
         }
-
-        if (ic != null) {
-            ic.finishComposingText();
-        }
-
-        return true;
     }
 
     @UnsupportedAppUsage
@@ -1966,92 +1998,6 @@
     }
 
     /**
-     * Called by ViewAncestor when its window gets input focus.
-     * @hide
-     */
-    public void onPostWindowFocus(View rootView, View focusedView,
-            @SoftInputModeFlags int softInputMode, int windowFlags) {
-        boolean forceNewFocus = false;
-        synchronized (mH) {
-            if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
-                    + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
-                    + " flags=#" + Integer.toHexString(windowFlags));
-            if (mRestartOnNextWindowFocus) {
-                if (DEBUG) Log.v(TAG, "Restarting due to mRestartOnNextWindowFocus");
-                mRestartOnNextWindowFocus = false;
-                forceNewFocus = true;
-            }
-            focusInLocked(focusedView != null ? focusedView : rootView);
-        }
-
-        int startInputFlags = 0;
-        if (focusedView != null) {
-            startInputFlags |= StartInputFlags.VIEW_HAS_FOCUS;
-            if (focusedView.onCheckIsTextEditor()) {
-                startInputFlags |= StartInputFlags.IS_TEXT_EDITOR;
-            }
-        }
-
-        final boolean forceNewFocus1 = forceNewFocus;
-        final int startInputFlags1 = startInputFlags;
-        if (mWindowFocusGainFuture != null) {
-            mWindowFocusGainFuture.cancel(false/* mayInterruptIfRunning */);
-        }
-        mWindowFocusGainFuture = mStartInputWorker.submit(() -> {
-            if (checkFocusNoStartInput(forceNewFocus1)) {
-                // We need to restart input on the current focus view.  This
-                // should be done in conjunction with telling the system service
-                // about the window gaining focus, to help make the transition
-                // smooth.
-                if (startInputInner(StartInputReason.WINDOW_FOCUS_GAIN, rootView.getWindowToken(),
-                        startInputFlags1, softInputMode, windowFlags)) {
-                    return;
-                }
-            }
-
-            // For some reason we didn't do a startInput + windowFocusGain, so
-            // we'll just do a window focus gain and call it a day.
-            synchronized (mH) {
-                try {
-                    if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
-                    mService.startInputOrWindowGainedFocus(
-                            StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
-                            rootView.getWindowToken(), startInputFlags1, softInputMode, windowFlags,
-                            null, null, 0 /* missingMethodFlags */,
-                            rootView.getContext().getApplicationInfo().targetSdkVersion);
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
-                }
-            }
-        });
-    }
-
-    /** @hide */
-    @UnsupportedAppUsage
-    public void onPreWindowFocus(View rootView, boolean hasWindowFocus) {
-        synchronized (mH) {
-            if (rootView == null) {
-                mCurRootView = null;
-            } if (hasWindowFocus) {
-                mCurRootView = rootView;
-            } else if (rootView == mCurRootView) {
-                // If the mCurRootView is losing window focus, release the strong reference to it
-                // so as not to prevent it from being garbage-collected.
-                mCurRootView = null;
-                if (mWindowFocusGainFuture != null) {
-                    mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */);
-                    mWindowFocusGainFuture = null;
-                }
-            } else {
-                if (DEBUG) {
-                    Log.v(TAG, "Ignoring onPreWindowFocus()."
-                            + " mCurRootView=" + mCurRootView + " rootView=" + rootView);
-                }
-            }
-        }
-    }
-
-    /**
      * Register for IME state callbacks and applying visibility in
      * {@link android.view.ImeInsetsSourceConsumer}.
      * @hide
@@ -2090,10 +2036,11 @@
      */
     public boolean requestImeShow(ResultReceiver resultReceiver) {
         synchronized (mH) {
-            if (mServedView == null) {
+            final View servedView = getServedViewLocked();
+            if (servedView == null) {
                 return false;
             }
-            showSoftInput(mServedView, 0 /* flags */, resultReceiver);
+            showSoftInput(servedView, 0 /* flags */, resultReceiver);
             return true;
         }
     }
@@ -2135,9 +2082,8 @@
 
         checkFocus();
         synchronized (mH) {
-            if ((mServedView != view && (mServedView == null
-                        || !mServedView.checkInputConnectionProxy(view)))
-                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
+            if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
+                    || mCurMethod == null) {
                 return;
             }
 
@@ -2185,12 +2131,17 @@
             return;
         }
 
-        final boolean focusChanged = mServedView != mNextServedView;
+        final View servedView;
+        final View nextServedView;
+        synchronized (mH) {
+            servedView = getServedViewLocked();
+            nextServedView = getNextServedViewLocked();
+        }
+        final boolean focusChanged = servedView != nextServedView;
         checkFocus();
         synchronized (mH) {
-            if ((mServedView != view && (mServedView == null
-                    || !mServedView.checkInputConnectionProxy(view)))
-                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
+            if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
+                    || mCurMethod == null) {
                 return;
             }
             try {
@@ -2258,9 +2209,8 @@
 
         checkFocus();
         synchronized (mH) {
-            if ((mServedView != view && (mServedView == null
-                        || !mServedView.checkInputConnectionProxy(view)))
-                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
+            if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
+                    || mCurMethod == null) {
                 return;
             }
 
@@ -2296,9 +2246,8 @@
 
         checkFocus();
         synchronized (mH) {
-            if ((mServedView != view &&
-                    (mServedView == null || !mServedView.checkInputConnectionProxy(view)))
-                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
+            if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
+                    || mCurMethod == null) {
                 return;
             }
             // If immediate bit is set, we will call updateCursorAnchorInfo() even when the data has
@@ -2354,9 +2303,8 @@
 
         checkFocus();
         synchronized (mH) {
-            if ((mServedView != view && (mServedView == null
-                        || !mServedView.checkInputConnectionProxy(view)))
-                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
+            if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
+                    || mCurMethod == null) {
                 return;
             }
             try {
@@ -2577,8 +2525,9 @@
         synchronized (mH) {
             ViewRootImpl viewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
             if (viewRootImpl == null) {
-                if (mServedView != null) {
-                    viewRootImpl = mServedView.getViewRootImpl();
+                final View servedView = getServedViewLocked();
+                if (servedView != null) {
+                    viewRootImpl = servedView.getViewRootImpl();
                 }
             }
             if (viewRootImpl != null) {
@@ -2759,6 +2708,7 @@
 
     /**
      * Show the settings for enabling subtypes of the specified input method.
+     *
      * @param imiId An input method, whose subtypes settings will be shown. If imiId is null,
      * subtypes of all input methods will be shown.
      */
@@ -3057,8 +3007,8 @@
         p.println("  mFullscreenMode=" + mFullscreenMode);
         p.println("  mCurMethod=" + mCurMethod);
         p.println("  mCurRootView=" + mCurRootView);
-        p.println("  mServedView=" + mServedView);
-        p.println("  mNextServedView=" + mNextServedView);
+        p.println("  mServedView=" + getServedViewLocked());
+        p.println("  mNextServedView=" + getNextServedViewLocked());
         p.println("  mServedConnecting=" + mServedConnecting);
         if (mCurrentTextBoxAttribute != null) {
             p.println("  mCurrentTextBoxAttribute:");
@@ -3134,6 +3084,8 @@
         sb.append(",window=" + view.getWindowToken());
         sb.append(",displayId=" + view.getContext().getDisplayId());
         sb.append(",temporaryDetach=" + view.isTemporarilyDetached());
+        sb.append(",hasImeFocus=" + view.hasImeFocus());
+
         return sb.toString();
     }
 }
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 1aa2aec..bda12b0 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -113,7 +113,7 @@
      * Returns the text that was used to generate these links.
      */
     @NonNull
-    public String getText() {
+    public CharSequence getText() {
         return mFullText;
     }
 
@@ -370,8 +370,8 @@
         }
 
         /**
-         * @return ordered list of locale preferences that can be used to disambiguate
-         *      the provided text
+         * Returns an ordered list of locale preferences that can be used to disambiguate the
+         * provided text.
          */
         @Nullable
         public LocaleList getDefaultLocales() {
@@ -379,7 +379,8 @@
         }
 
         /**
-         * @return The config representing the set of entities to look for
+         * Returns the config representing the set of entities to look for
+         *
          * @see Builder#setEntityConfig(EntityConfig)
          */
         @Nullable
@@ -398,8 +399,8 @@
         }
 
         /**
-         * @return reference time based on which relative dates (e.g. "tomorrow") should be
-         *      interpreted.
+         * Returns reference time based on which relative dates (e.g. "tomorrow") should be
+         * interpreted.
          */
         @Nullable
         public ZonedDateTime getReferenceTime() {
@@ -473,6 +474,9 @@
             }
 
             /**
+             * Sets ordered list of locale preferences that may be used to disambiguate the
+             * provided text.
+             *
              * @param defaultLocales ordered list of locale preferences that may be used to
              *                       disambiguate the provided text. If no locale preferences exist,
              *                       set this to null or an empty locale list.
@@ -524,9 +528,11 @@
             }
 
             /**
-             * @param referenceTime reference time based on which relative dates (e.g. "tomorrow"
-             *      should be interpreted. This should usually be the time when the text was
-             *      originally composed.
+             * Sets the reference time based on which relative dates (e.g.
+             * "tomorrow") should be interpreted.
+             *
+             * @param referenceTime reference time based on which relative dates. This should
+             *                      usually be the time when the text was originally composed.
              *
              * @return this builder
              */
@@ -716,6 +722,8 @@
         }
 
         /**
+         * Adds a TextLink.
+         *
          * @see #addLink(int, int, Map)
          * @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled.
          */
diff --git a/core/java/android/view/textclassifier/TextLinksParams.java b/core/java/android/view/textclassifier/TextLinksParams.java
index b7d63bf..f12b0d7 100644
--- a/core/java/android/view/textclassifier/TextLinksParams.java
+++ b/core/java/android/view/textclassifier/TextLinksParams.java
@@ -113,7 +113,7 @@
             return TextLinks.STATUS_UNSUPPORTED_CHARACTER;
         }
 
-        if (!textString.startsWith(textLinks.getText())) {
+        if (!textString.startsWith(textLinks.getText().toString())) {
             return TextLinks.STATUS_DIFFERENT_TEXT;
         }
         if (textLinks.getLinks().isEmpty()) {
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 3fdedc8..93659a4 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -371,7 +371,9 @@
         // targets during boot. Needs to read settings directly here.
         String shortcutTargets = Settings.Secure.getStringForUser(mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, mUserId);
-        if (TextUtils.isEmpty(shortcutTargets)) {
+        // A11y warning dialog updates settings to empty string, when user disables a11y shortcut.
+        // Only fallback to default a11y service, when setting is never updated.
+        if (shortcutTargets == null) {
             shortcutTargets = mContext.getString(R.string.config_defaultAccessibilityService);
         }
         return !TextUtils.isEmpty(shortcutTargets);
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index a9f0175..457c033 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -75,11 +75,14 @@
     private static final char SERVICES_SEPARATOR = ':';
     private static final TextUtils.SimpleStringSplitter sStringColonSplitter =
             new TextUtils.SimpleStringSplitter(SERVICES_SEPARATOR);
+    @UserShortcutType
     private static final int ACCESSIBILITY_BUTTON_USER_TYPE = convertToUserType(
             ACCESSIBILITY_BUTTON);
+    @UserShortcutType
     private static final int ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE = convertToUserType(
             ACCESSIBILITY_SHORTCUT_KEY);
 
+    @ShortcutType
     private int mShortcutType;
     private final List<AccessibilityButtonTarget> mTargets = new ArrayList<>();
     private AlertDialog mAlertDialog;
@@ -211,9 +214,14 @@
 
         mShortcutType = getIntent().getIntExtra(AccessibilityManager.EXTRA_SHORTCUT_TYPE,
                 ACCESSIBILITY_BUTTON);
+        if ((mShortcutType != ACCESSIBILITY_BUTTON)
+                && (mShortcutType != ACCESSIBILITY_SHORTCUT_KEY)) {
+            throw new IllegalStateException("Unexpected shortcut type: " + mShortcutType);
+        }
+
         mTargets.addAll(getServiceTargets(this, mShortcutType));
 
-        mTargetAdapter = new TargetAdapter(mTargets);
+        mTargetAdapter = new TargetAdapter(mTargets, mShortcutType);
         mAlertDialog = new AlertDialog.Builder(this)
                 .setAdapter(mTargetAdapter, /* listener= */ null)
                 .setPositiveButton(
@@ -357,16 +365,21 @@
     private static class TargetAdapter extends BaseAdapter {
         @ShortcutMenuMode
         private int mShortcutMenuMode = ShortcutMenuMode.LAUNCH;
+        @ShortcutType
+        private int mShortcutButtonType;
         private List<AccessibilityButtonTarget> mButtonTargets;
 
-        TargetAdapter(List<AccessibilityButtonTarget> targets) {
+        TargetAdapter(List<AccessibilityButtonTarget> targets,
+                @ShortcutType int shortcutButtonType) {
             this.mButtonTargets = targets;
+            this.mShortcutButtonType = shortcutButtonType;
         }
 
-        void setShortcutMenuMode(int shortcutMenuMode) {
+        void setShortcutMenuMode(@ShortcutMenuMode int shortcutMenuMode) {
             mShortcutMenuMode = shortcutMenuMode;
         }
 
+        @ShortcutMenuMode
         int getShortcutMenuMode() {
             return mShortcutMenuMode;
         }
@@ -441,14 +454,16 @@
 
         private void updateLegacyActionItemVisibility(@NonNull Context context,
                 @NonNull ViewHolder holder) {
-            final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT);
+            final boolean isLaunchMenuMode = (mShortcutMenuMode == ShortcutMenuMode.LAUNCH);
+            final boolean isHardwareButtonTriggered =
+                    (mShortcutButtonType == ACCESSIBILITY_SHORTCUT_KEY);
 
-            holder.mLabelView.setEnabled(!isEditMenuMode);
-            holder.mViewItem.setEnabled(!isEditMenuMode);
+            holder.mLabelView.setEnabled(isLaunchMenuMode || isHardwareButtonTriggered);
+            holder.mViewItem.setEnabled(isLaunchMenuMode || isHardwareButtonTriggered);
             holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
             holder.mViewItem.setVisibility(View.VISIBLE);
             holder.mSwitchItem.setVisibility(View.GONE);
-            holder.mItemContainer.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
+            holder.mItemContainer.setVisibility(isLaunchMenuMode ? View.GONE : View.VISIBLE);
         }
 
         private void updateInvisibleActionItemVisibility(@NonNull Context context,
@@ -574,8 +589,6 @@
             ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId());
         } else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
             switchServiceState(target);
-        } else {
-            throw new IllegalArgumentException("Unsupported shortcut type:" + mShortcutType);
         }
     }
 
@@ -611,13 +624,15 @@
                 ComponentName.unflattenFromString(target.getId());
 
         switch (target.getFragmentType()) {
+            case AccessibilityServiceFragmentType.LEGACY:
+                onLegacyTargetDeleted(targetComponentName);
+                break;
             case AccessibilityServiceFragmentType.INVISIBLE:
                 onInvisibleTargetDeleted(targetComponentName);
                 break;
             case AccessibilityServiceFragmentType.INTUITIVE:
                 onIntuitiveTargetDeleted(targetComponentName);
                 break;
-            case AccessibilityServiceFragmentType.LEGACY:
             case AccessibilityServiceFragmentType.BOUNCE:
                 // Do nothing
                 break;
@@ -633,6 +648,12 @@
         }
     }
 
+    private void onLegacyTargetDeleted(ComponentName componentName) {
+        if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
+            optOutValueFromSettings(this, ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE, componentName);
+        }
+    }
+
     private void onInvisibleTargetDeleted(ComponentName componentName) {
         if (mShortcutType == ACCESSIBILITY_BUTTON) {
             optOutValueFromSettings(this, ACCESSIBILITY_BUTTON_USER_TYPE, componentName);
@@ -648,8 +669,6 @@
                     ACCESSIBILITY_BUTTON_USER_TYPE, componentName)) {
                 disableService(componentName);
             }
-        } else {
-            throw new IllegalArgumentException("Unsupported shortcut type:" + mShortcutType);
         }
     }
 
@@ -658,8 +677,6 @@
             optOutValueFromSettings(this, ACCESSIBILITY_BUTTON_USER_TYPE, componentName);
         } else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
             optOutValueFromSettings(this, ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE, componentName);
-        } else {
-            throw new IllegalArgumentException("Unsupported shortcut type:" + mShortcutType);
         }
     }
 
@@ -772,7 +789,7 @@
      * @param componentName The component name that need to be opted out from Settings.
      */
     private void optOutValueFromSettings(
-            Context context, int shortcutType, ComponentName componentName) {
+            Context context, @UserShortcutType int shortcutType, ComponentName componentName) {
         final StringJoiner joiner = new StringJoiner(String.valueOf(SERVICES_SEPARATOR));
         final String targetsKey = convertToKey(shortcutType);
         final String targetsValue = Settings.Secure.getString(context.getContentResolver(),
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 221cd6d..ef9b3d10 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -561,7 +561,7 @@
                     flags |= Document.FLAG_SUPPORTS_MOVE;
 
                     if (shouldBlockFromTree(docId)) {
-                        flags |= Document.FLAG_DIR_BLOCKS_TREE;
+                        flags |= Document.FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE;
                     }
 
                 } else {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index b47080f..50a60a9 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -423,6 +423,14 @@
     transaction->setFlags(ctrl, flags, mask);
 }
 
+static void nativeSetFrameRateSelectionPriority(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject, jint priority) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+    SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+    transaction->setFrameRateSelectionPriority(ctrl, priority);
+}
+
 static void nativeSetTransparentRegionHint(JNIEnv* env, jclass clazz, jlong transactionObj,
         jlong nativeObject, jobject regionObj) {
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
@@ -818,13 +826,6 @@
     return static_cast<jint>(SurfaceComposerClient::getActiveConfig(token));
 }
 
-static jboolean nativeSetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj, jint id) {
-    sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
-    if (token == NULL) return JNI_FALSE;
-    status_t err = SurfaceComposerClient::setActiveConfig(token, static_cast<int>(id));
-    return err == NO_ERROR ? JNI_TRUE : JNI_FALSE;
-}
-
 static jintArray nativeGetDisplayColorModes(JNIEnv* env, jclass, jobject tokenObj) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
     if (token == NULL) return NULL;
@@ -1362,6 +1363,8 @@
             (void*)nativeSetColorSpaceAgnostic },
     {"nativeSetFlags", "(JJII)V",
             (void*)nativeSetFlags },
+    {"nativeSetFrameRateSelectionPriority", "(JJI)V",
+            (void*)nativeSetFrameRateSelectionPriority },
     {"nativeSetWindowCrop", "(JJIIII)V",
             (void*)nativeSetWindowCrop },
     {"nativeSetCornerRadius", "(JJF)V",
@@ -1390,8 +1393,6 @@
             (void*)nativeGetDisplayConfigs },
     {"nativeGetActiveConfig", "(Landroid/os/IBinder;)I",
             (void*)nativeGetActiveConfig },
-    {"nativeSetActiveConfig", "(Landroid/os/IBinder;I)Z",
-            (void*)nativeSetActiveConfig },
     {"nativeSetDesiredDisplayConfigSpecs",
             "(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayConfigSpecs;)Z",
             (void*)nativeSetDesiredDisplayConfigSpecs },
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 39ea45a..05b573a 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -174,6 +174,8 @@
 static constexpr const uint64_t UPPER_HALF_WORD_MASK = 0xFFFF'FFFF'0000'0000;
 static constexpr const uint64_t LOWER_HALF_WORD_MASK = 0x0000'0000'FFFF'FFFF;
 
+static constexpr const char* kCurProfileDirPath = "/data/misc/profiles/cur";
+
 /**
  * The maximum value that the gUSAPPoolSizeMax variable may take.  This value
  * is a mirror of ZygoteServer.USAP_POOL_SIZE_MAX_LIMIT
@@ -1382,6 +1384,49 @@
   freecon(dataDataContext);
 }
 
+/**
+ * Like isolateAppData(), isolate jit profile directories, so apps don't see what
+ * other apps are installed by reading content inside /data/misc/profiles/cur.
+ *
+ * The implementation is similar to isolateAppData(), it creates a tmpfs
+ * on /data/misc/profiles/cur, and bind mounts related package profiles to it.
+ */
+static void isolateJitProfile(JNIEnv* env, jobjectArray pkg_data_info_list,
+    uid_t uid, const char* process_name, jstring managed_nice_name,
+    fail_fn_t fail_fn) {
+
+  auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
+  const userid_t user_id = multiuser_get_user_id(uid);
+
+  int size = (pkg_data_info_list != nullptr) ? env->GetArrayLength(pkg_data_info_list) : 0;
+  // Size should be a multiple of 3, as it contains list of <package_name, volume_uuid, inode>
+  if ((size % 3) != 0) {
+    fail_fn(CREATE_ERROR("Wrong pkg_inode_list size %d", size));
+  }
+
+  // Mount (namespace) tmpfs on profile directory, so apps no longer access
+  // the original profile directory anymore.
+  MountAppDataTmpFs(kCurProfileDirPath, fail_fn);
+
+  // Create profile directory for this user.
+  std::string actualCurUserProfile = StringPrintf("%s/%d", kCurProfileDirPath, user_id);
+  PrepareDir(actualCurUserProfile.c_str(), DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT,
+      fail_fn);
+
+  for (int i = 0; i < size; i += 3) {
+    jstring package_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i));
+    std::string packageName = extract_fn(package_str).value();
+
+    std::string actualCurPackageProfile = StringPrintf("%s/%s", actualCurUserProfile.c_str(),
+        packageName.c_str());
+    std::string mirrorCurPackageProfile = StringPrintf("/data_mirror/cur_profiles/%d/%s",
+        user_id, packageName.c_str());
+
+    PrepareDir(actualCurPackageProfile, DEFAULT_DATA_DIR_PERMISSION, uid, uid, fail_fn);
+    BindMount(mirrorCurPackageProfile, actualCurPackageProfile, fail_fn);
+  }
+}
+
 // Utility routine to specialize a zygote child process.
 static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
                              jint runtime_flags, jobjectArray rlimits,
@@ -1432,8 +1477,8 @@
   // so they can't even traverse CE / DE directories.
   if (pkg_data_info_list != nullptr
       && GetBoolProperty(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true)) {
-    isolateAppData(env, pkg_data_info_list, uid, process_name, managed_nice_name,
-        fail_fn);
+    isolateAppData(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
+    isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
   }
 
   // If this zygote isn't root, it won't be able to create a process group,
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 082a289..1fcc8ac 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -39,6 +39,7 @@
         "/apex/com.android.media/javalib/updatable-media.jar",
         "/apex/com.android.mediaprovider/javalib/framework-mediaprovider.jar",
         "/apex/com.android.os.statsd/javalib/framework-statsd.jar",
+        "/apex/com.android.permission/javalib/framework-permission.jar",
         "/apex/com.android.sdkext/javalib/framework-sdkextensions.jar",
         "/apex/com.android.wifi/javalib/framework-wifi.jar",
         "/apex/com.android.tethering/javalib/framework-tethering.jar",
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 8f9c041..da8c944 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -165,6 +165,11 @@
         (section).args = "security -L"
     ];
 
+    optional android.util.PersistedLogProto persisted_logs = 1116 [
+        (section).type = SECTION_COMMAND,
+        (section).args = "/system/bin/sh /system/bin/incident-helper-cmd -l run persisted_logs --limit 10MB"
+    ];
+
     // Stack dumps
     optional android.os.BackTraceProto native_traces = 1200 [
         (section).type = SECTION_TOMBSTONE,
diff --git a/core/proto/android/stats/sysui/notification_enums.proto b/core/proto/android/stats/sysui/notification_enums.proto
new file mode 100644
index 0000000..0983702
--- /dev/null
+++ b/core/proto/android/stats/sysui/notification_enums.proto
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package android.stats.sysui;
+
+// Enum used in NotificationReported and NotificationChannelModified atoms
+enum NotificationImportance {  // Constants from NotificationManager.java
+  IMPORTANCE_UNSPECIFIED = -1000;  // Should not occur for real notifications.
+  IMPORTANCE_NONE = 0;  // No importance: does not show in the shade.
+  IMPORTANCE_MIN = 1;  // Minimum to show in the shade.
+  IMPORTANCE_LOW = 2;  // Shows in shade, maybe status bar, no buzz/beep.
+  IMPORTANCE_DEFAULT = 3;  // Shows everywhere, makes noise, no heads-up.
+  IMPORTANCE_HIGH = 4;  // Shows everywhere, makes noise, heads-up, may full-screen.
+}
diff --git a/core/proto/android/util/log.proto b/core/proto/android/util/log.proto
index 09870ae..a214a1a 100644
--- a/core/proto/android/util/log.proto
+++ b/core/proto/android/util/log.proto
@@ -94,3 +94,16 @@
     repeated BinaryLogEntry binary_logs = 2;
 }
 
+message PersistedLogProto {
+    option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+    repeated TextLogEntry main_logs = 1;
+    repeated TextLogEntry radio_logs = 2;
+    repeated TextLogEntry events_logs = 3;
+    repeated TextLogEntry system_logs = 4;
+    repeated TextLogEntry crash_logs = 5;
+    repeated TextLogEntry stats_logs = 6;
+    repeated TextLogEntry security_logs = 7;
+    repeated TextLogEntry kernel_logs = 8;
+}
+
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2665e8a..f8c5166 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -362,7 +362,6 @@
     <protected-broadcast android:name="android.net.wifi.WIFI_STATE_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.WIFI_CREDENTIAL_CHANGED" />
-    <protected-broadcast android:name="android.net.wifi.WIFI_SCAN_AVAILABLE" />
     <protected-broadcast android:name="android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.SCAN_RESULTS" />
@@ -377,6 +376,7 @@
     <protected-broadcast android:name="android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION" />
     <protected-broadcast android:name="android.net.wifi.action.PASSPOINT_LAUNCH_OSU_VIEW" />
     <protected-broadcast android:name="android.net.wifi.action.WIFI_NETWORK_SUGGESTION_POST_CONNECTION" />
+    <protected-broadcast android:name="android.net.wifi.action.WIFI_SCAN_AVAILABLE" />
     <protected-broadcast android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" />
     <protected-broadcast android:name="android.net.wifi.supplicant.STATE_CHANGE" />
     <protected-broadcast android:name="android.net.wifi.p2p.STATE_CHANGED" />
@@ -3980,6 +3980,13 @@
     <permission android:name="android.permission.BACKUP"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows an application to make modifications to device settings such that these
+         modifications will be overridden by settings restore..
+    <p>Not for use by third-party applications.
+         @hide -->
+    <permission android:name="android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE"
+                android:protectionLevel="signature|setup" />
+
     <!-- @SystemApi Allows application to manage
          {@link android.security.keystore.recovery.RecoveryController}.
          <p>Not for use by third-party applications.
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 6435cdd..94f3b8a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2005,6 +2005,15 @@
         <attr name="maxSdkVersion" />
     </declare-styleable>
 
+    <!-- The <code>extension-sdk</code> tag is a child of the <uses-sdk> tag,
+         and specifies required extension sdk features. -->
+    <declare-styleable name="AndroidManifestExtensionSdk">
+        <!-- The extension SDK version that this tag refers to. -->
+        <attr name="sdkVersion" format="integer" />
+        <!-- The minimum version of the extension SDK this application requires.-->
+        <attr name="minExtensionVersion" format="integer" />
+    </declare-styleable>
+
     <!-- The <code>library</code> tag declares that this apk is providing itself
          as a shared library for other applications to use.  It can only be used
          with apks that are built in to the system image.  Other apks can link to
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6f554f02..a78195b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4294,4 +4294,7 @@
 
     <!-- Class name of the custom country detector to be used. -->
     <string name="config_customCountryDetector" translatable="false">com.android.server.location.ComprehensiveCountryDetector</string>
+
+    <!-- Package name of the required service extension package. -->
+    <string name="config_servicesExtensionPackage" translatable="false">android.ext.services</string>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 9860830..36dbcbd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3008,6 +3008,10 @@
       <public name="supportsInlineSuggestions" />
       <public name="crossProfile" />
       <public name="canTakeScreenshot"/>
+      <!-- @hide @SystemApi -->
+      <public name="sdkVersion" />
+      <!-- @hide @SystemApi -->
+      <public name="minExtensionVersion" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 43c966c..669b41e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3818,4 +3818,5 @@
   <java-symbol type="string" name="capability_desc_canTakeScreenshot" />
   <java-symbol type="string" name="capability_title_canTakeScreenshot" />
 
+  <java-symbol type="string" name="config_servicesExtensionPackage" />
 </resources>
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 2df6d1c..3836d6f 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -84,6 +84,11 @@
         ":FrameworksCoreTests_install_split_feature_a",
         ":FrameworksCoreTests_install_use_perm_good",
         ":FrameworksCoreTests_install_uses_feature",
+        ":FrameworksCoreTests_install_uses_sdk_0",
+        ":FrameworksCoreTests_install_uses_sdk_q0",
+        ":FrameworksCoreTests_install_uses_sdk_r",
+        ":FrameworksCoreTests_install_uses_sdk_r0",
+        ":FrameworksCoreTests_install_uses_sdk_r5",
         ":FrameworksCoreTests_install_verifier_bad",
         ":FrameworksCoreTests_install_verifier_good",
         ":FrameworksCoreTests_keyset_permdef_sa_unone",
diff --git a/core/tests/coretests/apks/install_uses_sdk/Android.bp b/core/tests/coretests/apks/install_uses_sdk/Android.bp
new file mode 100644
index 0000000..92b09ed
--- /dev/null
+++ b/core/tests/coretests/apks/install_uses_sdk/Android.bp
@@ -0,0 +1,39 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_uses_sdk_r0",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    manifest: "AndroidManifest-r0.xml",
+
+    srcs: ["**/*.java"],
+}
+
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_uses_sdk_r5",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    manifest: "AndroidManifest-r5.xml",
+
+    srcs: ["**/*.java"],
+}
+
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_uses_sdk_q0",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    manifest: "AndroidManifest-q0.xml",
+
+    srcs: ["**/*.java"],
+}
+
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_uses_sdk_r",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    manifest: "AndroidManifest-r.xml",
+
+    srcs: ["**/*.java"],
+}
+
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_uses_sdk_0",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    manifest: "AndroidManifest-0.xml",
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-0.xml b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-0.xml
new file mode 100644
index 0000000..634228b
--- /dev/null
+++ b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-0.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworks.coretests.install_uses_sdk">
+
+    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
+        <!-- This is invalid, because there is no sdk version specified -->
+        <extension-sdk android:minExtensionVersion="5" />
+    </uses-sdk>
+
+    <application>
+    </application>
+</manifest>
diff --git a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-q0.xml b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-q0.xml
new file mode 100644
index 0000000..8994966
--- /dev/null
+++ b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-q0.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworks.coretests.install_uses_sdk">
+
+    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
+        <!-- This fails because 29 doesn't have an extension sdk -->
+        <extension-sdk android:sdkVersion="29" android:minExtensionVersion="0" />
+    </uses-sdk>
+
+    <application>
+    </application>
+</manifest>
diff --git a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r.xml b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r.xml
new file mode 100644
index 0000000..0d0d8b9
--- /dev/null
+++ b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworks.coretests.install_uses_sdk">
+
+    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
+        <!-- This is invalid, because there is no minimum extension version specified -->
+        <extension-sdk android:sdkVersion="10000" />
+    </uses-sdk>
+
+    <application>
+    </application>
+</manifest>
diff --git a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r0.xml b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r0.xml
new file mode 100644
index 0000000..a987afa
--- /dev/null
+++ b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r0.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworks.coretests.install_uses_sdk">
+
+    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
+        <extension-sdk android:sdkVersion="10000" android:minExtensionVersion="0" />
+    </uses-sdk>
+
+    <application>
+    </application>
+</manifest>
diff --git a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r5.xml b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r5.xml
new file mode 100644
index 0000000..9860096
--- /dev/null
+++ b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r5.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworks.coretests.install_uses_sdk">
+
+    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
+        <!-- This will fail to install, because minExtensionVersion is not met -->
+        <extension-sdk android:sdkVersion="10000" android:minExtensionVersion="5" />
+    </uses-sdk>
+
+    <application>
+    </application>
+</manifest>
diff --git a/core/tests/coretests/apks/install_uses_sdk/res/values/strings.xml b/core/tests/coretests/apks/install_uses_sdk/res/values/strings.xml
new file mode 100644
index 0000000..3b8b3b1
--- /dev/null
+++ b/core/tests/coretests/apks/install_uses_sdk/res/values/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Just need this dummy file to have something to build. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string name="dummy">dummy</string>
+</resources>
diff --git a/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java b/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java
new file mode 100644
index 0000000..21259cc
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.app.appsearch;
+
+import static  com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import androidx.test.filters.SmallTest;
+
+import com.google.android.icing.proto.DocumentProto;
+import com.google.android.icing.proto.SearchResultProto;
+
+import org.junit.Test;
+
+@SmallTest
+public class SearchResultsTest {
+
+    @Test
+    public void testSearchResultsEqual() {
+        final String uri = "testUri";
+        final String schemaType = "testSchema";
+        SearchResultProto.ResultProto result1 = SearchResultProto.ResultProto.newBuilder()
+                .setDocument(DocumentProto.newBuilder()
+                        .setUri(uri)
+                        .setSchema(schemaType)
+                        .build())
+                .build();
+        SearchResultProto searchResults1 = SearchResultProto.newBuilder()
+                .addResults(result1)
+                .build();
+        SearchResults res1 = new SearchResults(searchResults1);
+        SearchResultProto.ResultProto result2 = SearchResultProto.ResultProto.newBuilder()
+                .setDocument(DocumentProto.newBuilder()
+                        .setUri(uri)
+                        .setSchema(schemaType)
+                        .build())
+                .build();
+        SearchResultProto searchResults2 = SearchResultProto.newBuilder()
+                .addResults(result2)
+                .build();
+        SearchResults res2 = new SearchResults(searchResults2);
+        assertThat(res1.toString()).isEqualTo(res2.toString());
+    }
+
+    @Test
+    public void buildSearchSpecWithoutTermMatchType() {
+        assertThrows(RuntimeException.class, () -> SearchSpec.newBuilder()
+                .setSchemaTypes("testSchemaType")
+                .build());
+    }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index 1410f4f..09ea1b1 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -124,7 +124,7 @@
         assertArrayEquals(new int[] {}, path(ON_START));
         assertArrayEquals(new int[] {ON_RESUME}, path(ON_RESUME));
         assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE}, path(ON_PAUSE));
-        assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE, ON_STOP}, path(ON_STOP));
+        assertArrayEquals(new int[] {ON_STOP}, path(ON_STOP));
         assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY}, path(ON_DESTROY));
     }
 
@@ -362,7 +362,9 @@
     public void testClosestStateResolutionFromOnStart() {
         mClientRecord.setState(ON_START);
         assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
-                new int[] {ON_CREATE, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY})));
+                new int[] {ON_CREATE, ON_RESUME, ON_PAUSE, ON_DESTROY})));
+        assertEquals(ON_STOP, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+                new int[] {ON_STOP})));
         assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
                 new int[] {ON_CREATE})));
     }
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
index 4402190..dfd762b 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.apex.ApexInfo;
 import android.content.Context;
@@ -486,4 +487,34 @@
         assertTrue((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0);
         assertTrue((pi.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0);
     }
+
+    @Test
+    public void testUsesSdk() throws Exception {
+        parsePackage("install_uses_sdk.apk_r0", R.raw.install_uses_sdk_r0, x -> x);
+        try {
+            parsePackage("install_uses_sdk.apk_r5", R.raw.install_uses_sdk_r5, x -> x);
+            fail("Expected parsing exception due to incompatible extension SDK version");
+        } catch (PackageParser.PackageParserException expected) {
+            assertEquals(PackageManager.INSTALL_FAILED_OLDER_SDK, expected.error);
+        }
+        try {
+            parsePackage("install_uses_sdk.apk_q0", R.raw.install_uses_sdk_q0, x -> x);
+            fail("Expected parsing exception due to non-existent extension SDK");
+        } catch (PackageParser.PackageParserException expected) {
+            assertEquals(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, expected.error);
+        }
+        try {
+            parsePackage("install_uses_sdk.apk_r", R.raw.install_uses_sdk_r, x -> x);
+            fail("Expected parsing exception due to unspecified extension SDK version");
+        } catch (PackageParser.PackageParserException expected) {
+            assertEquals(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, expected.error);
+        }
+        try {
+            parsePackage("install_uses_sdk.apk_0", R.raw.install_uses_sdk_0, x -> x);
+            fail("Expected parsing exception due to unspecified extension SDK");
+        } catch (PackageParser.PackageParserException expected) {
+            assertEquals(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, expected.error);
+        }
+
+    }
 }
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 1db96b1..628f7ec 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -16,9 +16,13 @@
 
 package android.view;
 
+import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
+import static android.view.InsetsController.ANIMATION_TYPE_NONE;
+import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
 import static android.view.InsetsState.ITYPE_IME;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
 import static android.view.WindowInsets.Type.statusBars;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 
@@ -40,12 +44,15 @@
 import android.view.WindowInsets.Type;
 import android.view.WindowManager.BadTokenException;
 import android.view.WindowManager.LayoutParams;
+import android.view.test.InsetsModeSession;
 import android.widget.TextView;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.AfterClass;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -69,6 +76,17 @@
     private SurfaceSession mSession = new SurfaceSession();
     private SurfaceControl mLeash;
     private ViewRootImpl mViewRoot;
+    private static InsetsModeSession sInsetsModeSession;
+
+    @BeforeClass
+    public static void setupOnce() {
+        sInsetsModeSession = new InsetsModeSession(NEW_INSETS_MODE_FULL);
+    }
+
+    @AfterClass
+    public static void tearDownOnce() {
+        sInsetsModeSession.close();
+    }
 
     @Before
     public void setup() {
@@ -86,6 +104,11 @@
             }
             mController = new InsetsController(mViewRoot);
             final Rect rect = new Rect(5, 5, 5, 5);
+            mController.getState().getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 10));
+            mController.getState().getSource(ITYPE_NAVIGATION_BAR).setFrame(
+                    new Rect(0, 90, 100, 100));
+            mController.getState().getSource(ITYPE_IME).setFrame(new Rect(0, 50, 100, 100));
+            mController.getState().setDisplayFrame(new Rect(0, 0, 100, 100));
             mController.calculateInsets(
                     false,
                     false,
@@ -93,7 +116,6 @@
                             Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
                     rect, rect, SOFT_INPUT_ADJUST_RESIZE);
             mController.onFrameChanged(new Rect(0, 0, 100, 100));
-            mController.getState().setDisplayFrame(new Rect(0, 0, 100, 100));
         });
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
@@ -205,18 +227,24 @@
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             int types = Type.navigationBars() | Type.systemBars();
-            // test show select types.
-            mController.show(types);
+            // test hide select types.
+            mController.hide(types);
+            assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
+            assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
             mController.cancelExistingAnimation();
-            assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
-            assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+            assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
+            assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_STATUS_BAR));
+            assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+            assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
             assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
 
             // test hide all
-            mController.hide(types);
+            mController.show(types);
+            assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
+            assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_STATUS_BAR));
             mController.cancelExistingAnimation();
-            assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
-            assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+            assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+            assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
             assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
         });
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -271,30 +299,38 @@
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             // start two animations and see if previous is cancelled and final state is reached.
-            mController.show(Type.navigationBars());
-            mController.show(Type.systemBars());
-            mController.cancelExistingAnimation();
-            assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
-            assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
-            assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
-
             mController.hide(Type.navigationBars());
             mController.hide(Type.systemBars());
+            assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
+            assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
             mController.cancelExistingAnimation();
             assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
             assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
             assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
 
+            mController.show(Type.navigationBars());
+            mController.show(Type.systemBars());
+            assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
+            assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_STATUS_BAR));
+            mController.cancelExistingAnimation();
+            assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+            assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+            assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+
             int types = Type.navigationBars() | Type.systemBars();
             // show two at a time and hide one by one.
             mController.show(types);
             mController.hide(Type.navigationBars());
+            assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
+            assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_STATUS_BAR));
             mController.cancelExistingAnimation();
             assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
             assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
             assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
 
             mController.hide(Type.systemBars());
+            assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
+            assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
             mController.cancelExistingAnimation();
             assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
             assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
index e23c51e..8e24907 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
@@ -46,6 +46,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
@@ -193,4 +194,15 @@
             }
         });
     }
+
+    @Test
+    public void testSetWindowMagnificationConnection() throws Exception {
+        AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
+        IWindowMagnificationConnection connection = Mockito.mock(
+                IWindowMagnificationConnection.class);
+
+        manager.setWindowMagnificationConnection(connection);
+
+        verify(mMockService).setWindowMagnificationConnection(connection);
+    }
 }
diff --git a/core/tests/overlaytests/remount/host/AndroidTest.xml b/core/tests/overlaytests/remount/host/AndroidTest.xml
index 11eadf1a..087b731 100644
--- a/core/tests/overlaytests/remount/host/AndroidTest.xml
+++ b/core/tests/overlaytests/remount/host/AndroidTest.xml
@@ -19,9 +19,6 @@
     <option name="test-tag" value="OverlayRemountedTest" />
 
     <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
-    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="run-command" value="remount" />
-    </target_preparer>
 
     <test class="com.android.tradefed.testtype.HostTest">
         <option name="jar" value="OverlayRemountedTest.jar" />
diff --git a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlayHostTest.java b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlayHostTest.java
deleted file mode 100644
index 84af187..0000000
--- a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlayHostTest.java
+++ /dev/null
@@ -1,114 +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.overlaytest.remounted;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-
-import org.junit.Rule;
-import org.junit.rules.RuleChain;
-import org.junit.rules.TemporaryFolder;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class OverlayHostTest extends BaseHostJUnit4Test {
-    private static final long TIME_OUT_MS = 30000;
-    private static final String RES_INSTRUMENTATION_ARG = "res";
-    private static final String OVERLAY_INSTRUMENTATION_ARG = "overlays";
-    private static final String RESOURCES_TYPE_SUFFIX = "_type";
-    private static final String RESOURCES_DATA_SUFFIX = "_data";
-
-    public final TemporaryFolder mTemporaryFolder = new TemporaryFolder();
-    public final SystemPreparer mPreparer = new SystemPreparer(mTemporaryFolder, this::getDevice);
-
-    @Rule
-    public final RuleChain ruleChain = RuleChain.outerRule(mTemporaryFolder).around(mPreparer);
-    private Map<String, String> mLastResults;
-
-    /**
-     * Retrieves the values of the resources in the test package. The test package must use the
-     * {@link com.android.overlaytest.remounted.target.ResourceRetrievalRunner} instrumentation.
-     **/
-    void retrieveResource(String testPackageName, List<String> requiredOverlayPaths,
-            String... resourceNames) throws DeviceNotAvailableException {
-        final HashMap<String, String> args = new HashMap<>();
-        if (!requiredOverlayPaths.isEmpty()) {
-            // Enclose the require overlay paths in quotes so the arguments will be string arguments
-            // rather than file arguments.
-            args.put(OVERLAY_INSTRUMENTATION_ARG,
-                    String.format("\"%s\"", String.join(" ", requiredOverlayPaths)));
-        }
-
-        if (resourceNames.length == 0) {
-            throw new IllegalArgumentException("Must specify at least one resource to retrieve.");
-        }
-
-        // Pass the names of the resources to retrieve into the test as one string.
-        args.put(RES_INSTRUMENTATION_ARG,
-                String.format("\"%s\"", String.join(" ", resourceNames)));
-
-        runDeviceTests(getDevice(), null, testPackageName, null, null, null, TIME_OUT_MS,
-                TIME_OUT_MS, TIME_OUT_MS, false, false, args);
-
-        // Retrieve the results of the most recently run test.
-        mLastResults = (getLastDeviceRunResults().getRunMetrics() == mLastResults) ? null :
-                getLastDeviceRunResults().getRunMetrics();
-    }
-
-    /** Returns the base resource directories of the specified packages. */
-    List<String> getPackagePaths(String... packageNames)
-            throws DeviceNotAvailableException {
-        final ArrayList<String> paths = new ArrayList<>();
-        for (String packageName : packageNames) {
-            // Use the package manager shell command to find the path of the package.
-            final String result = getDevice().executeShellCommand(
-                    String.format("pm dump %s | grep \"resourcePath=\"", packageName));
-            assertNotNull("Failed to find path for package " + packageName, result);
-            int splitIndex = result.indexOf('=');
-            assertTrue(splitIndex >= 0);
-            paths.add(result.substring(splitIndex + 1).trim());
-        }
-        return paths;
-    }
-
-    /** Builds the full name of a resource in the form package:type/entry. */
-    String resourceName(String pkg, String type, String entry) {
-        return String.format("%s:%s/%s", pkg, type, entry);
-    }
-
-    /**
-     * Asserts that the type and data of a a previously retrieved is the same as expected.
-     * @param resourceName the full name of the resource in the form package:type/entry
-     * @param type the expected {@link android.util.TypedValue} type of the resource
-     * @param data the expected value of the resource when coerced to a string using
-     *             {@link android.util.TypedValue#coerceToString()}
-     **/
-    void assertResource(String resourceName, int type, String data) {
-        assertNotNull("Failed to get test results", mLastResults);
-        assertNotEquals("No resource values were retrieved", mLastResults.size(), 0);
-        assertEquals("" + type, mLastResults.get(resourceName + RESOURCES_TYPE_SUFFIX));
-        assertEquals("" + data, mLastResults.get(resourceName + RESOURCES_DATA_SUFFIX));
-    }
-}
diff --git a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java
index 4939e16..06b2ac8 100644
--- a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java
+++ b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java
@@ -16,17 +16,21 @@
 
 package com.android.overlaytest.remounted;
 
+import static org.junit.Assert.assertTrue;
+
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
+import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 
-import java.util.Collections;
-import java.util.List;
-
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class OverlaySharedLibraryTest extends OverlayHostTest {
+public class OverlaySharedLibraryTest extends BaseHostJUnit4Test {
     private static final String TARGET_APK = "OverlayRemountedTest_Target.apk";
     private static final String TARGET_PACKAGE = "com.android.overlaytest.remounted.target";
     private static final String SHARED_LIBRARY_APK =
@@ -38,6 +42,17 @@
     private static final String SHARED_LIBRARY_OVERLAY_PACKAGE =
             "com.android.overlaytest.remounted.shared_library.overlay";
 
+    public final TemporaryFolder temporaryFolder = new TemporaryFolder();
+    public final SystemPreparer preparer = new SystemPreparer(temporaryFolder, this::getDevice);
+
+    @Rule
+    public final RuleChain ruleChain = RuleChain.outerRule(temporaryFolder).around(preparer);
+
+    @Before
+    public void startBefore() throws DeviceNotAvailableException {
+        getDevice().waitForDeviceAvailable();
+    }
+
     @Test
     public void testSharedLibrary() throws Exception {
         final String targetResource = resourceName(TARGET_PACKAGE, "bool",
@@ -45,23 +60,20 @@
         final String libraryResource = resourceName(SHARED_LIBRARY_PACKAGE, "bool",
                 "shared_library_overlaid");
 
-        mPreparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk")
+        preparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk")
                 .installResourceApk(SHARED_LIBRARY_OVERLAY_APK, SHARED_LIBRARY_OVERLAY_PACKAGE)
                 .reboot()
                 .setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, false)
                 .installResourceApk(TARGET_APK, TARGET_PACKAGE);
 
         // The shared library resource is not currently overlaid.
-        retrieveResource(Collections.emptyList(), targetResource, libraryResource);
-        assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "false");
-        assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "false");
+        assertResource(targetResource, "false");
+        assertResource(libraryResource, "false");
 
         // Overlay the shared library resource.
-        mPreparer.setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, true);
-        retrieveResource(getPackagePaths(SHARED_LIBRARY_OVERLAY_PACKAGE), targetResource,
-                libraryResource);
-        assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
-        assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
+        preparer.setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, true);
+        assertResource(targetResource, "true");
+        assertResource(libraryResource, "true");
     }
 
     @Test
@@ -71,20 +83,27 @@
         final String libraryResource = resourceName(SHARED_LIBRARY_PACKAGE, "bool",
                 "shared_library_overlaid");
 
-        mPreparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk")
+        preparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk")
                 .installResourceApk(SHARED_LIBRARY_OVERLAY_APK, SHARED_LIBRARY_OVERLAY_PACKAGE)
                 .setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, true)
                 .reboot()
                 .installResourceApk(TARGET_APK, TARGET_PACKAGE);
 
-        retrieveResource(getPackagePaths(SHARED_LIBRARY_OVERLAY_PACKAGE), targetResource,
-                libraryResource);
-        assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
-        assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
+        assertResource(targetResource, "true");
+        assertResource(libraryResource, "true");
     }
 
-    private void retrieveResource(List<String> requiredOverlayPaths, String... resourceNames)
+    /** Builds the full name of a resource in the form package:type/entry. */
+    String resourceName(String pkg, String type, String entry) {
+        return String.format("%s:%s/%s", pkg, type, entry);
+    }
+
+    void assertResource(String resourceName, String expectedValue)
             throws DeviceNotAvailableException {
-        retrieveResource(TARGET_PACKAGE, requiredOverlayPaths, resourceNames);
+        final String result = getDevice().executeShellCommand(
+                String.format("cmd overlay lookup %s %s", TARGET_PACKAGE, resourceName));
+        assertTrue(String.format("expected: <[%s]> in: <[%s]>", expectedValue, result),
+                result.equals(expectedValue + "\n") ||
+                result.endsWith("-> " + expectedValue + "\n"));
     }
 }
diff --git a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java
index 7028f2f..8696091 100644
--- a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java
+++ b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java
@@ -38,8 +38,7 @@
 import java.util.concurrent.TimeoutException;
 
 class SystemPreparer extends ExternalResource {
-    private static final long REBOOT_SLEEP_MS = 30000;
-    private static final long OVERLAY_ENABLE_TIMEOUT_MS = 20000;
+    private static final long OVERLAY_ENABLE_TIMEOUT_MS = 30000;
 
     // The paths of the files pushed onto the device through this rule.
     private ArrayList<String> mPushedFiles = new ArrayList<>();
@@ -59,6 +58,7 @@
     SystemPreparer pushResourceFile(String resourcePath,
             String outputPath) throws DeviceNotAvailableException, IOException {
         final ITestDevice device = mDeviceProvider.getDevice();
+        device.executeAdbCommand("remount");
         assertTrue(device.pushFile(copyResourceToTemp(resourcePath), outputPath));
         mPushedFiles.add(outputPath);
         return this;
@@ -77,7 +77,7 @@
 
     /** Sets the enable state of an overlay pacakage. */
     SystemPreparer setOverlayEnabled(String packageName, boolean enabled)
-            throws ExecutionException, TimeoutException {
+            throws ExecutionException, DeviceNotAvailableException {
         final ITestDevice device = mDeviceProvider.getDevice();
 
         // Wait for the overlay to change its enabled state.
@@ -86,8 +86,10 @@
                 device.executeShellCommand(String.format("cmd overlay %s %s",
                         enabled ? "enable" : "disable", packageName));
 
-                final String pattern = (enabled ? "[x]" : "[ ]") + " " + packageName;
-                if (device.executeShellCommand("cmd overlay list").contains(pattern)) {
+                final String result = device.executeShellCommand("cmd overlay dump " + packageName);
+                final int startIndex = result.indexOf("mIsEnabled");
+                final int endIndex = result.indexOf('\n', startIndex);
+                if (result.substring(startIndex, endIndex).contains((enabled) ? "true" : "false")) {
                     return true;
                 }
             }
@@ -98,6 +100,8 @@
         try {
             enabledListener.get(OVERLAY_ENABLE_TIMEOUT_MS, MILLISECONDS);
         } catch (InterruptedException ignored) {
+        } catch (TimeoutException e) {
+            throw new IllegalStateException(device.executeShellCommand("cmd overlay list"));
         }
 
         return this;
@@ -106,14 +110,7 @@
     /** Restarts the device and waits until after boot is completed. */
     SystemPreparer reboot() throws DeviceNotAvailableException {
         final ITestDevice device = mDeviceProvider.getDevice();
-        device.executeShellCommand("stop");
-        device.executeShellCommand("start");
-        try {
-            // Sleep until the device is ready for test execution.
-            Thread.sleep(REBOOT_SLEEP_MS);
-        } catch (InterruptedException ignored) {
-        }
-
+        device.reboot();
         return this;
     }
 
@@ -141,12 +138,14 @@
     protected void after() {
         final ITestDevice device = mDeviceProvider.getDevice();
         try {
+            device.executeAdbCommand("remount");
             for (final String file : mPushedFiles) {
                 device.deleteFile(file);
             }
             for (final String packageName : mInstalledPackages) {
                 device.uninstallPackage(packageName);
             }
+            device.reboot();
         } catch (DeviceNotAvailableException e) {
             Assert.fail(e.toString());
         }
diff --git a/core/tests/overlaytests/remount/target/AndroidManifest.xml b/core/tests/overlaytests/remount/target/AndroidManifest.xml
index 32fec43..dc07dca 100644
--- a/core/tests/overlaytests/remount/target/AndroidManifest.xml
+++ b/core/tests/overlaytests/remount/target/AndroidManifest.xml
@@ -23,8 +23,4 @@
         <uses-library android:name="com.android.overlaytest.remounted.shared_library"
                       android:required="true" />
     </application>
-
-    <instrumentation android:name="com.android.overlaytest.remounted.target.ResourceRetrievalRunner"
-                     android:targetPackage="com.android.overlaytest.remounted.target"
-                     android:label="Remounted system RRO tests" />
 </manifest>
diff --git a/core/tests/overlaytests/remount/target/src/com/android/overlaytest/remounted/target/ResourceRetrievalRunner.java b/core/tests/overlaytests/remount/target/src/com/android/overlaytest/remounted/target/ResourceRetrievalRunner.java
deleted file mode 100644
index 2e4c211..0000000
--- a/core/tests/overlaytests/remount/target/src/com/android/overlaytest/remounted/target/ResourceRetrievalRunner.java
+++ /dev/null
@@ -1,140 +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.overlaytest.remounted.target;
-
-import android.app.Activity;
-import android.app.Instrumentation;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.util.Log;
-import android.util.TypedValue;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.concurrent.Executor;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
-
-/**
- * An {@link Instrumentation} that retrieves the value of specified resources within the
- * application.
- **/
-public class ResourceRetrievalRunner extends Instrumentation {
-    private static final String TAG = ResourceRetrievalRunner.class.getSimpleName();
-
-    // A list of whitespace separated resource names of which to retrieve the resource values.
-    private static final String RESOURCE_LIST_TAG = "res";
-
-    // A list of whitespace separated overlay package paths that must be present before retrieving
-    // resource values.
-    private static final String REQUIRED_OVERLAYS_LIST_TAG = "overlays";
-
-    // The suffixes of the keys returned from the instrumentation. To retrieve the type of a
-    // resource looked up with the instrumentation, append the {@link #RESOURCES_TYPE_SUFFIX} suffix
-    // to the end of the name of the resource. For the value of a resource, use
-    // {@link #RESOURCES_DATA_SUFFIX} instead.
-    private static final String RESOURCES_TYPE_SUFFIX = "_type";
-    private static final String RESOURCES_DATA_SUFFIX = "_data";
-
-    // The amount of time in seconds to wait for the overlays to be present in the AssetManager.
-    private static final int OVERLAY_PATH_TIMEOUT = 60;
-
-    private final ArrayList<String> mResourceNames = new ArrayList<>();
-    private final ArrayList<String> mOverlayPaths = new ArrayList<>();
-    private final Bundle mResult = new Bundle();
-
-    /**
-     * Receives the instrumentation arguments and runs the resource retrieval.
-     * The entry with key {@link #RESOURCE_LIST_TAG} in the {@link Bundle} arguments is a
-     * whitespace separated string of resource names of which to retrieve the resource values.
-     * The entry with key {@link #REQUIRED_OVERLAYS_LIST_TAG} in the {@link Bundle} arguments is a
-     * whitespace separated string of overlay package paths prefixes that must be present before
-     * retrieving the resource values.
-     */
-    @Override
-    public void onCreate(Bundle arguments) {
-        super.onCreate(arguments);
-        mResourceNames.addAll(Arrays.asList(arguments.getString(RESOURCE_LIST_TAG).split(" ")));
-        if (arguments.containsKey(REQUIRED_OVERLAYS_LIST_TAG)) {
-            mOverlayPaths.addAll(Arrays.asList(
-                    arguments.getString(REQUIRED_OVERLAYS_LIST_TAG).split(" ")));
-        }
-        start();
-    }
-
-    @Override
-    public void onStart() {
-        final Resources res = getContext().getResources();
-        res.getAssets().setResourceResolutionLoggingEnabled(true);
-
-        if (!mOverlayPaths.isEmpty()) {
-            Log.d(TAG, String.format("Waiting for overlay paths [%s]",
-                    String.join(",", mOverlayPaths)));
-
-            // Wait for all required overlays to be present in the AssetManager.
-            final FutureTask<Boolean> overlayListener = new FutureTask<>(() -> {
-                while (!mOverlayPaths.isEmpty()) {
-                    final String[] apkPaths = res.getAssets().getApkPaths();
-                    for (String path : apkPaths) {
-                        for (String overlayPath : mOverlayPaths) {
-                            if (path.startsWith(overlayPath)) {
-                                mOverlayPaths.remove(overlayPath);
-                                break;
-                            }
-                        }
-                    }
-                }
-                return true;
-            });
-
-            try {
-                final Executor executor = (t) -> new Thread(t).start();
-                executor.execute(overlayListener);
-                overlayListener.get(OVERLAY_PATH_TIMEOUT, TimeUnit.SECONDS);
-            } catch (Exception e) {
-                Log.e(TAG, String.format("Failed to wait for required overlays [%s]",
-                        String.join(",", mOverlayPaths)), e);
-                finish(Activity.RESULT_CANCELED, mResult);
-            }
-        }
-
-        // Retrieve the values for each resource passed in.
-        final TypedValue typedValue = new TypedValue();
-        for (final String resourceName : mResourceNames) {
-            try {
-                final int resId = res.getIdentifier(resourceName, null, null);
-                res.getValue(resId, typedValue, true);
-                Log.d(TAG, String.format("Resolution for 0x%s: %s", Integer.toHexString(resId),
-                        res.getAssets().getLastResourceResolution()));
-            } catch (Resources.NotFoundException e) {
-                Log.e(TAG, "Failed to retrieve value for resource " + resourceName, e);
-                finish(Activity.RESULT_CANCELED, mResult);
-            }
-
-            putValue(resourceName, typedValue);
-        }
-
-        finish(Activity.RESULT_OK, mResult);
-    }
-
-    private void putValue(String resourceName, TypedValue value) {
-        mResult.putInt(resourceName + RESOURCES_TYPE_SUFFIX, value.type);
-        final CharSequence textValue = value.coerceToString();
-        mResult.putString(resourceName + RESOURCES_DATA_SUFFIX,
-                textValue == null ? "null" : textValue.toString());
-    }
-}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 0574775..877ef26 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -180,7 +180,6 @@
     <assign-permission name="android.permission.ACCESS_LOWPAN_STATE" uid="lowpan" />
     <assign-permission name="android.permission.MANAGE_LOWPAN_INTERFACES" uid="lowpan" />
 
-    <assign-permission name="android.permission.BATTERY_STATS" uid="statsd" />
     <assign-permission name="android.permission.DUMP" uid="statsd" />
     <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="statsd" />
     <assign-permission name="android.permission.STATSCOMPANION" uid="statsd" />
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index b93759f..c445885 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -108,10 +108,26 @@
     }
 }
 
+// FIXME: Share with the version in android_bitmap.cpp?
+// Skia's SkNamedGamut::kDCIP3 is based on a white point of D65. This gamut
+// matches the white point used by ColorSpace.Named.DCIP3.
+static constexpr skcms_Matrix3x3 kDCIP3 = {{
+        {0.486143, 0.323835, 0.154234},
+        {0.226676, 0.710327, 0.0629966},
+        {0.000800549, 0.0432385, 0.78275},
+}};
+
 sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) {
     if (dataspace == HAL_DATASPACE_UNKNOWN) {
         return SkColorSpace::MakeSRGB();
     }
+    if (dataspace == HAL_DATASPACE_DCI_P3) {
+        // This cannot be handled by the switch statements below because it
+        // needs to use the locally-defined kDCIP3 gamut, rather than the one in
+        // Skia (SkNamedGamut), which is used for other data spaces with
+        // HAL_DATASPACE_STANDARD_DCI_P3 (e.g. HAL_DATASPACE_DISPLAY_P3).
+        return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, kDCIP3);
+    }
 
     skcms_Matrix3x3 gamut;
     switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
@@ -152,10 +168,12 @@
             return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
         case HAL_DATASPACE_TRANSFER_GAMMA2_8:
             return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
+        case HAL_DATASPACE_TRANSFER_ST2084:
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut);
+        case HAL_DATASPACE_TRANSFER_SMPTE_170M:
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, gamut);
         case HAL_DATASPACE_TRANSFER_UNSPECIFIED:
             return nullptr;
-        case HAL_DATASPACE_TRANSFER_SMPTE_170M:
-        case HAL_DATASPACE_TRANSFER_ST2084:
         case HAL_DATASPACE_TRANSFER_HLG:
         default:
             ALOGV("Unsupported Gamma: %d", dataspace);
diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
index 751bb6a..127d00c 100644
--- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
@@ -110,7 +110,6 @@
      * Logs the status of a location report received from the HAL
      */
     public void logReceivedLocationStatus(boolean isSuccessful) {
-        StatsLog.write(StatsLog.GPS_LOCATION_STATUS_REPORTED, isSuccessful);
         if (!isSuccessful) {
             mLocationFailureStatistics.addItem(1.0);
             return;
@@ -127,7 +126,6 @@
                 DEFAULT_TIME_BETWEEN_FIXES_MILLISECS, desiredTimeBetweenFixesMilliSeconds)) - 1;
         if (numReportMissed > 0) {
             for (int i = 0; i < numReportMissed; i++) {
-                StatsLog.write(StatsLog.GPS_LOCATION_STATUS_REPORTED, false);
                 mLocationFailureStatistics.addItem(1.0);
             }
         }
@@ -138,7 +136,6 @@
      */
     public void logTimeToFirstFixMilliSecs(int timeToFirstFixMilliSeconds) {
         mTimeToFirstFixSecStatistics.addItem((double) (timeToFirstFixMilliSeconds / 1000));
-        StatsLog.write(StatsLog.GPS_TIME_TO_FIRST_FIX_REPORTED, timeToFirstFixMilliSeconds);
     }
 
     /**
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index ece5335..8cd3c6e 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -598,6 +598,11 @@
         private boolean mMuteHapticChannels = true;
         private HashSet<String> mTags = new HashSet<String>();
         private Bundle mBundle;
+        private int mPrivacySensitive = PRIVACY_SENSITIVE_DEFAULT;
+
+        private static final int PRIVACY_SENSITIVE_DEFAULT = -1;
+        private static final int PRIVACY_SENSITIVE_DISABLED = 0;
+        private static final int PRIVACY_SENSITIVE_ENABLED = 1;
 
         /**
          * Constructs a new Builder with the defaults.
@@ -638,11 +643,20 @@
             if (mMuteHapticChannels) {
                 aa.mFlags |= FLAG_MUTE_HAPTIC;
             }
-            // capturing for camcorder of communication is private by default to
-            // reflect legacy behavior
-            if (aa.mSource == MediaRecorder.AudioSource.VOICE_COMMUNICATION
-                    || aa.mSource == MediaRecorder.AudioSource.CAMCORDER) {
+
+            if (mPrivacySensitive == PRIVACY_SENSITIVE_DEFAULT) {
+                // capturing for camcorder or communication is private by default to
+                // reflect legacy behavior
+                if (mSource == MediaRecorder.AudioSource.VOICE_COMMUNICATION
+                        || mSource == MediaRecorder.AudioSource.CAMCORDER) {
+                    aa.mFlags |= FLAG_CAPTURE_PRIVATE;
+                } else {
+                    aa.mFlags &= ~FLAG_CAPTURE_PRIVATE;
+                }
+            } else if (mPrivacySensitive == PRIVACY_SENSITIVE_ENABLED) {
                 aa.mFlags |= FLAG_CAPTURE_PRIVATE;
+            } else {
+                aa.mFlags &= ~FLAG_CAPTURE_PRIVATE;
             }
             aa.mTags = (HashSet<String>) mTags.clone();
             aa.mFormattedTags = TextUtils.join(";", mTags);
@@ -967,6 +981,20 @@
             mMuteHapticChannels = muted;
             return this;
         }
+
+        /**
+         * @hide
+         * Indicates if an AudioRecord build with this AudioAttributes is privacy sensitive or not.
+         * See {@link AudioRecord.Builder#setPrivacySensitive(boolean)}.
+         * @param privacySensitive True if capture must be marked as privacy sensitive,
+         * false otherwise.
+         * @return the same Builder instance.
+         */
+        public @NonNull Builder setPrivacySensitive(boolean privacySensitive) {
+            mPrivacySensitive =
+                privacySensitive ? PRIVACY_SENSITIVE_ENABLED : PRIVACY_SENSITIVE_DISABLED;
+            return this;
+        }
     };
 
     @Override
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index fd3523d..4d26b8d 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -315,7 +315,7 @@
      * @hide
      * Class constructor with {@link AudioAttributes} and {@link AudioFormat}.
      * @param attributes a non-null {@link AudioAttributes} instance. Use
-     *     {@link AudioAttributes.Builder#setAudioSource(int)} for configuring the audio
+     *     {@link AudioAttributes.Builder#setCapturePreset(int)} for configuring the audio
      *     source for this instance.
      * @param format a non-null {@link AudioFormat} instance describing the format of the data
      *     that will be recorded through this AudioRecord. See {@link AudioFormat.Builder} for
@@ -754,17 +754,10 @@
                             "Cannot request private capture with source: " + source);
                 }
 
-                int flags = mAttributes.getAllFlags();
-                if (mPrivacySensitive == PRIVACY_SENSITIVE_DISABLED) {
-                    flags &= ~AudioAttributes.FLAG_CAPTURE_PRIVATE;
-                } else if (mPrivacySensitive == PRIVACY_SENSITIVE_ENABLED) {
-                    flags |= AudioAttributes.FLAG_CAPTURE_PRIVATE;
-                }
-                if (flags != mAttributes.getAllFlags()) {
-                    mAttributes = new AudioAttributes.Builder(mAttributes)
-                            .replaceFlags(flags)
-                            .build();
-                }
+                mAttributes = new AudioAttributes.Builder(mAttributes)
+                        .setInternalCapturePreset(source)
+                        .setPrivacySensitive(mPrivacySensitive == PRIVACY_SENSITIVE_ENABLED)
+                        .build();
             }
 
             try {
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index 5dd0b1c..aa38e51 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -24,8 +24,7 @@
  */
 oneway interface IMediaRoute2Provider {
     void setClient(IMediaRoute2ProviderClient client);
-    void requestCreateSession(String packageName, String routeId,
-            String routeFeature, long requestId);
+    void requestCreateSession(String packageName, String routeId, long requestId);
     void releaseSession(String sessionId);
 
     void selectRoute(String sessionId, String routeId);
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 3cdaa07..2d3e185 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -47,12 +47,12 @@
     List<MediaRoute2Info> getSystemRoutes();
     void registerClient2(IMediaRouter2Client client, String packageName);
     void unregisterClient2(IMediaRouter2Client client);
-    void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route, in Intent request);
+    void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route,
+            in Intent request);
     void requestSetVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int volume);
     void requestUpdateVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int direction);
 
-    void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route,
-            String routeFeature, int requestId);
+    void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route, int requestId);
     void setDiscoveryRequest2(IMediaRouter2Client client, in RouteDiscoveryPreference preference);
     void selectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
     void deselectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 021e23e..239dfed 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -134,59 +134,204 @@
      */
     public static final int DEVICE_TYPE_BLUETOOTH = 3;
 
-    @NonNull
     final String mId;
-    @Nullable
-    final String mProviderId;
-    @NonNull
     final CharSequence mName;
-    @Nullable
-    final CharSequence mDescription;
-    @Nullable
-    final @ConnectionState int mConnectionState;
-    @Nullable
-    final Uri mIconUri;
-    @Nullable
-    final String mClientPackageName;
-    @NonNull
     final List<String> mFeatures;
+    @DeviceType
+    final int mDeviceType;
+    final Uri mIconUri;
+    final CharSequence mDescription;
+    @ConnectionState
+    final int mConnectionState;
+    final String mClientPackageName;
     final int mVolume;
     final int mVolumeMax;
     final int mVolumeHandling;
-    final @DeviceType int mDeviceType;
-    @Nullable
     final Bundle mExtras;
+    final String mProviderId;
 
     MediaRoute2Info(@NonNull Builder builder) {
         mId = builder.mId;
-        mProviderId = builder.mProviderId;
         mName = builder.mName;
+        mFeatures = builder.mFeatures;
+        mDeviceType = builder.mDeviceType;
+        mIconUri = builder.mIconUri;
         mDescription = builder.mDescription;
         mConnectionState = builder.mConnectionState;
-        mIconUri = builder.mIconUri;
         mClientPackageName = builder.mClientPackageName;
-        mFeatures = builder.mFeatures;
-        mVolume = builder.mVolume;
-        mVolumeMax = builder.mVolumeMax;
         mVolumeHandling = builder.mVolumeHandling;
-        mDeviceType = builder.mDeviceType;
+        mVolumeMax = builder.mVolumeMax;
+        mVolume = builder.mVolume;
         mExtras = builder.mExtras;
+        mProviderId = builder.mProviderId;
     }
 
     MediaRoute2Info(@NonNull Parcel in) {
         mId = in.readString();
-        mProviderId = in.readString();
         mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mFeatures = in.createStringArrayList();
+        mDeviceType = in.readInt();
+        mIconUri = in.readParcelable(null);
         mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
         mConnectionState = in.readInt();
-        mIconUri = in.readParcelable(null);
         mClientPackageName = in.readString();
-        mFeatures = in.createStringArrayList();
-        mVolume = in.readInt();
-        mVolumeMax = in.readInt();
         mVolumeHandling = in.readInt();
-        mDeviceType = in.readInt();
+        mVolumeMax = in.readInt();
+        mVolume = in.readInt();
         mExtras = in.readBundle();
+        mProviderId = in.readString();
+    }
+
+    /**
+     * Gets the id of the route. The routes which are given by {@link MediaRouter2} will have
+     * unique IDs.
+     * <p>
+     * In order to ensure uniqueness in {@link MediaRouter2} side, the value of this method
+     * can be different from what was set in {@link MediaRoute2ProviderService}.
+     *
+     * @see Builder#Builder(String, CharSequence)
+     */
+    @NonNull
+    public String getId() {
+        if (mProviderId != null) {
+            return toUniqueId(mProviderId, mId);
+        } else {
+            return mId;
+        }
+    }
+
+    /**
+     * Gets the user-visible name of the route.
+     */
+    @NonNull
+    public CharSequence getName() {
+        return mName;
+    }
+
+    /**
+     * Gets the supported features of the route.
+     */
+    @NonNull
+    public List<String> getFeatures() {
+        return mFeatures;
+    }
+
+    /**
+     * Gets the type of the receiver device associated with this route.
+     *
+     * @return The type of the receiver device associated with this route:
+     * {@link #DEVICE_TYPE_REMOTE_TV}, {@link #DEVICE_TYPE_REMOTE_SPEAKER},
+     * {@link #DEVICE_TYPE_BLUETOOTH}.
+     */
+    @DeviceType
+    public int getDeviceType() {
+        return mDeviceType;
+    }
+
+    /**
+     * Gets the URI of the icon representing this route.
+     * <p>
+     * This icon will be used in picker UIs if available.
+     *
+     * @return The URI of the icon representing this route, or null if none.
+     */
+    @Nullable
+    public Uri getIconUri() {
+        return mIconUri;
+    }
+
+    /**
+     * Gets the user-visible description of the route.
+     */
+    @Nullable
+    public CharSequence getDescription() {
+        return mDescription;
+    }
+
+    /**
+     * Gets the connection state of the route.
+     *
+     * @return The connection state of this route: {@link #CONNECTION_STATE_DISCONNECTED},
+     * {@link #CONNECTION_STATE_CONNECTING}, or {@link #CONNECTION_STATE_CONNECTED}.
+     */
+    @ConnectionState
+    public int getConnectionState() {
+        return mConnectionState;
+    }
+
+    /**
+     * Gets the package name of the client that uses the route.
+     * Returns null if no clients use this route.
+     * @hide
+     */
+    @Nullable
+    public String getClientPackageName() {
+        return mClientPackageName;
+    }
+
+    /**
+     * Gets information about how volume is handled on the route.
+     *
+     * @return {@link #PLAYBACK_VOLUME_FIXED} or {@link #PLAYBACK_VOLUME_VARIABLE}
+     */
+    public int getVolumeHandling() {
+        return mVolumeHandling;
+    }
+
+    /**
+     * Gets the maximum volume of the route.
+     */
+    public int getVolumeMax() {
+        return mVolumeMax;
+    }
+
+    /**
+     * Gets the current volume of the route. This may be invalid if the route is not selected.
+     */
+    public int getVolume() {
+        return mVolume;
+    }
+
+    @Nullable
+    public Bundle getExtras() {
+        return mExtras == null ? null : new Bundle(mExtras);
+    }
+
+    /**
+     * Gets the original id set by {@link Builder#Builder(String, CharSequence)}.
+     * @hide
+     */
+    @NonNull
+    public String getOriginalId() {
+        return mId;
+    }
+
+    /**
+     * Gets the provider id of the route. It is assigned automatically by
+     * {@link com.android.server.media.MediaRouterService}.
+     *
+     * @return provider id of the route or null if it's not set.
+     * @hide
+     */
+    @Nullable
+    public String getProviderId() {
+        return mProviderId;
+    }
+
+    /**
+     * Returns if the route has at least one of the specified route features.
+     *
+     * @param features the list of route features to consider
+     * @return true if the route has at least one feature in the list
+     */
+    public boolean hasAnyFeatures(@NonNull Collection<String> features) {
+        Objects.requireNonNull(features, "features must not be null");
+        for (String feature : features) {
+            if (getFeatures().contains(feature)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
@@ -213,172 +358,49 @@
             return false;
         }
         MediaRoute2Info other = (MediaRoute2Info) obj;
+
+        // Note: mExtras is not included.
         return Objects.equals(mId, other.mId)
-                && Objects.equals(mProviderId, other.mProviderId)
                 && Objects.equals(mName, other.mName)
+                && Objects.equals(mFeatures, other.mFeatures)
+                && (mDeviceType == other.mDeviceType)
+                && Objects.equals(mIconUri, other.mIconUri)
                 && Objects.equals(mDescription, other.mDescription)
                 && (mConnectionState == other.mConnectionState)
-                && Objects.equals(mIconUri, other.mIconUri)
                 && Objects.equals(mClientPackageName, other.mClientPackageName)
-                && Objects.equals(mFeatures, other.mFeatures)
-                && (mVolume == other.mVolume)
-                && (mVolumeMax == other.mVolumeMax)
                 && (mVolumeHandling == other.mVolumeHandling)
-                && (mDeviceType == other.mDeviceType)
-                //TODO: This will be evaluated as false in most cases. Try not to.
-                && Objects.equals(mExtras, other.mExtras);
+                && (mVolumeMax == other.mVolumeMax)
+                && (mVolume == other.mVolume)
+                && Objects.equals(mProviderId, other.mProviderId);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mId, mName, mDescription, mConnectionState, mIconUri,
-                mFeatures, mVolume, mVolumeMax, mVolumeHandling, mDeviceType);
+        // Note: mExtras is not included.
+        return Objects.hash(mId, mName, mFeatures, mDeviceType, mIconUri, mDescription,
+                mConnectionState, mClientPackageName, mVolumeHandling, mVolumeMax, mVolume,
+                mProviderId);
     }
 
-    /**
-     * Gets the id of the route. The routes which are given by {@link MediaRouter2} will have
-     * unique IDs.
-     * <p>
-     * In order to ensure uniqueness in {@link MediaRouter2} side, the value of this method
-     * can be different from what was set in {@link MediaRoute2ProviderService}.
-     *
-     * @see Builder#Builder(String, CharSequence)
-     */
-    @NonNull
-    public String getId() {
-        if (mProviderId != null) {
-            return toUniqueId(mProviderId, mId);
-        } else {
-            return mId;
-        }
-    }
-
-    /**
-     * Gets the original id set by {@link Builder#Builder(String, CharSequence)}.
-     * @hide
-     */
-    @NonNull
-    public String getOriginalId() {
-        return mId;
-    }
-
-    /**
-     * Gets the provider id of the route. It is assigned automatically by
-     * {@link com.android.server.media.MediaRouterService}.
-     *
-     * @return provider id of the route or null if it's not set.
-     * @hide
-     */
-    @Nullable
-    public String getProviderId() {
-        return mProviderId;
-    }
-
-    @NonNull
-    public CharSequence getName() {
-        return mName;
-    }
-
-    @Nullable
-    public CharSequence getDescription() {
-        return mDescription;
-    }
-
-    /**
-     * Gets the connection state of the route.
-     *
-     * @return The connection state of this route: {@link #CONNECTION_STATE_DISCONNECTED},
-     * {@link #CONNECTION_STATE_CONNECTING}, or {@link #CONNECTION_STATE_CONNECTED}.
-     */
-    @ConnectionState
-    public int getConnectionState() {
-        return mConnectionState;
-    }
-
-    /**
-     * Gets the URI of the icon representing this route.
-     * <p>
-     * This icon will be used in picker UIs if available.
-     *
-     * @return The URI of the icon representing this route, or null if none.
-     */
-    @Nullable
-    public Uri getIconUri() {
-        return mIconUri;
-    }
-
-    /**
-     * Gets the package name of the client that uses the route.
-     * Returns null if no clients use this.
-     * @hide
-     */
-    @Nullable
-    public String getClientPackageName() {
-        return mClientPackageName;
-    }
-
-    /**
-     * Gets the supported categories of the route.
-     */
-    @NonNull
-    public List<String> getFeatures() {
-        return mFeatures;
-    }
-
-    /**
-     * Gets the type of the receiver device associated with this route.
-     *
-     * @return The type of the receiver device associated with this route:
-     * {@link #DEVICE_TYPE_REMOTE_TV}, {@link #DEVICE_TYPE_REMOTE_SPEAKER},
-     * {@link #DEVICE_TYPE_BLUETOOTH}.
-     */
-    @DeviceType
-    public int getDeviceType() {
-        return mDeviceType;
-    }
-
-    /**
-     * Gets the current volume of the route. This may be invalid if the route is not selected.
-     */
-    public int getVolume() {
-        return mVolume;
-    }
-
-    /**
-     * Gets the maximum volume of the route.
-     */
-    public int getVolumeMax() {
-        return mVolumeMax;
-    }
-
-    /**
-     * Gets information about how volume is handled on the route.
-     *
-     * @return {@link #PLAYBACK_VOLUME_FIXED} or {@link #PLAYBACK_VOLUME_VARIABLE}
-     */
-    public int getVolumeHandling() {
-        return mVolumeHandling;
-    }
-
-    @Nullable
-    public Bundle getExtras() {
-        return mExtras;
-    }
-
-    /**
-     * Returns if the route has at least one of the specified route features.
-     *
-     * @param features the list of route features to consider
-     * @return true if the route has at least one feature in the list
-     */
-    public boolean hasAnyFeatures(@NonNull Collection<String> features) {
-        Objects.requireNonNull(features, "features must not be null");
-        for (String feature : features) {
-            if (getFeatures().contains(feature)) {
-                return true;
-            }
-        }
-        return false;
+    @Override
+    public String toString() {
+        // Note: mExtras is not printed here.
+        StringBuilder result = new StringBuilder()
+                .append("MediaRoute2Info{ ")
+                .append("id=").append(getId())
+                .append(", name=").append(getName())
+                .append(", features=").append(getFeatures())
+                .append(", deviceType=").append(getDeviceType())
+                .append(", iconUri=").append(getIconUri())
+                .append(", description=").append(getDescription())
+                .append(", connectionState=").append(getConnectionState())
+                .append(", clientPackageName=").append(getClientPackageName())
+                .append(", volumeHandling=").append(getVolumeHandling())
+                .append(", volumeMax=").append(getVolumeMax())
+                .append(", volume=").append(getVolume())
+                .append(", providerId=").append(getProviderId())
+                .append(" }");
+        return result.toString();
     }
 
     @Override
@@ -389,36 +411,18 @@
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(mId);
-        dest.writeString(mProviderId);
         TextUtils.writeToParcel(mName, dest, flags);
+        dest.writeStringList(mFeatures);
+        dest.writeInt(mDeviceType);
+        dest.writeParcelable(mIconUri, flags);
         TextUtils.writeToParcel(mDescription, dest, flags);
         dest.writeInt(mConnectionState);
-        dest.writeParcelable(mIconUri, flags);
         dest.writeString(mClientPackageName);
-        dest.writeStringList(mFeatures);
-        dest.writeInt(mVolume);
-        dest.writeInt(mVolumeMax);
         dest.writeInt(mVolumeHandling);
-        dest.writeInt(mDeviceType);
+        dest.writeInt(mVolumeMax);
+        dest.writeInt(mVolume);
         dest.writeBundle(mExtras);
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder result = new StringBuilder()
-                .append("MediaRouteInfo{ ")
-                .append("id=").append(getId())
-                .append(", name=").append(getName())
-                .append(", description=").append(getDescription())
-                .append(", connectionState=").append(getConnectionState())
-                .append(", iconUri=").append(getIconUri())
-                .append(", volume=").append(getVolume())
-                .append(", volumeMax=").append(getVolumeMax())
-                .append(", volumeHandling=").append(getVolumeHandling())
-                .append(", deviceType=").append(getDeviceType())
-                .append(", providerId=").append(getProviderId())
-                .append(" }");
-        return result.toString();
+        dest.writeString(mProviderId);
     }
 
     /**
@@ -426,20 +430,21 @@
      */
     public static final class Builder {
         final String mId;
-        String mProviderId;
         final CharSequence mName;
+        final List<String> mFeatures;
+
+        @DeviceType
+        int mDeviceType = DEVICE_TYPE_UNKNOWN;
+        Uri mIconUri;
         CharSequence mDescription;
         @ConnectionState
         int mConnectionState;
-        Uri mIconUri;
         String mClientPackageName;
-        List<String> mFeatures;
-        int mVolume;
-        int mVolumeMax;
         int mVolumeHandling = PLAYBACK_VOLUME_FIXED;
-        @DeviceType
-        int mDeviceType = DEVICE_TYPE_UNKNOWN;
+        int mVolumeMax;
+        int mVolume;
         Bundle mExtras;
+        String mProviderId;
 
         /**
          * Constructor for builder to create {@link MediaRoute2Info}.
@@ -448,8 +453,8 @@
          * obtained from {@link MediaRouter2} can be different from what was set in
          * {@link MediaRoute2ProviderService}.
          * </p>
-         * @param id
-         * @param name
+         * @param id The ID of the route. Must not be empty.
+         * @param name The user-visible name of the route.
          */
         public Builder(@NonNull String id, @NonNull CharSequence name) {
             if (TextUtils.isEmpty(id)) {
@@ -463,40 +468,91 @@
             mFeatures = new ArrayList<>();
         }
 
+        /**
+         * Constructor for builder to create {@link MediaRoute2Info} with
+         * existing {@link MediaRoute2Info} instance.
+         *
+         * @param routeInfo the existing instance to copy data from.
+         */
         public Builder(@NonNull MediaRoute2Info routeInfo) {
-            if (routeInfo == null) {
-                throw new IllegalArgumentException("route info must not be null");
-            }
+            Objects.requireNonNull(routeInfo, "routeInfo must not be null");
+
             mId = routeInfo.mId;
             mName = routeInfo.mName;
-
-            if (!TextUtils.isEmpty(routeInfo.mProviderId)) {
-                setProviderId(routeInfo.mProviderId);
-            }
+            mFeatures = new ArrayList<>(routeInfo.mFeatures);
+            mDeviceType = routeInfo.mDeviceType;
+            mIconUri = routeInfo.mIconUri;
             mDescription = routeInfo.mDescription;
             mConnectionState = routeInfo.mConnectionState;
-            mIconUri = routeInfo.mIconUri;
-            setClientPackageName(routeInfo.mClientPackageName);
-            mFeatures = new ArrayList<>(routeInfo.mFeatures);
-            setVolume(routeInfo.mVolume);
-            setVolumeMax(routeInfo.mVolumeMax);
-            setVolumeHandling(routeInfo.mVolumeHandling);
-            setDeviceType(routeInfo.mDeviceType);
+            mClientPackageName = routeInfo.mClientPackageName;
+            mVolumeHandling = routeInfo.mVolumeHandling;
+            mVolumeMax = routeInfo.mVolumeMax;
+            mVolume = routeInfo.mVolume;
             if (routeInfo.mExtras != null) {
                 mExtras = new Bundle(routeInfo.mExtras);
             }
+            mProviderId = routeInfo.mProviderId;
         }
 
         /**
-         * Sets the provider id of the route.
-         * @hide
+         * Adds a feature for the route.
          */
         @NonNull
-        public Builder setProviderId(@NonNull String providerId) {
-            if (TextUtils.isEmpty(providerId)) {
-                throw new IllegalArgumentException("providerId must not be null or empty");
+        public Builder addFeature(@NonNull String feature) {
+            if (TextUtils.isEmpty(feature)) {
+                throw new IllegalArgumentException("feature must not be null or empty");
             }
-            mProviderId = providerId;
+            mFeatures.add(feature);
+            return this;
+        }
+
+        /**
+         * Adds features for the route. A route must support at least one route type.
+         */
+        @NonNull
+        public Builder addFeatures(@NonNull Collection<String> features) {
+            Objects.requireNonNull(features, "features must not be null");
+            for (String feature : features) {
+                addFeature(feature);
+            }
+            return this;
+        }
+
+        /**
+         * Clears the features of the route. A route must support at least one route type.
+         */
+        @NonNull
+        public Builder clearFeatures() {
+            mFeatures.clear();
+            return this;
+        }
+
+        /**
+         * Sets the route's device type.
+         */
+        @NonNull
+        public Builder setDeviceType(@DeviceType int deviceType) {
+            mDeviceType = deviceType;
+            return this;
+        }
+
+        /**
+         * Sets the URI of the icon representing this route.
+         * <p>
+         * This icon will be used in picker UIs if available.
+         * </p><p>
+         * The URI must be one of the following formats:
+         * <ul>
+         * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+         * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
+         * </li>
+         * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+         * </ul>
+         * </p>
+         */
+        @NonNull
+        public Builder setIconUri(@Nullable Uri iconUri) {
+            mIconUri = iconUri;
             return this;
         }
 
@@ -523,26 +579,6 @@
         }
 
         /**
-         * Sets the URI of the icon representing this route.
-         * <p>
-         * This icon will be used in picker UIs if available.
-         * </p><p>
-         * The URI must be one of the following formats:
-         * <ul>
-         * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
-         * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
-         * </li>
-         * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
-         * </ul>
-         * </p>
-         */
-        @NonNull
-        public Builder setIconUri(@Nullable Uri iconUri) {
-            mIconUri = iconUri;
-            return this;
-        }
-
-        /**
          * Sets the package name of the app using the route.
          */
         @NonNull
@@ -552,57 +588,6 @@
         }
 
         /**
-         * Clears the features of the route.
-         */
-        @NonNull
-        public Builder clearFeatures() {
-            mFeatures = new ArrayList<>();
-            return this;
-        }
-
-        /**
-         * Adds features for the route.
-         */
-        @NonNull
-        public Builder addFeatures(@NonNull Collection<String> features) {
-            Objects.requireNonNull(features, "features must not be null");
-            for (String feature : features) {
-                addFeature(feature);
-            }
-            return this;
-        }
-
-        /**
-         * Adds a feature for the route.
-         */
-        @NonNull
-        public Builder addFeature(@NonNull String feature) {
-            if (TextUtils.isEmpty(feature)) {
-                throw new IllegalArgumentException("feature must not be null or empty");
-            }
-            mFeatures.add(feature);
-            return this;
-        }
-
-        /**
-         * Sets the route's current volume, or 0 if unknown.
-         */
-        @NonNull
-        public Builder setVolume(int volume) {
-            mVolume = volume;
-            return this;
-        }
-
-        /**
-         * Sets the route's maximum volume, or 0 if unknown.
-         */
-        @NonNull
-        public Builder setVolumeMax(int volumeMax) {
-            mVolumeMax = volumeMax;
-            return this;
-        }
-
-        /**
          * Sets the route's volume handling.
          */
         @NonNull
@@ -612,28 +597,61 @@
         }
 
         /**
-         * Sets the route's device type.
+         * Sets the route's maximum volume, or 0 if unknown.
          */
         @NonNull
-        public Builder setDeviceType(@DeviceType int deviceType) {
-            mDeviceType = deviceType;
+        public Builder setVolumeMax(int volumeMax) {
+            mVolumeMax = volumeMax;
+            return this;
+        }
+
+        /**
+         * Sets the route's current volume, or 0 if unknown.
+         */
+        @NonNull
+        public Builder setVolume(int volume) {
+            mVolume = volume;
             return this;
         }
 
         /**
          * Sets a bundle of extras for the route.
+         * <p>
+         * Note: The extras will not affect the result of {@link MediaRoute2Info#equals(Object)}.
          */
         @NonNull
         public Builder setExtras(@Nullable Bundle extras) {
+            if (extras == null) {
+                mExtras = null;
+                return this;
+            }
             mExtras = new Bundle(extras);
             return this;
         }
 
         /**
+         * Sets the provider id of the route.
+         * @hide
+         */
+        @NonNull
+        public Builder setProviderId(@NonNull String providerId) {
+            if (TextUtils.isEmpty(providerId)) {
+                throw new IllegalArgumentException("providerId must not be null or empty");
+            }
+            mProviderId = providerId;
+            return this;
+        }
+
+        /**
          * Builds the {@link MediaRoute2Info media route info}.
+         *
+         * @throws IllegalArgumentException if no features are added.
          */
         @NonNull
         public MediaRoute2Info build() {
+            if (mFeatures.isEmpty()) {
+                throw new IllegalArgumentException("features must not be empty!");
+            }
             return new MediaRoute2Info(this);
         }
     }
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 6d9aea5..5a3de6d 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -161,8 +161,8 @@
      * @param sessionInfo information of the new session.
      *                    The {@link RoutingSessionInfo#getId() id} of the session must be unique.
      * @param requestId id of the previous request to create this session provided in
-     *                  {@link #onCreateSession(String, String, String, long)}
-     * @see #onCreateSession(String, String, String, long)
+     *                  {@link #onCreateSession(String, String, long)}
+     * @see #onCreateSession(String, String, long)
      * @hide
      */
     public final void notifySessionCreated(@NonNull RoutingSessionInfo sessionInfo,
@@ -196,8 +196,8 @@
      * Notifies clients of that the session could not be created.
      *
      * @param requestId id of the previous request to create the session provided in
-     *                  {@link #onCreateSession(String, String, String, long)}.
-     * @see #onCreateSession(String, String, String, long)
+     *                  {@link #onCreateSession(String, String, long)}.
+     * @see #onCreateSession(String, String, long)
      * @hide
      */
     public final void notifySessionCreationFailed(long requestId) {
@@ -289,16 +289,15 @@
      *
      * @param packageName the package name of the application that selected the route
      * @param routeId the id of the route initially being connected
-     * @param routeFeature the route feature of the new session
      * @param requestId the id of this session creation request
      *
-     * @see RoutingSessionInfo.Builder#Builder(String, String, String)
+     * @see RoutingSessionInfo.Builder#Builder(String, String)
      * @see RoutingSessionInfo.Builder#addSelectedRoute(String)
      * @see RoutingSessionInfo.Builder#setControlHints(Bundle)
      * @hide
      */
     public abstract void onCreateSession(@NonNull String packageName, @NonNull String routeId,
-            @NonNull String routeFeature, long requestId);
+            long requestId);
 
     /**
      * Called when the session should be released. A client of the session or system can request
@@ -433,14 +432,12 @@
         }
 
         @Override
-        public void requestCreateSession(String packageName, String routeId,
-                String routeFeature, long requestId) {
+        public void requestCreateSession(String packageName, String routeId, long requestId) {
             if (!checkCallerisSystem()) {
                 return;
             }
             mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession,
-                    MediaRoute2ProviderService.this, packageName, routeId, routeFeature,
-                    requestId));
+                    MediaRoute2ProviderService.this, packageName, routeId, requestId));
         }
 
         @Override
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index bc4da10..971b08d 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -284,26 +284,20 @@
      * Requests the media route provider service to create a session with the given route.
      *
      * @param route the route you want to create a session with.
-     * @param routeFeature the route feature of the session. Should not be empty.
      *
      * @see SessionCallback#onSessionCreated
      * @see SessionCallback#onSessionCreationFailed
      * @hide
      */
     @NonNull
-    public void requestCreateSession(@NonNull MediaRoute2Info route,
-            @NonNull String routeFeature) {
+    public void requestCreateSession(@NonNull MediaRoute2Info route) {
         Objects.requireNonNull(route, "route must not be null");
-        if (TextUtils.isEmpty(routeFeature)) {
-            throw new IllegalArgumentException("routeFeature must not be empty");
-        }
         // TODO: Check the given route exists
-        // TODO: Check the route supports the given routeFeature
 
         final int requestId;
         requestId = mSessionCreationRequestCnt.getAndIncrement();
 
-        SessionCreationRequest request = new SessionCreationRequest(requestId, route, routeFeature);
+        SessionCreationRequest request = new SessionCreationRequest(requestId, route);
         mSessionCreationRequests.add(request);
 
         Client2 client;
@@ -312,7 +306,7 @@
         }
         if (client != null) {
             try {
-                mMediaRouterService.requestCreateSession(client, route, routeFeature, requestId);
+                mMediaRouterService.requestCreateSession(client, route, requestId);
             } catch (RemoteException ex) {
                 Log.e(TAG, "Unable to request to create session.", ex);
                 mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler,
@@ -468,27 +462,18 @@
             mSessionCreationRequests.remove(matchingRequest);
 
             MediaRoute2Info requestedRoute = matchingRequest.mRoute;
-            String requestedRouteFeature = matchingRequest.mRouteFeature;
 
             if (sessionInfo == null) {
                 // TODO: We may need to distinguish between failure and rejection.
                 //       One way can be introducing 'reason'.
-                notifySessionCreationFailed(requestedRoute, requestedRouteFeature);
-                return;
-            } else if (!TextUtils.equals(requestedRouteFeature,
-                    sessionInfo.getRouteFeature())) {
-                Log.w(TAG, "The session has different route feature from what we requested. "
-                        + "(requested=" + requestedRouteFeature
-                        + ", actual=" + sessionInfo.getRouteFeature()
-                        + ")");
-                notifySessionCreationFailed(requestedRoute, requestedRouteFeature);
+                notifySessionCreationFailed(requestedRoute);
                 return;
             } else if (!sessionInfo.getSelectedRoutes().contains(requestedRoute.getId())) {
                 Log.w(TAG, "The session does not contain the requested route. "
                         + "(requestedRouteId=" + requestedRoute.getId()
                         + ", actualRoutes=" + sessionInfo.getSelectedRoutes()
                         + ")");
-                notifySessionCreationFailed(requestedRoute, requestedRouteFeature);
+                notifySessionCreationFailed(requestedRoute);
                 return;
             } else if (!TextUtils.equals(requestedRoute.getProviderId(),
                     sessionInfo.getProviderId())) {
@@ -496,7 +481,7 @@
                         + "(requested route's providerId=" + requestedRoute.getProviderId()
                         + ", actual providerId=" + sessionInfo.getProviderId()
                         + ")");
-                notifySessionCreationFailed(requestedRoute, requestedRouteFeature);
+                notifySessionCreationFailed(requestedRoute);
                 return;
             }
         }
@@ -617,10 +602,10 @@
         }
     }
 
-    private void notifySessionCreationFailed(MediaRoute2Info route, String routeFeature) {
+    private void notifySessionCreationFailed(MediaRoute2Info route) {
         for (SessionCallbackRecord record: mSessionCallbackRecords) {
             record.mExecutor.execute(
-                    () -> record.mSessionCallback.onSessionCreationFailed(route, routeFeature));
+                    () -> record.mSessionCallback.onSessionCreationFailed(route));
         }
     }
 
@@ -688,10 +673,8 @@
          * Called when the session creation request failed.
          *
          * @param requestedRoute the route info which was used for the request
-         * @param requestedRouteFeature the route feature which was used for the request
          */
-        public void onSessionCreationFailed(@NonNull MediaRoute2Info requestedRoute,
-                @NonNull String requestedRouteFeature) {}
+        public void onSessionCreationFailed(@NonNull MediaRoute2Info requestedRoute) {}
 
         /**
          * Called when the session info has changed.
@@ -753,16 +736,6 @@
         }
 
         /**
-         * @return the feature which is used by the session mainly.
-         */
-        @NonNull
-        public String getRouteFeature() {
-            synchronized (mControllerLock) {
-                return mSessionInfo.getRouteFeature();
-            }
-        }
-
-        /**
          * @return the control hints used to control routing session if available.
          */
         @Nullable
@@ -1012,7 +985,6 @@
             StringBuilder result = new StringBuilder()
                     .append("RoutingController{ ")
                     .append("sessionId=").append(getSessionId())
-                    .append(", routeFeature=").append(getRouteFeature())
                     .append(", selectedRoutes={")
                     .append(selectedRoutes)
                     .append("}")
@@ -1122,13 +1094,10 @@
 
     final class SessionCreationRequest {
         public final MediaRoute2Info mRoute;
-        public final String mRouteFeature;
         public final int mRequestId;
 
-        SessionCreationRequest(int requestId, @NonNull MediaRoute2Info route,
-                @NonNull String routeFeature) {
+        SessionCreationRequest(int requestId, @NonNull MediaRoute2Info route) {
             mRoute = route;
-            mRouteFeature = routeFeature;
             mRequestId = requestId;
         }
     }
diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
index 96acf6c..228adde 100644
--- a/media/java/android/media/RoutingSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -51,7 +51,6 @@
 
     final String mId;
     final String mClientPackageName;
-    final String mRouteFeature;
     @Nullable
     final String mProviderId;
     final List<String> mSelectedRoutes;
@@ -66,7 +65,6 @@
 
         mId = builder.mId;
         mClientPackageName = builder.mClientPackageName;
-        mRouteFeature = builder.mRouteFeature;
         mProviderId = builder.mProviderId;
 
         // TODO: Needs to check that the routes already have unique IDs.
@@ -87,7 +85,6 @@
 
         mId = ensureString(src.readString());
         mClientPackageName = ensureString(src.readString());
-        mRouteFeature = ensureString(src.readString());
         mProviderId = src.readString();
 
         mSelectedRoutes = ensureList(src.createStringArrayList());
@@ -119,7 +116,7 @@
      * In order to ensure uniqueness in {@link MediaRouter2} side, the value of this method
      * can be different from what was set in {@link MediaRoute2ProviderService}.
      *
-     * @see Builder#Builder(String, String, String)
+     * @see Builder#Builder(String, String)
      */
     @NonNull
     public String getId() {
@@ -131,7 +128,7 @@
     }
 
     /**
-     * Gets the original id set by {@link Builder#Builder(String, String, String)}.
+     * Gets the original id set by {@link Builder#Builder(String, String)}.
      * @hide
      */
     @NonNull
@@ -148,15 +145,6 @@
     }
 
     /**
-     * Gets the route feature of the session.
-     * Routes that don't have the feature can't be selected into the session.
-     */
-    @NonNull
-    public String getRouteFeature() {
-        return mRouteFeature;
-    }
-
-    /**
      * Gets the provider id of the session.
      * @hide
      */
@@ -214,7 +202,6 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(mId);
         dest.writeString(mClientPackageName);
-        dest.writeString(mRouteFeature);
         dest.writeString(mProviderId);
         dest.writeStringList(mSelectedRoutes);
         dest.writeStringList(mSelectableRoutes);
@@ -235,7 +222,6 @@
         RoutingSessionInfo other = (RoutingSessionInfo) obj;
         return Objects.equals(mId, other.mId)
                 && Objects.equals(mClientPackageName, other.mClientPackageName)
-                && Objects.equals(mRouteFeature, other.mRouteFeature)
                 && Objects.equals(mProviderId, other.mProviderId)
                 && Objects.equals(mSelectedRoutes, other.mSelectedRoutes)
                 && Objects.equals(mSelectableRoutes, other.mSelectableRoutes)
@@ -245,7 +231,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mId, mClientPackageName, mRouteFeature, mProviderId,
+        return Objects.hash(mId, mClientPackageName, mProviderId,
                 mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferrableRoutes);
     }
 
@@ -254,7 +240,6 @@
         StringBuilder result = new StringBuilder()
                 .append("RoutingSessionInfo{ ")
                 .append("sessionId=").append(mId)
-                .append(", routeFeature=").append(mRouteFeature)
                 .append(", selectedRoutes={")
                 .append(String.join(",", mSelectedRoutes))
                 .append("}")
@@ -295,7 +280,6 @@
     public static final class Builder {
         final String mId;
         final String mClientPackageName;
-        final String mRouteFeature;
         String mProviderId;
         final List<String> mSelectedRoutes;
         final List<String> mSelectableRoutes;
@@ -314,22 +298,16 @@
          * @param id ID of the session. Must not be empty.
          * @param clientPackageName package name of the client app which uses this session.
          *                          If is is unknown, then just use an empty string.
-         * @param routeFeature the route feature of session. Must not be empty.
          * @see MediaRoute2Info#getId()
          */
-        public Builder(@NonNull String id, @NonNull String clientPackageName,
-                @NonNull String routeFeature) {
+        public Builder(@NonNull String id, @NonNull String clientPackageName) {
             if (TextUtils.isEmpty(id)) {
                 throw new IllegalArgumentException("id must not be empty");
             }
             Objects.requireNonNull(clientPackageName, "clientPackageName must not be null");
-            if (TextUtils.isEmpty(routeFeature)) {
-                throw new IllegalArgumentException("routeFeature must not be empty");
-            }
 
             mId = id;
             mClientPackageName = clientPackageName;
-            mRouteFeature = routeFeature;
             mSelectedRoutes = new ArrayList<>();
             mSelectableRoutes = new ArrayList<>();
             mDeselectableRoutes = new ArrayList<>();
@@ -347,7 +325,6 @@
 
             mId = sessionInfo.mId;
             mClientPackageName = sessionInfo.mClientPackageName;
-            mRouteFeature = sessionInfo.mRouteFeature;
             mProviderId = sessionInfo.mProviderId;
 
             mSelectedRoutes = new ArrayList<>(sessionInfo.mSelectedRoutes);
diff --git a/media/java/android/media/tv/tuner/Descrambler.java b/media/java/android/media/tv/tuner/Descrambler.java
index 0143582..23016e9 100644
--- a/media/java/android/media/tv/tuner/Descrambler.java
+++ b/media/java/android/media/tv/tuner/Descrambler.java
@@ -34,7 +34,7 @@
  */
 public class Descrambler implements AutoCloseable {
     /** @hide */
-    @IntDef(prefix = "PID_TYPE_", value = {PID_TYPE_T, PID_TYPE_MMPT})
+    @IntDef(prefix = "PID_TYPE_", value = {PID_TYPE_T, PID_TYPE_MMTP})
     @Retention(RetentionPolicy.SOURCE)
     public @interface PidType {}
 
@@ -45,7 +45,7 @@
     /**
      * Packet ID is used to specify packets in MMTP.
      */
-    public static final int PID_TYPE_MMPT = 2;
+    public static final int PID_TYPE_MMTP = 2;
 
 
     private long mNativeContext;
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index 8e579bf..a9a15d9 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -40,7 +40,8 @@
 @SystemApi
 public class Lnb implements AutoCloseable {
     /** @hide */
-    @IntDef({VOLTAGE_NONE, VOLTAGE_5V, VOLTAGE_11V, VOLTAGE_12V, VOLTAGE_13V, VOLTAGE_14V,
+    @IntDef(prefix = "VOLTAGE_",
+            value = {VOLTAGE_NONE, VOLTAGE_5V, VOLTAGE_11V, VOLTAGE_12V, VOLTAGE_13V, VOLTAGE_14V,
             VOLTAGE_15V, VOLTAGE_18V, VOLTAGE_19V})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Voltage {}
@@ -83,7 +84,8 @@
     public static final int VOLTAGE_19V = Constants.LnbVoltage.VOLTAGE_19V;
 
     /** @hide */
-    @IntDef({TONE_NONE, TONE_CONTINUOUS})
+    @IntDef(prefix = "TONE_",
+            value = {TONE_NONE, TONE_CONTINUOUS})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Tone {}
 
@@ -97,7 +99,8 @@
     public static final int TONE_CONTINUOUS = Constants.LnbTone.CONTINUOUS;
 
     /** @hide */
-    @IntDef({POSITION_UNDEFINED, POSITION_A, POSITION_B})
+    @IntDef(prefix = "POSITION_",
+            value = {POSITION_UNDEFINED, POSITION_A, POSITION_B})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Position {}
 
@@ -114,6 +117,37 @@
      */
     public static final int POSITION_B = Constants.LnbPosition.POSITION_B;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "EVENT_TYPE_",
+            value = {EVENT_TYPE_DISEQC_RX_OVERFLOW, EVENT_TYPE_DISEQC_RX_TIMEOUT,
+            EVENT_TYPE_DISEQC_RX_PARITY_ERROR, EVENT_TYPE_LNB_OVERLOAD})
+    public @interface EventType {}
+
+    /**
+     * Outgoing Diseqc message overflow.
+     * @hide
+     */
+    public static final int EVENT_TYPE_DISEQC_RX_OVERFLOW =
+            Constants.LnbEventType.DISEQC_RX_OVERFLOW;
+    /**
+     * Outgoing Diseqc message isn't delivered on time.
+     * @hide
+     */
+    public static final int EVENT_TYPE_DISEQC_RX_TIMEOUT =
+            Constants.LnbEventType.DISEQC_RX_TIMEOUT;
+    /**
+     * Incoming Diseqc message has parity error.
+     * @hide
+     */
+    public static final int EVENT_TYPE_DISEQC_RX_PARITY_ERROR =
+            Constants.LnbEventType.DISEQC_RX_PARITY_ERROR;
+    /**
+     * LNB is overload.
+     * @hide
+     */
+    public static final int EVENT_TYPE_LNB_OVERLOAD = Constants.LnbEventType.LNB_OVERLOAD;
+
     int mId;
     LnbCallback mCallback;
     Context mContext;
diff --git a/media/java/android/media/tv/tuner/LnbCallback.java b/media/java/android/media/tv/tuner/LnbCallback.java
index 99bbf86..5155f60 100644
--- a/media/java/android/media/tv/tuner/LnbCallback.java
+++ b/media/java/android/media/tv/tuner/LnbCallback.java
@@ -17,6 +17,8 @@
 package android.media.tv.tuner;
 
 
+import android.media.tv.tuner.Lnb.EventType;
+
 /**
  * Callback interface for receiving information from LNBs.
  *
@@ -26,7 +28,7 @@
     /**
      * Invoked when there is a LNB event.
      */
-    void onEvent(int lnbEventType);
+    void onEvent(@EventType int lnbEventType);
 
     /**
      * Invoked when there is a new DiSEqC message.
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 6f5ba84..3a8ae92 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -16,6 +16,7 @@
 
 package android.media.tv.tuner;
 
+import android.annotation.BytesLong;
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -23,11 +24,13 @@
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.media.tv.tuner.TunerConstants.FilterStatus;
-import android.media.tv.tuner.TunerConstants.FilterSubtype;
 import android.media.tv.tuner.TunerConstants.FrontendScanType;
 import android.media.tv.tuner.TunerConstants.Result;
 import android.media.tv.tuner.dvr.Dvr;
-import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
+import android.media.tv.tuner.dvr.DvrCallback;
+import android.media.tv.tuner.dvr.DvrSettings;
+import android.media.tv.tuner.filter.Filter.Subtype;
+import android.media.tv.tuner.filter.Filter.Type;
 import android.media.tv.tuner.filter.FilterEvent;
 import android.media.tv.tuner.filter.TimeFilter;
 import android.media.tv.tuner.frontend.FrontendCallback;
@@ -88,6 +91,33 @@
         nativeSetup();
     }
 
+    /**
+     * Constructs a Tuner instance.
+     *
+     * @param context the context of the caller.
+     * @param tvInputSessionId the session ID of the TV input.
+     * @param useCase the use case of this Tuner instance.
+     *
+     * @hide
+     * TODO: replace the other constructor
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    public Tuner(@NonNull Context context, @NonNull String tvInputSessionId, int useCase,
+            @Nullable OnResourceLostListener listener) {
+        mContext = context;
+    }
+
+    /**
+     * Shares the frontend resource with another Tuner instance
+     *
+     * @param tuner the Tuner instance to share frontend resource with.
+     *
+     * @hide
+     */
+    public void shareFrontendFromTuner(@NonNull Tuner tuner) {
+    }
+
+
     private long mNativeContext; // used by native jMediaTuner
 
     /** @hide */
@@ -119,13 +149,13 @@
     private native int nativeStopScan();
     private native int nativeSetLnb(int lnbId);
     private native int nativeSetLna(boolean enable);
-    private native FrontendStatus[] nativeGetFrontendStatus(int[] statusTypes);
+    private native FrontendStatus nativeGetFrontendStatus(int[] statusTypes);
     private native int nativeGetAvSyncHwId(Filter filter);
     private native long nativeGetAvSyncTime(int avSyncId);
     private native int nativeConnectCiCam(int ciCamId);
     private native int nativeDisconnectCiCam();
     private native FrontendInfo nativeGetFrontendInfo(int id);
-    private native Filter nativeOpenFilter(int type, int subType, int bufferSize);
+    private native Filter nativeOpenFilter(int type, int subType, long bufferSize);
     private native TimeFilter nativeOpenTimeFilter();
 
     private native List<Integer> nativeGetLnbIds();
@@ -133,7 +163,7 @@
 
     private native Descrambler nativeOpenDescrambler();
 
-    private native Dvr nativeOpenDvr(int type, int bufferSize);
+    private native Dvr nativeOpenDvr(int type, long bufferSize);
 
     private static native DemuxCapabilities nativeGetDemuxCapabilities();
 
@@ -159,6 +189,21 @@
         void onFilterStatusChanged(@NonNull Filter filter, @FilterStatus int status);
     }
 
+
+    /**
+     * Listener for resource lost.
+     *
+     * @hide
+     */
+    public interface OnResourceLostListener {
+        /**
+         * Invoked when resource lost.
+         *
+         * @param tuner the tuner instance whose resource is being reclaimed.
+         */
+        void onResourceLost(@NonNull Tuner tuner);
+    }
+
     @Nullable
     private EventHandler createEventHandler() {
         Looper looper;
@@ -221,23 +266,32 @@
     /**
      * Stops a previous tuning.
      *
-     * If the method completes successfully the frontend is no longer tuned and no data
+     * <p>If the method completes successfully, the frontend is no longer tuned and no data
      * will be sent to attached filters.
      *
      * @return result status of the operation.
+     *
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @Result
     public int stopTune() {
+        TunerUtils.checkTunerPermission(mContext);
         return nativeStopTune();
     }
 
     /**
      * Scan channels.
+     *
+     * @param settings A {@link FrontendSettings} to configure the frontend.
+     * @param scanType The scan type.
+     *
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     public int scan(@NonNull FrontendSettings settings, @FrontendScanType int scanType,
             @NonNull @CallbackExecutor Executor executor, @NonNull ScanCallback scanCallback) {
+        TunerUtils.checkTunerPermission(mContext);
         mScanCallback = scanCallback;
         mScanCallbackExecutor = executor;
         return nativeScan(settings.getType(), settings, scanType);
@@ -255,6 +309,7 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @Result
     public int stopScan() {
         TunerUtils.checkTunerPermission(mContext);
         int retVal = nativeStopScan();
@@ -266,42 +321,49 @@
     /**
      * Sets Low-Noise Block downconverter (LNB) for satellite frontend.
      *
-     * This assigns a hardware LNB resource to the satellite tuner. It can be
+     * <p>This assigns a hardware LNB resource to the satellite tuner. It can be
      * called multiple times to update LNB assignment.
      *
      * @param lnb the LNB instance.
      *
      * @return result status of the operation.
+     *
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @Result
     public int setLnb(@NonNull Lnb lnb) {
+        TunerUtils.checkTunerPermission(mContext);
         return nativeSetLnb(lnb.mId);
     }
 
     /**
      * Enable or Disable Low Noise Amplifier (LNA).
      *
-     * @param enable true to activate LNA module; false to deactivate LNA
+     * @param enable {@code true} to activate LNA module; {@code false} to deactivate LNA.
      *
      * @return result status of the operation.
+     *
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @Result
     public int setLna(boolean enable) {
+        TunerUtils.checkTunerPermission(mContext);
         return nativeSetLna(enable);
     }
 
     /**
      * Gets the statuses of the frontend.
      *
-     * This retrieve the statuses of the frontend for given status types.
+     * <p>This retrieve the statuses of the frontend for given status types.
      *
-     * @param statusTypes an array of status type which the caller request.
-     *
-     * @return statuses an array of statuses which response the caller's
-     *         request.
+     * @param statusTypes an array of status types which the caller requests.
+     * @return statuses which response the caller's requests.
      * @hide
      */
-    public FrontendStatus[] getFrontendStatus(int[] statusTypes) {
+    @Nullable
+    public FrontendStatus getFrontendStatus(int[] statusTypes) {
         return nativeGetFrontendStatus(statusTypes);
     }
 
@@ -310,59 +372,77 @@
      *
      * @param filter the filter instance for the hardware sync ID.
      * @return the id of hardware A/V sync.
+     *
      * @hide
      */
-    public int getAvSyncHwId(Filter filter) {
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    public int getAvSyncHwId(@NonNull Filter filter) {
+        TunerUtils.checkTunerPermission(mContext);
         return nativeGetAvSyncHwId(filter);
     }
+
     /**
-     * Gets the current timestamp for A/V sync
+     * Gets the current timestamp for Audio/Video sync
      *
-     * The timestamp is maintained by hardware. The timestamp based on 90KHz, and it's format is the
-     * same as PTS (Presentation Time Stamp).
+     * <p>The timestamp is maintained by hardware. The timestamp based on 90KHz, and it's format is
+     * the same as PTS (Presentation Time Stamp).
      *
      * @param avSyncHwId the hardware id of A/V sync.
      * @return the current timestamp of hardware A/V sync.
+     *
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     public long getAvSyncTime(int avSyncHwId) {
+        TunerUtils.checkTunerPermission(mContext);
         return nativeGetAvSyncTime(avSyncHwId);
     }
 
-
     /**
      * Connects Conditional Access Modules (CAM) through Common Interface (CI)
      *
-     * The demux uses the output from the frontend as the input by default, and must change to use
-     * the output from CI-CAM as the input after this call.
+     * <p>The demux uses the output from the frontend as the input by default, and must change to
+     * use the output from CI-CAM as the input after this call.
      *
      * @param ciCamId specify CI-CAM Id to connect.
      * @return result status of the operation.
+     *
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     @Result
     public int connectCiCam(int ciCamId) {
+        TunerUtils.checkTunerPermission(mContext);
         return nativeConnectCiCam(ciCamId);
     }
 
     /**
      * Disconnects Conditional Access Modules (CAM)
      *
-     * The demux will use the output from the frontend as the input after this call.
+     * <p>The demux will use the output from the frontend as the input after this call.
      *
      * @return result status of the operation.
+     *
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     @Result
     public int disconnectCiCam() {
+        TunerUtils.checkTunerPermission(mContext);
         return nativeDisconnectCiCam();
     }
 
     /**
-     * Retrieve the frontend information.
+     * Gets the frontend information.
+     *
+     * @return The frontend information. {@code null} if the operation failed.
+     *
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @Nullable
     public FrontendInfo getFrontendInfo() {
+        TunerUtils.checkTunerPermission(mContext);
         if (mFrontend == null) {
             throw new IllegalStateException("frontend is not initialized");
         }
@@ -370,10 +450,13 @@
     }
 
     /**
-     * Gets frontend ID.
+     * Gets the frontend ID.
+     *
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     public int getFrontendId() {
+        TunerUtils.checkTunerPermission(mContext);
         if (mFrontend == null) {
             throw new IllegalStateException("frontend is not initialized");
         }
@@ -382,6 +465,7 @@
 
     /**
      * Gets Demux capabilities.
+     *
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@@ -424,8 +508,26 @@
         private Filter() {}
     }
 
-    private Filter openFilter(@FilterType int mainType, @FilterSubtype int subType, int bufferSize,
-            FilterCallback cb) {
+    /**
+     * Opens a filter object based on the given types and buffer size.
+     *
+     * @param mainType the main type of the filter.
+     * @param subType the subtype of the filter.
+     * @param bufferSize the buffer size of the filter to be opened in bytes. The buffer holds the
+     * data output from the filter.
+     * @param executor the executor on which callback will be invoked. The default event handler
+     * executor is used if it's {@code null}.
+     * @param cb the callback to receive notifications from filter.
+     * @return the opened filter. {@code null} if the operation failed.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @Nullable
+    public Filter openFilter(@Type int mainType, @Subtype int subType,
+            @BytesLong long bufferSize, @CallbackExecutor @Nullable Executor executor,
+            @Nullable FilterCallback cb) {
+        TunerUtils.checkTunerPermission(mContext);
         Filter filter = nativeOpenFilter(
                 mainType, TunerUtils.getFilterSubtype(mainType, subType), bufferSize);
         if (filter != null) {
@@ -437,6 +539,42 @@
         return filter;
     }
 
+    /**
+     * Opens an LNB (low-noise block downconverter) object.
+     *
+     * @param executor the executor on which callback will be invoked. The default event handler
+     * executor is used if it's {@code null}.
+     * @param cb the callback to receive notifications from LNB.
+     * @return the opened LNB object. {@code null} if the operation failed.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @Nullable
+    public Lnb openLnb(@CallbackExecutor @Nullable Executor executor, LnbCallback cb) {
+        return openLnbByName(null, executor, cb);
+    }
+
+    /**
+     * Opens an LNB (low-noise block downconverter) object.
+     *
+     * @param name the LNB name.
+     * @param executor the executor on which callback will be invoked. The default event handler
+     * executor is used if it's {@code null}.
+     * @param cb the callback to receive notifications from LNB.
+     * @return the opened LNB object. {@code null} if the operation failed.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @Nullable
+    public Lnb openLnbByName(@Nullable String name, @CallbackExecutor @Nullable Executor executor,
+            LnbCallback cb) {
+        TunerUtils.checkTunerPermission(mContext);
+        // TODO: use resource manager to get LNB ID.
+        return new Lnb(0);
+    }
+
     private List<Integer> getLnbIds() {
         mLnbIds = nativeGetLnbIds();
         return mLnbIds;
@@ -484,7 +622,24 @@
         return nativeOpenDescrambler();
     }
 
-    private Dvr openDvr(int type, int bufferSize) {
+    /**
+     * Open a DVR (Digital Video Record) instance.
+     *
+     * @param type the DVR type to be opened.
+     * @param bufferSize the buffer size of the output in bytes. It's used to hold output data of
+     * the attached filters.
+     * @param executor the executor on which callback will be invoked. The default event handler
+     * executor is used if it's {@code null}.
+     * @param cb the callback to receive notifications from DVR.
+     * @return the opened DVR object. {@code null} if the operation failed.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @Nullable
+    public Dvr openDvr(@DvrSettings.Type int type, @BytesLong long bufferSize,
+            @CallbackExecutor @Nullable Executor executor, DvrCallback cb) {
+        TunerUtils.checkTunerPermission(mContext);
         Dvr dvr = nativeOpenDvr(type, bufferSize);
         return dvr;
     }
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index 202b979..fe4c954 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -36,69 +36,42 @@
  */
 @SystemApi
 public final class TunerConstants {
-    /** @hide */
+    /**
+     * Invalid TS packet ID.
+     * @hide
+     */
     public static final int INVALID_TS_PID = Constants.Constant.INVALID_TS_PID;
-    /** @hide */
+    /**
+     * Invalid stream ID.
+     * @hide
+     */
     public static final int INVALID_STREAM_ID = Constants.Constant.INVALID_STREAM_ID;
 
 
     /** @hide */
-    @IntDef({FRONTEND_EVENT_TYPE_LOCKED, FRONTEND_EVENT_TYPE_NO_SIGNAL,
-            FRONTEND_EVENT_TYPE_LOST_LOCK})
+    @IntDef(prefix = "FRONTEND_EVENT_TYPE_",
+            value = {FRONTEND_EVENT_TYPE_LOCKED, FRONTEND_EVENT_TYPE_NO_SIGNAL,
+                    FRONTEND_EVENT_TYPE_LOST_LOCK})
     @Retention(RetentionPolicy.SOURCE)
     public @interface FrontendEventType {}
-    /** @hide */
+    /**
+     * Frontend locked.
+     * @hide
+     */
     public static final int FRONTEND_EVENT_TYPE_LOCKED = Constants.FrontendEventType.LOCKED;
-    /** @hide */
+    /**
+     * No signal detected.
+     * @hide
+     */
     public static final int FRONTEND_EVENT_TYPE_NO_SIGNAL = Constants.FrontendEventType.NO_SIGNAL;
-    /** @hide */
+    /**
+     * Frontend lock lost.
+     * @hide
+     */
     public static final int FRONTEND_EVENT_TYPE_LOST_LOCK = Constants.FrontendEventType.LOST_LOCK;
 
 
     /** @hide */
-    @IntDef({FILTER_SUBTYPE_UNDEFINED, FILTER_SUBTYPE_SECTION, FILTER_SUBTYPE_PES,
-            FILTER_SUBTYPE_AUDIO, FILTER_SUBTYPE_VIDEO, FILTER_SUBTYPE_DOWNLOAD,
-            FILTER_SUBTYPE_RECORD, FILTER_SUBTYPE_TS, FILTER_SUBTYPE_PCR, FILTER_SUBTYPE_TEMI,
-            FILTER_SUBTYPE_MMPT, FILTER_SUBTYPE_NTP, FILTER_SUBTYPE_IP_PAYLOAD, FILTER_SUBTYPE_IP,
-            FILTER_SUBTYPE_PAYLOAD_THROUGH, FILTER_SUBTYPE_TLV, FILTER_SUBTYPE_PTP, })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface FilterSubtype {}
-    /** @hide */
-    public static final int FILTER_SUBTYPE_UNDEFINED = 0;
-    /** @hide */
-    public static final int FILTER_SUBTYPE_SECTION = 1;
-    /** @hide */
-    public static final int FILTER_SUBTYPE_PES = 2;
-    /** @hide */
-    public static final int FILTER_SUBTYPE_AUDIO = 3;
-    /** @hide */
-    public static final int FILTER_SUBTYPE_VIDEO = 4;
-    /** @hide */
-    public static final int FILTER_SUBTYPE_DOWNLOAD = 5;
-    /** @hide */
-    public static final int FILTER_SUBTYPE_RECORD = 6;
-    /** @hide */
-    public static final int FILTER_SUBTYPE_TS = 7;
-    /** @hide */
-    public static final int FILTER_SUBTYPE_PCR = 8;
-    /** @hide */
-    public static final int FILTER_SUBTYPE_TEMI = 9;
-    /** @hide */
-    public static final int FILTER_SUBTYPE_MMPT = 10;
-    /** @hide */
-    public static final int FILTER_SUBTYPE_NTP = 11;
-    /** @hide */
-    public static final int FILTER_SUBTYPE_IP_PAYLOAD = 12;
-    /** @hide */
-    public static final int FILTER_SUBTYPE_IP = 13;
-    /** @hide */
-    public static final int FILTER_SUBTYPE_PAYLOAD_THROUGH = 14;
-    /** @hide */
-    public static final int FILTER_SUBTYPE_TLV = 15;
-    /** @hide */
-    public static final int FILTER_SUBTYPE_PTP = 16;
-
-    /** @hide */
     @IntDef(flag = true, prefix = "FILTER_STATUS_", value = {FILTER_STATUS_DATA_READY,
             FILTER_STATUS_LOW_WATER, FILTER_STATUS_HIGH_WATER, FILTER_STATUS_OVERFLOW})
     @Retention(RetentionPolicy.SOURCE)
@@ -129,93 +102,29 @@
      */
     public static final int FILTER_STATUS_OVERFLOW = Constants.DemuxFilterStatus.OVERFLOW;
 
-    /**
-     * Indexes can be tagged through TS (Transport Stream) header.
-     *
-     * @hide
-     */
-    @IntDef(flag = true, value = {TS_INDEX_FIRST_PACKET, TS_INDEX_PAYLOAD_UNIT_START_INDICATOR,
-            TS_INDEX_CHANGE_TO_NOT_SCRAMBLED, TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED,
-            TS_INDEX_CHANGE_TO_ODD_SCRAMBLED, TS_INDEX_DISCONTINUITY_INDICATOR,
-            TS_INDEX_RANDOM_ACCESS_INDICATOR, TS_INDEX_PRIORITY_INDICATOR, TS_INDEX_PCR_FLAG,
-            TS_INDEX_OPCR_FLAG, TS_INDEX_SPLICING_POINT_FLAG, TS_INDEX_PRIVATE_DATA,
-            TS_INDEX_ADAPTATION_EXTENSION_FLAG})
+
+    /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    public @interface TsIndex {}
+    @IntDef(prefix = "INDEX_TYPE_", value =
+            {INDEX_TYPE_NONE, INDEX_TYPE_SC, INDEX_TYPE_SC_HEVC})
+    public @interface ScIndexType {}
 
     /**
-     * TS index FIRST_PACKET.
+     * Start Code Index is not used.
      * @hide
      */
-    public static final int TS_INDEX_FIRST_PACKET = Constants.DemuxTsIndex.FIRST_PACKET;
+    public static final int INDEX_TYPE_NONE = Constants.DemuxRecordScIndexType.NONE;
     /**
-     * TS index PAYLOAD_UNIT_START_INDICATOR.
+     * Start Code index.
      * @hide
      */
-    public static final int TS_INDEX_PAYLOAD_UNIT_START_INDICATOR =
-            Constants.DemuxTsIndex.PAYLOAD_UNIT_START_INDICATOR;
+    public static final int INDEX_TYPE_SC = Constants.DemuxRecordScIndexType.SC;
     /**
-     * TS index CHANGE_TO_NOT_SCRAMBLED.
+     * Start Code index for HEVC.
      * @hide
      */
-    public static final int TS_INDEX_CHANGE_TO_NOT_SCRAMBLED =
-            Constants.DemuxTsIndex.CHANGE_TO_NOT_SCRAMBLED;
-    /**
-     * TS index CHANGE_TO_EVEN_SCRAMBLED.
-     * @hide
-     */
-    public static final int TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED =
-            Constants.DemuxTsIndex.CHANGE_TO_EVEN_SCRAMBLED;
-    /**
-     * TS index CHANGE_TO_ODD_SCRAMBLED.
-     * @hide
-     */
-    public static final int TS_INDEX_CHANGE_TO_ODD_SCRAMBLED =
-            Constants.DemuxTsIndex.CHANGE_TO_ODD_SCRAMBLED;
-    /**
-     * TS index DISCONTINUITY_INDICATOR.
-     * @hide
-     */
-    public static final int TS_INDEX_DISCONTINUITY_INDICATOR =
-            Constants.DemuxTsIndex.DISCONTINUITY_INDICATOR;
-    /**
-     * TS index RANDOM_ACCESS_INDICATOR.
-     * @hide
-     */
-    public static final int TS_INDEX_RANDOM_ACCESS_INDICATOR =
-            Constants.DemuxTsIndex.RANDOM_ACCESS_INDICATOR;
-    /**
-     * TS index PRIORITY_INDICATOR.
-     * @hide
-     */
-    public static final int TS_INDEX_PRIORITY_INDICATOR = Constants.DemuxTsIndex.PRIORITY_INDICATOR;
-    /**
-     * TS index PCR_FLAG.
-     * @hide
-     */
-    public static final int TS_INDEX_PCR_FLAG = Constants.DemuxTsIndex.PCR_FLAG;
-    /**
-     * TS index OPCR_FLAG.
-     * @hide
-     */
-    public static final int TS_INDEX_OPCR_FLAG = Constants.DemuxTsIndex.OPCR_FLAG;
-    /**
-     * TS index SPLICING_POINT_FLAG.
-     * @hide
-     */
-    public static final int TS_INDEX_SPLICING_POINT_FLAG =
-            Constants.DemuxTsIndex.SPLICING_POINT_FLAG;
-    /**
-     * TS index PRIVATE_DATA.
-     * @hide
-     */
-    public static final int TS_INDEX_PRIVATE_DATA = Constants.DemuxTsIndex.PRIVATE_DATA;
-    /**
-     * TS index ADAPTATION_EXTENSION_FLAG.
-     * @hide
-     */
-    public static final int TS_INDEX_ADAPTATION_EXTENSION_FLAG =
-            Constants.DemuxTsIndex.ADAPTATION_EXTENSION_FLAG;
+    public static final int INDEX_TYPE_SC_HEVC = Constants.DemuxRecordScIndexType.SC_HEVC;
+
 
     /**
      * Indexes can be tagged by Start Code in PES (Packetized Elementary Stream)
@@ -322,156 +231,6 @@
     public static final int FRONTEND_SCAN_BLIND = Constants.FrontendScanType.SCAN_BLIND;
 
 
-    /** @hide */
-    @IntDef({FRONTEND_STATUS_TYPE_DEMOD_LOCK, FRONTEND_STATUS_TYPE_SNR, FRONTEND_STATUS_TYPE_BER,
-            FRONTEND_STATUS_TYPE_PER, FRONTEND_STATUS_TYPE_PRE_BER,
-            FRONTEND_STATUS_TYPE_SIGNAL_QUALITY, FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH,
-            FRONTEND_STATUS_TYPE_SYMBOL_RATE, FRONTEND_STATUS_TYPE_FEC,
-            FRONTEND_STATUS_TYPE_MODULATION, FRONTEND_STATUS_TYPE_SPECTRAL,
-            FRONTEND_STATUS_TYPE_LNB_VOLTAGE, FRONTEND_STATUS_TYPE_PLP_ID,
-            FRONTEND_STATUS_TYPE_EWBS, FRONTEND_STATUS_TYPE_AGC, FRONTEND_STATUS_TYPE_LNA,
-            FRONTEND_STATUS_TYPE_LAYER_ERROR, FRONTEND_STATUS_TYPE_VBER_CN,
-            FRONTEND_STATUS_TYPE_LBER_CN, FRONTEND_STATUS_TYPE_XER_CN, FRONTEND_STATUS_TYPE_MER,
-            FRONTEND_STATUS_TYPE_FREQ_OFFSET, FRONTEND_STATUS_TYPE_HIERARCHY,
-            FRONTEND_STATUS_TYPE_RF_LOCK, FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface FrontendStatusType {}
-
-    /**
-     * Lock status for Demod.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_DEMOD_LOCK =
-            Constants.FrontendStatusType.DEMOD_LOCK;
-    /**
-     * Signal to Noise Ratio.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_SNR = Constants.FrontendStatusType.SNR;
-    /**
-     * Bit Error Ratio.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_BER = Constants.FrontendStatusType.BER;
-    /**
-     * Packages Error Ratio.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_PER = Constants.FrontendStatusType.PER;
-    /**
-     * Bit Error Ratio before FEC.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_PRE_BER = Constants.FrontendStatusType.PRE_BER;
-    /**
-     * Signal Quality (0..100). Good data over total data in percent can be
-     * used as a way to present Signal Quality.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_SIGNAL_QUALITY =
-            Constants.FrontendStatusType.SIGNAL_QUALITY;
-    /**
-     * Signal Strength.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH =
-            Constants.FrontendStatusType.SIGNAL_STRENGTH;
-    /**
-     * Symbol Rate.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE =
-            Constants.FrontendStatusType.SYMBOL_RATE;
-    /**
-     * Forward Error Correction Type.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_FEC = Constants.FrontendStatusType.FEC;
-    /**
-     * Modulation Type.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_MODULATION =
-            Constants.FrontendStatusType.MODULATION;
-    /**
-     * Spectral Inversion Type.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_SPECTRAL = Constants.FrontendStatusType.SPECTRAL;
-    /**
-     * LNB Voltage.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_LNB_VOLTAGE =
-            Constants.FrontendStatusType.LNB_VOLTAGE;
-    /**
-     * Physical Layer Pipe ID.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_PLP_ID = Constants.FrontendStatusType.PLP_ID;
-    /**
-     * Status for Emergency Warning Broadcasting System.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_EWBS = Constants.FrontendStatusType.EWBS;
-    /**
-     * Automatic Gain Control.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_AGC = Constants.FrontendStatusType.AGC;
-    /**
-     * Low Noise Amplifier.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_LNA = Constants.FrontendStatusType.LNA;
-    /**
-     * Error status by layer.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_LAYER_ERROR =
-            Constants.FrontendStatusType.LAYER_ERROR;
-    /**
-     * CN value by VBER.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_VBER_CN = Constants.FrontendStatusType.VBER_CN;
-    /**
-     * CN value by LBER.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_LBER_CN = Constants.FrontendStatusType.LBER_CN;
-    /**
-     * CN value by XER.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_XER_CN = Constants.FrontendStatusType.XER_CN;
-    /**
-     * Moduration Error Ratio.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_MER = Constants.FrontendStatusType.MER;
-    /**
-     * Difference between tuning frequency and actual locked frequency.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_FREQ_OFFSET =
-            Constants.FrontendStatusType.FREQ_OFFSET;
-    /**
-     * Hierarchy for DVBT.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_HIERARCHY = Constants.FrontendStatusType.HIERARCHY;
-    /**
-     * Lock status for RF.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_RF_LOCK = Constants.FrontendStatusType.RF_LOCK;
-    /**
-     * PLP information in a frequency band for ATSC3.0 frontend.
-     * @hide
-     */
-    public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO =
-            Constants.FrontendStatusType.ATSC3_PLP_INFO;
 
     /** @hide */
     @LongDef({FEC_UNDEFINED, FEC_AUTO, FEC_1_2, FEC_1_3, FEC_1_4, FEC_1_5, FEC_2_3, FEC_2_5,
@@ -716,23 +475,6 @@
 
 
     /** @hide */
-    @IntDef({FILTER_SETTINGS_TS, FILTER_SETTINGS_MMTP, FILTER_SETTINGS_IP, FILTER_SETTINGS_TLV,
-            FILTER_SETTINGS_ALP})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface FilterSettingsType {}
-    /** @hide */
-    public static final int FILTER_SETTINGS_TS = Constants.DemuxFilterMainType.TS;
-    /** @hide */
-    public static final int FILTER_SETTINGS_MMTP = Constants.DemuxFilterMainType.MMTP;
-    /** @hide */
-    public static final int FILTER_SETTINGS_IP = Constants.DemuxFilterMainType.IP;
-    /** @hide */
-    public static final int FILTER_SETTINGS_TLV = Constants.DemuxFilterMainType.TLV;
-    /** @hide */
-    public static final int FILTER_SETTINGS_ALP = Constants.DemuxFilterMainType.ALP;
-
-
-    /** @hide */
     @IntDef({RESULT_SUCCESS, RESULT_UNAVAILABLE, RESULT_NOT_INITIALIZED, RESULT_INVALID_STATE,
             RESULT_INVALID_ARGUMENT, RESULT_OUT_OF_MEMORY, RESULT_UNKNOWN_ERROR})
     @Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/tv/tuner/TunerUtils.java b/media/java/android/media/tv/tuner/TunerUtils.java
index 8780b72..30aaa02 100644
--- a/media/java/android/media/tv/tuner/TunerUtils.java
+++ b/media/java/android/media/tv/tuner/TunerUtils.java
@@ -19,9 +19,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.tv.tuner.V1_0.Constants;
-import android.media.tv.tuner.TunerConstants.FilterSubtype;
-import android.media.tv.tuner.filter.FilterConfiguration;
-import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
+import android.media.tv.tuner.filter.Filter;
 
 /**
  * Utility class for tuner framework.
@@ -50,91 +48,91 @@
      * @param mainType filter main type.
      * @param subtype filter subtype.
      */
-    public static int getFilterSubtype(@FilterType int mainType, @FilterSubtype int subtype) {
-        if (mainType == FilterConfiguration.FILTER_TYPE_TS) {
+    public static int getFilterSubtype(@Filter.Type int mainType, @Filter.Subtype int subtype) {
+        if (mainType == Filter.TYPE_TS) {
             switch (subtype) {
-                case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
+                case Filter.SUBTYPE_UNDEFINED:
                     return Constants.DemuxTsFilterType.UNDEFINED;
-                case TunerConstants.FILTER_SUBTYPE_SECTION:
+                case Filter.SUBTYPE_SECTION:
                     return Constants.DemuxTsFilterType.SECTION;
-                case TunerConstants.FILTER_SUBTYPE_PES:
+                case Filter.SUBTYPE_PES:
                     return Constants.DemuxTsFilterType.PES;
-                case TunerConstants.FILTER_SUBTYPE_TS:
+                case Filter.SUBTYPE_TS:
                     return Constants.DemuxTsFilterType.TS;
-                case TunerConstants.FILTER_SUBTYPE_AUDIO:
+                case Filter.SUBTYPE_AUDIO:
                     return Constants.DemuxTsFilterType.AUDIO;
-                case TunerConstants.FILTER_SUBTYPE_VIDEO:
+                case Filter.SUBTYPE_VIDEO:
                     return Constants.DemuxTsFilterType.VIDEO;
-                case TunerConstants.FILTER_SUBTYPE_PCR:
+                case Filter.SUBTYPE_PCR:
                     return Constants.DemuxTsFilterType.PCR;
-                case TunerConstants.FILTER_SUBTYPE_RECORD:
+                case Filter.SUBTYPE_RECORD:
                     return Constants.DemuxTsFilterType.RECORD;
-                case TunerConstants.FILTER_SUBTYPE_TEMI:
+                case Filter.SUBTYPE_TEMI:
                     return Constants.DemuxTsFilterType.TEMI;
                 default:
                     break;
             }
-        } else if (mainType == FilterConfiguration.FILTER_TYPE_MMTP) {
+        } else if (mainType == Filter.TYPE_MMTP) {
             switch (subtype) {
-                case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
+                case Filter.SUBTYPE_UNDEFINED:
                     return Constants.DemuxMmtpFilterType.UNDEFINED;
-                case TunerConstants.FILTER_SUBTYPE_SECTION:
+                case Filter.SUBTYPE_SECTION:
                     return Constants.DemuxMmtpFilterType.SECTION;
-                case TunerConstants.FILTER_SUBTYPE_PES:
+                case Filter.SUBTYPE_PES:
                     return Constants.DemuxMmtpFilterType.PES;
-                case TunerConstants.FILTER_SUBTYPE_MMPT:
+                case Filter.SUBTYPE_MMTP:
                     return Constants.DemuxMmtpFilterType.MMTP;
-                case TunerConstants.FILTER_SUBTYPE_AUDIO:
+                case Filter.SUBTYPE_AUDIO:
                     return Constants.DemuxMmtpFilterType.AUDIO;
-                case TunerConstants.FILTER_SUBTYPE_VIDEO:
+                case Filter.SUBTYPE_VIDEO:
                     return Constants.DemuxMmtpFilterType.VIDEO;
-                case TunerConstants.FILTER_SUBTYPE_RECORD:
+                case Filter.SUBTYPE_RECORD:
                     return Constants.DemuxMmtpFilterType.RECORD;
-                case TunerConstants.FILTER_SUBTYPE_DOWNLOAD:
+                case Filter.SUBTYPE_DOWNLOAD:
                     return Constants.DemuxMmtpFilterType.DOWNLOAD;
                 default:
                     break;
             }
 
-        } else if (mainType == FilterConfiguration.FILTER_TYPE_IP) {
+        } else if (mainType == Filter.TYPE_IP) {
             switch (subtype) {
-                case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
+                case Filter.SUBTYPE_UNDEFINED:
                     return Constants.DemuxIpFilterType.UNDEFINED;
-                case TunerConstants.FILTER_SUBTYPE_SECTION:
+                case Filter.SUBTYPE_SECTION:
                     return Constants.DemuxIpFilterType.SECTION;
-                case TunerConstants.FILTER_SUBTYPE_NTP:
+                case Filter.SUBTYPE_NTP:
                     return Constants.DemuxIpFilterType.NTP;
-                case TunerConstants.FILTER_SUBTYPE_IP_PAYLOAD:
+                case Filter.SUBTYPE_IP_PAYLOAD:
                     return Constants.DemuxIpFilterType.IP_PAYLOAD;
-                case TunerConstants.FILTER_SUBTYPE_IP:
+                case Filter.SUBTYPE_IP:
                     return Constants.DemuxIpFilterType.IP;
-                case TunerConstants.FILTER_SUBTYPE_PAYLOAD_THROUGH:
+                case Filter.SUBTYPE_PAYLOAD_THROUGH:
                     return Constants.DemuxIpFilterType.PAYLOAD_THROUGH;
                 default:
                     break;
             }
-        } else if (mainType == FilterConfiguration.FILTER_TYPE_TLV) {
+        } else if (mainType == Filter.TYPE_TLV) {
             switch (subtype) {
-                case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
+                case Filter.SUBTYPE_UNDEFINED:
                     return Constants.DemuxTlvFilterType.UNDEFINED;
-                case TunerConstants.FILTER_SUBTYPE_SECTION:
+                case Filter.SUBTYPE_SECTION:
                     return Constants.DemuxTlvFilterType.SECTION;
-                case TunerConstants.FILTER_SUBTYPE_TLV:
+                case Filter.SUBTYPE_TLV:
                     return Constants.DemuxTlvFilterType.TLV;
-                case TunerConstants.FILTER_SUBTYPE_PAYLOAD_THROUGH:
+                case Filter.SUBTYPE_PAYLOAD_THROUGH:
                     return Constants.DemuxTlvFilterType.PAYLOAD_THROUGH;
                 default:
                     break;
             }
-        } else if (mainType == FilterConfiguration.FILTER_TYPE_ALP) {
+        } else if (mainType == Filter.TYPE_ALP) {
             switch (subtype) {
-                case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
+                case Filter.SUBTYPE_UNDEFINED:
                     return Constants.DemuxAlpFilterType.UNDEFINED;
-                case TunerConstants.FILTER_SUBTYPE_SECTION:
+                case Filter.SUBTYPE_SECTION:
                     return Constants.DemuxAlpFilterType.SECTION;
-                case TunerConstants.FILTER_SUBTYPE_PTP:
+                case Filter.SUBTYPE_PTP:
                     return Constants.DemuxAlpFilterType.PTP;
-                case TunerConstants.FILTER_SUBTYPE_PAYLOAD_THROUGH:
+                case Filter.SUBTYPE_PAYLOAD_THROUGH:
                     return Constants.DemuxAlpFilterType.PAYLOAD_THROUGH;
                 default:
                     break;
diff --git a/media/java/android/media/tv/tuner/dvr/Dvr.java b/media/java/android/media/tv/tuner/dvr/Dvr.java
index 95508d3..a17773c 100644
--- a/media/java/android/media/tv/tuner/dvr/Dvr.java
+++ b/media/java/android/media/tv/tuner/dvr/Dvr.java
@@ -17,11 +17,16 @@
 package android.media.tv.tuner.dvr;
 
 import android.annotation.BytesLong;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.hardware.tv.tuner.V1_0.Constants;
 import android.media.tv.tuner.Tuner.Filter;
 import android.media.tv.tuner.TunerConstants.Result;
 import android.os.ParcelFileDescriptor;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Digital Video Record (DVR) interface provides record control on Demux's output buffer and
  * playback control on Demux's input buffer.
@@ -29,6 +34,37 @@
  * @hide
  */
 public class Dvr {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "PLAYBACK_STATUS_",
+            value = {PLAYBACK_STATUS_EMPTY, PLAYBACK_STATUS_ALMOST_EMPTY,
+                    PLAYBACK_STATUS_ALMOST_FULL, PLAYBACK_STATUS_FULL})
+    @interface PlaybackStatus {}
+
+    /**
+     * The space of the playback is empty.
+     */
+    public static final int PLAYBACK_STATUS_EMPTY = Constants.PlaybackStatus.SPACE_EMPTY;
+    /**
+     * The space of the playback is almost empty.
+     *
+     * <p> the threshold is set in {@link DvrSettings}.
+     */
+    public static final int PLAYBACK_STATUS_ALMOST_EMPTY =
+            Constants.PlaybackStatus.SPACE_ALMOST_EMPTY;
+    /**
+     * The space of the playback is almost full.
+     *
+     * <p> the threshold is set in {@link DvrSettings}.
+     */
+    public static final int PLAYBACK_STATUS_ALMOST_FULL =
+            Constants.PlaybackStatus.SPACE_ALMOST_FULL;
+    /**
+     * The space of the playback is full.
+     */
+    public static final int PLAYBACK_STATUS_FULL = Constants.PlaybackStatus.SPACE_FULL;
+
+
     private long mNativeContext;
     private DvrCallback mCallback;
 
diff --git a/media/java/android/media/tv/tuner/dvr/DvrCallback.java b/media/java/android/media/tv/tuner/dvr/DvrCallback.java
index 3d140f0..ee0cfa7d 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrCallback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrCallback.java
@@ -16,6 +16,9 @@
 
 package android.media.tv.tuner.dvr;
 
+import android.media.tv.tuner.TunerConstants.FilterStatus;
+import android.media.tv.tuner.dvr.Dvr.PlaybackStatus;
+
 /**
  * Callback interface for receiving information from DVR interfaces.
  *
@@ -25,9 +28,9 @@
     /**
      * Invoked when record status changed.
      */
-    void onRecordStatusChanged(int status);
+    void onRecordStatusChanged(@FilterStatus int status);
     /**
      * Invoked when playback status changed.
      */
-    void onPlaybackStatusChanged(int status);
+    void onPlaybackStatusChanged(@PlaybackStatus int status);
 }
diff --git a/media/java/android/media/tv/tuner/dvr/DvrSettings.java b/media/java/android/media/tv/tuner/dvr/DvrSettings.java
index 46efd7a..49e875a 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrSettings.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrSettings.java
@@ -19,8 +19,11 @@
 import android.annotation.BytesLong;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
 import android.hardware.tv.tuner.V1_0.Constants;
 import android.media.tv.tuner.TunerConstants.FilterStatus;
+import android.media.tv.tuner.TunerUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -94,9 +97,13 @@
 
     /**
      * Creates a builder for {@link DvrSettings}.
+     *
+     * @param context the context of the caller.
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     @NonNull
-    public static Builder newBuilder() {
+    public static Builder builder(Context context) {
+        TunerUtils.checkTunerPermission(context);
         return new Builder();
     }
 
diff --git a/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
index f0fe533..fcca6a1 100644
--- a/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
@@ -16,20 +16,123 @@
 
 package android.media.tv.tuner.filter;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.hardware.tv.tuner.V1_0.Constants;
+import android.media.tv.tuner.TunerUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Filter configuration for a ALP filter.
  * @hide
  */
 public class AlpFilterConfiguration extends FilterConfiguration {
-    private int mPacketType;
-    private int mLengthType;
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "LENGTH_TYPE_", value =
+            {LENGTH_TYPE_UNDEFINED, LENGTH_TYPE_WITHOUT_ADDITIONAL_HEADER,
+            LENGTH_TYPE_WITH_ADDITIONAL_HEADER})
+    public @interface LengthType {}
 
-    public AlpFilterConfiguration(Settings settings) {
+    /**
+     * Length type not defined.
+     */
+    public static final int LENGTH_TYPE_UNDEFINED = Constants.DemuxAlpLengthType.UNDEFINED;
+    /**
+     * Length does NOT include additional header.
+     */
+    public static final int LENGTH_TYPE_WITHOUT_ADDITIONAL_HEADER =
+            Constants.DemuxAlpLengthType.WITHOUT_ADDITIONAL_HEADER;
+    /**
+     * Length includes additional header.
+     */
+    public static final int LENGTH_TYPE_WITH_ADDITIONAL_HEADER =
+            Constants.DemuxAlpLengthType.WITH_ADDITIONAL_HEADER;
+
+
+    private final int mPacketType;
+    private final int mLengthType;
+
+    public AlpFilterConfiguration(Settings settings, int packetType, int lengthType) {
         super(settings);
+        mPacketType = packetType;
+        mLengthType = lengthType;
     }
 
     @Override
     public int getType() {
         return FilterConfiguration.FILTER_TYPE_ALP;
     }
+
+    /**
+     * Gets packet type.
+     */
+    @FilterConfiguration.PacketType
+    public int getPacketType() {
+        return mPacketType;
+    }
+    /**
+     * Gets length type.
+     */
+    @LengthType
+    public int getLengthType() {
+        return mLengthType;
+    }
+
+    /**
+     * Creates a builder for {@link AlpFilterConfiguration}.
+     *
+     * @param context the context of the caller.
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @NonNull
+    public static Builder builder(@NonNull Context context) {
+        TunerUtils.checkTunerPermission(context);
+        return new Builder();
+    }
+
+    /**
+     * Builder for {@link AlpFilterConfiguration}.
+     */
+    public static class Builder extends FilterConfiguration.Builder<Builder> {
+        private int mPacketType;
+        private int mLengthType;
+
+        private Builder() {
+        }
+
+        /**
+         * Sets packet type.
+         */
+        @NonNull
+        public Builder setPacketType(@FilterConfiguration.PacketType int packetType) {
+            mPacketType = packetType;
+            return this;
+        }
+        /**
+         * Sets length type.
+         */
+        @NonNull
+        public Builder setLengthType(@LengthType int lengthType) {
+            mLengthType = lengthType;
+            return this;
+        }
+
+        /**
+         * Builds a {@link AlpFilterConfiguration} object.
+         */
+        @NonNull
+        public AlpFilterConfiguration build() {
+            return new AlpFilterConfiguration(mSettings, mPacketType, mLengthType);
+        }
+
+        @Override
+        Builder self() {
+            return this;
+        }
+    }
 }
diff --git a/media/java/android/media/tv/tuner/filter/AvSettings.java b/media/java/android/media/tv/tuner/filter/AvSettings.java
index a7c49d5..93eaaa4 100644
--- a/media/java/android/media/tv/tuner/filter/AvSettings.java
+++ b/media/java/android/media/tv/tuner/filter/AvSettings.java
@@ -16,21 +16,82 @@
 
 package android.media.tv.tuner.filter;
 
-import android.media.tv.tuner.TunerConstants;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
 import android.media.tv.tuner.TunerUtils;
 
 /**
  * Filter Settings for a Video and Audio.
+ *
  * @hide
  */
 public class AvSettings extends Settings {
-    private boolean mIsPassthrough;
+    private final boolean mIsPassthrough;
 
-    private AvSettings(int mainType, boolean isAudio) {
+    private AvSettings(int mainType, boolean isAudio, boolean isPassthrough) {
         super(TunerUtils.getFilterSubtype(
                 mainType,
                 isAudio
-                        ? TunerConstants.FILTER_SUBTYPE_AUDIO
-                        : TunerConstants.FILTER_SUBTYPE_VIDEO));
+                        ? Filter.SUBTYPE_AUDIO
+                        : Filter.SUBTYPE_VIDEO));
+        mIsPassthrough = isPassthrough;
+    }
+
+    /**
+     * Checks whether it's passthrough.
+     */
+    public boolean isPassthrough() {
+        return mIsPassthrough;
+    }
+
+    /**
+     * Creates a builder for {@link AvSettings}.
+     *
+     * @param context the context of the caller.
+     * @param mainType the filter main type.
+     * @param isAudio {@code true} if it's audio settings; {@code false} if it's video settings.
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @NonNull
+    public static Builder builder(
+            @NonNull Context context, @Filter.Type int mainType, boolean isAudio) {
+        TunerUtils.checkTunerPermission(context);
+        return new Builder(mainType, isAudio);
+    }
+
+    /**
+     * Builder for {@link AvSettings}.
+     */
+    public static class Builder extends Settings.Builder<Builder> {
+        private final boolean mIsAudio;
+        private boolean mIsPassthrough;
+
+        private Builder(int mainType, boolean isAudio) {
+            super(mainType);
+            mIsAudio = isAudio;
+        }
+
+        /**
+         * Sets whether it's passthrough.
+         */
+        @NonNull
+        public Builder setPassthrough(boolean isPassthrough) {
+            mIsPassthrough = isPassthrough;
+            return this;
+        }
+
+        /**
+         * Builds a {@link AvSettings} object.
+         */
+        @NonNull
+        public AvSettings build() {
+            return new AvSettings(mMainType, mIsAudio, mIsPassthrough);
+        }
+
+        @Override
+        Builder self() {
+            return this;
+        }
     }
 }
diff --git a/media/java/android/media/tv/tuner/filter/DownloadSettings.java b/media/java/android/media/tv/tuner/filter/DownloadSettings.java
index 0742b11..fa7744a 100644
--- a/media/java/android/media/tv/tuner/filter/DownloadSettings.java
+++ b/media/java/android/media/tv/tuner/filter/DownloadSettings.java
@@ -16,7 +16,9 @@
 
 package android.media.tv.tuner.filter;
 
-import android.media.tv.tuner.TunerConstants;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
 import android.media.tv.tuner.TunerUtils;
 
 /**
@@ -24,9 +26,63 @@
  * @hide
  */
 public class DownloadSettings extends Settings {
-    private int mDownloadId;
+    private final int mDownloadId;
 
-    public DownloadSettings(int mainType) {
-        super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_DOWNLOAD));
+    private DownloadSettings(int mainType, int downloadId) {
+        super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_DOWNLOAD));
+        mDownloadId = downloadId;
+    }
+
+    /**
+     * Gets download ID.
+     */
+    public int getDownloadId() {
+        return mDownloadId;
+    }
+
+    /**
+     * Creates a builder for {@link DownloadSettings}.
+     *
+     * @param context the context of the caller.
+     * @param mainType the filter main type.
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @NonNull
+    public static Builder builder(@NonNull Context context, @Filter.Type int mainType) {
+        TunerUtils.checkTunerPermission(context);
+        return new Builder(mainType);
+    }
+
+    /**
+     * Builder for {@link DownloadSettings}.
+     */
+    public static class Builder extends Settings.Builder<Builder> {
+        private int mDownloadId;
+
+        private Builder(int mainType) {
+            super(mainType);
+        }
+
+        /**
+         * Sets download ID.
+         */
+        @NonNull
+        public Builder setDownloadId(int downloadId) {
+            mDownloadId = downloadId;
+            return this;
+        }
+
+        /**
+         * Builds a {@link DownloadSettings} object.
+         */
+        @NonNull
+        public DownloadSettings build() {
+            return new DownloadSettings(mMainType, mDownloadId);
+        }
+
+        @Override
+        Builder self() {
+            return this;
+        }
     }
 }
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 804c0c5..3f6154b 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -17,9 +17,15 @@
 package android.media.tv.tuner.filter;
 
 import android.annotation.BytesLong;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.hardware.tv.tuner.V1_0.Constants;
 import android.media.tv.tuner.Tuner.FilterCallback;
+import android.media.tv.tuner.TunerConstants.Result;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Tuner data filter.
@@ -29,6 +35,128 @@
  * @hide
  */
 public class Filter implements AutoCloseable {
+    /** @hide */
+    @IntDef(prefix = "TYPE_",
+            value = {TYPE_TS, TYPE_MMTP, TYPE_IP, TYPE_TLV, TYPE_ALP})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    /**
+     * TS filter type.
+     */
+    public static final int TYPE_TS = Constants.DemuxFilterMainType.TS;
+    /**
+     * MMTP filter type.
+     */
+    public static final int TYPE_MMTP = Constants.DemuxFilterMainType.MMTP;
+    /**
+     * IP filter type.
+     */
+    public static final int TYPE_IP = Constants.DemuxFilterMainType.IP;
+    /**
+     * TLV filter type.
+     */
+    public static final int TYPE_TLV = Constants.DemuxFilterMainType.TLV;
+    /**
+     * ALP filter type.
+     */
+    public static final int TYPE_ALP = Constants.DemuxFilterMainType.ALP;
+
+    /** @hide */
+    @IntDef(prefix = "SUBTYPE_",
+            value = {SUBTYPE_UNDEFINED, SUBTYPE_SECTION, SUBTYPE_PES, SUBTYPE_AUDIO, SUBTYPE_VIDEO,
+                    SUBTYPE_DOWNLOAD, SUBTYPE_RECORD, SUBTYPE_TS, SUBTYPE_PCR, SUBTYPE_TEMI,
+                    SUBTYPE_MMTP, SUBTYPE_NTP, SUBTYPE_IP_PAYLOAD, SUBTYPE_IP,
+                    SUBTYPE_PAYLOAD_THROUGH, SUBTYPE_TLV, SUBTYPE_PTP, })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Subtype {}
+    /**
+     * Filter subtype undefined.
+     * @hide
+     */
+    public static final int SUBTYPE_UNDEFINED = 0;
+    /**
+     * Section filter subtype.
+     * @hide
+     */
+    public static final int SUBTYPE_SECTION = 1;
+    /**
+     * PES filter subtype.
+     * @hide
+     */
+    public static final int SUBTYPE_PES = 2;
+    /**
+     * Audio filter subtype.
+     * @hide
+     */
+    public static final int SUBTYPE_AUDIO = 3;
+    /**
+     * Video filter subtype.
+     * @hide
+     */
+    public static final int SUBTYPE_VIDEO = 4;
+    /**
+     * Download filter subtype.
+     * @hide
+     */
+    public static final int SUBTYPE_DOWNLOAD = 5;
+    /**
+     * Record filter subtype.
+     * @hide
+     */
+    public static final int SUBTYPE_RECORD = 6;
+    /**
+     * TS filter subtype.
+     * @hide
+     */
+    public static final int SUBTYPE_TS = 7;
+    /**
+     * PCR filter subtype.
+     * @hide
+     */
+    public static final int SUBTYPE_PCR = 8;
+    /**
+     * TEMI filter subtype.
+     * @hide
+     */
+    public static final int SUBTYPE_TEMI = 9;
+    /**
+     * MMTP filter subtype.
+     * @hide
+     */
+    public static final int SUBTYPE_MMTP = 10;
+    /**
+     * NTP filter subtype.
+     * @hide
+     */
+    public static final int SUBTYPE_NTP = 11;
+    /**
+     * Payload filter subtype.
+     * @hide
+     */
+    public static final int SUBTYPE_IP_PAYLOAD = 12;
+    /**
+     * IP filter subtype.
+     * @hide
+     */
+    public static final int SUBTYPE_IP = 13;
+    /**
+     * Payload through filter subtype.
+     * @hide
+     */
+    public static final int SUBTYPE_PAYLOAD_THROUGH = 14;
+    /**
+     * TLV filter subtype.
+     * @hide
+     */
+    public static final int SUBTYPE_TLV = 15;
+    /**
+     * PTP filter subtype.
+     * @hide
+     */
+    public static final int SUBTYPE_PTP = 16;
+
+
     private long mNativeContext;
     private FilterCallback mCallback;
     private final int mId;
@@ -56,6 +184,7 @@
      * @param config the configuration of the filter.
      * @return result status of the operation.
      */
+    @Result
     public int configure(@NonNull FilterConfiguration config) {
         int subType = -1;
         Settings s = config.getSettings();
@@ -68,6 +197,7 @@
     /**
      * Gets the filter Id.
      */
+    @Result
     public int getId() {
         return nativeGetId();
     }
@@ -84,6 +214,7 @@
      * use demux as data source if the filter instance is NULL.
      * @return result status of the operation.
      */
+    @Result
     public int setDataSource(@Nullable Filter source) {
         return nativeSetDataSource(source);
     }
@@ -93,6 +224,7 @@
      *
      * @return result status of the operation.
      */
+    @Result
     public int start() {
         return nativeStartFilter();
     }
@@ -103,6 +235,7 @@
      *
      * @return result status of the operation.
      */
+    @Result
     public int stop() {
         return nativeStopFilter();
     }
@@ -112,6 +245,7 @@
      *
      * @return result status of the operation.
      */
+    @Result
     public int flush() {
         return nativeFlushFilter();
     }
@@ -124,6 +258,7 @@
      * @param size the maximum number of bytes to read.
      * @return the number of bytes read.
      */
+    @Result
     public int read(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) {
         size = Math.min(size, buffer.length - offset);
         return nativeRead(buffer, offset, size);
diff --git a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
index 6496627..c901e2b 100644
--- a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
@@ -32,8 +32,12 @@
 @SystemApi
 public abstract class FilterConfiguration {
 
-    /** @hide */
-    @IntDef({FILTER_TYPE_TS, FILTER_TYPE_MMTP, FILTER_TYPE_IP, FILTER_TYPE_TLV, FILTER_TYPE_ALP})
+    /**
+     * TODO: moved to Filter. Remove it here.
+     * @hide
+     */
+    @IntDef(prefix = "FILTER_TYPE_", value =
+            {FILTER_TYPE_TS, FILTER_TYPE_MMTP, FILTER_TYPE_IP, FILTER_TYPE_TLV, FILTER_TYPE_ALP})
     @Retention(RetentionPolicy.SOURCE)
     public @interface FilterType {}
 
@@ -58,6 +62,30 @@
      */
     public static final int FILTER_TYPE_ALP = Constants.DemuxFilterMainType.ALP;
 
+
+    /** @hide */
+    @IntDef(prefix = "PACKET_TYPE_", value =
+            {PACKET_TYPE_IPV4, PACKET_TYPE_COMPRESSED, PACKET_TYPE_SIGNALING})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PacketType {}
+
+    /**
+     * IP v4 packet type.
+     * @hide
+     */
+    public static final int PACKET_TYPE_IPV4 = 0;
+    /**
+     * Compressed packet type.
+     * @hide
+     */
+    public static final int PACKET_TYPE_COMPRESSED = 2;
+    /**
+     * Signaling packet type.
+     * @hide
+     */
+    public static final int PACKET_TYPE_SIGNALING = 4;
+
+
     @Nullable
     /* package */ final Settings mSettings;
 
@@ -77,4 +105,27 @@
     public Settings getSettings() {
         return mSettings;
     }
+
+    /**
+     * Builder for {@link FilterConfiguration}.
+     *
+     * @param <T> The subclass to be built.
+     * @hide
+     */
+    public abstract static class Builder<T extends Builder<T>> {
+        /* package */ Settings mSettings;
+
+        /* package */ Builder() {
+        }
+
+        /**
+         * Sets filter settings.
+         */
+        @Nullable
+        public T setFrequency(Settings settings) {
+            mSettings = settings;
+            return self();
+        }
+        /* package */ abstract T self();
+    }
 }
diff --git a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
index c896368..98edf10 100644
--- a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
@@ -16,23 +16,152 @@
 
 package android.media.tv.tuner.filter;
 
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.Size;
+import android.content.Context;
+import android.media.tv.tuner.TunerUtils;
+
 /**
  * Filter configuration for a IP filter.
  * @hide
  */
 public class IpFilterConfiguration extends FilterConfiguration {
-    private byte[] mSrcIpAddress;
-    private byte[] mDstIpAddress;
-    private int mSrcPort;
-    private int mDstPort;
-    private boolean mPassthrough;
+    private final byte[] mSrcIpAddress;
+    private final byte[] mDstIpAddress;
+    private final int mSrcPort;
+    private final int mDstPort;
+    private final boolean mPassthrough;
 
-    public IpFilterConfiguration(Settings settings) {
+    public IpFilterConfiguration(Settings settings, byte[] srcAddr, byte[] dstAddr, int srcPort,
+            int dstPort, boolean passthrough) {
         super(settings);
+        mSrcIpAddress = srcAddr;
+        mDstIpAddress = dstAddr;
+        mSrcPort = srcPort;
+        mDstPort = dstPort;
+        mPassthrough = passthrough;
     }
 
     @Override
     public int getType() {
         return FilterConfiguration.FILTER_TYPE_IP;
     }
+
+    /**
+     * Gets source IP address.
+     */
+    @Size(min = 4, max = 16)
+    public byte[] getSrcIpAddress() {
+        return mSrcIpAddress;
+    }
+    /**
+     * Gets destination IP address.
+     */
+    @Size(min = 4, max = 16)
+    public byte[] getDstIpAddress() {
+        return mDstIpAddress;
+    }
+    /**
+     * Gets source port.
+     */
+    public int getSrcPort() {
+        return mSrcPort;
+    }
+    /**
+     * Gets destination port.
+     */
+    public int getDstPort() {
+        return mDstPort;
+    }
+    /**
+     * Checks whether the filter is passthrough.
+     *
+     * @return {@code true} if the data from IP subtype go to next filter directly;
+     *         {@code false} otherwise.
+     */
+    public boolean isPassthrough() {
+        return mPassthrough;
+    }
+
+    /**
+     * Creates a builder for {@link IpFilterConfiguration}.
+     *
+     * @param context the context of the caller.
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @NonNull
+    public static Builder builder(@NonNull Context context) {
+        TunerUtils.checkTunerPermission(context);
+        return new Builder();
+    }
+
+    /**
+     * Builder for {@link IpFilterConfiguration}.
+     */
+    public static class Builder extends FilterConfiguration.Builder<Builder> {
+        private byte[] mSrcIpAddress;
+        private byte[] mDstIpAddress;
+        private int mSrcPort;
+        private int mDstPort;
+        private boolean mPassthrough;
+
+        private Builder() {
+        }
+
+        /**
+         * Sets source IP address.
+         */
+        @NonNull
+        public Builder setSrcIpAddress(byte[] srcIpAddress) {
+            mSrcIpAddress = srcIpAddress;
+            return this;
+        }
+        /**
+         * Sets destination IP address.
+         */
+        @NonNull
+        public Builder setDstIpAddress(byte[] dstIpAddress) {
+            mDstIpAddress = dstIpAddress;
+            return this;
+        }
+        /**
+         * Sets source port.
+         */
+        @NonNull
+        public Builder setSrcPort(int srcPort) {
+            mSrcPort = srcPort;
+            return this;
+        }
+        /**
+         * Sets destination port.
+         */
+        @NonNull
+        public Builder setDstPort(int dstPort) {
+            mDstPort = dstPort;
+            return this;
+        }
+        /**
+         * Sets passthrough.
+         */
+        @NonNull
+        public Builder setPassthrough(boolean passthrough) {
+            mPassthrough = passthrough;
+            return this;
+        }
+
+        /**
+         * Builds a {@link IpFilterConfiguration} object.
+         */
+        @NonNull
+        public IpFilterConfiguration build() {
+            return new IpFilterConfiguration(
+                    mSettings, mSrcIpAddress, mDstIpAddress, mSrcPort, mDstPort, mPassthrough);
+        }
+
+        @Override
+        Builder self() {
+            return this;
+        }
+    }
 }
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
index 37f94ae..0b5c56b 100644
--- a/media/java/android/media/tv/tuner/filter/MediaEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -16,6 +16,7 @@
 
 package android.media.tv.tuner.filter;
 
+import android.annotation.BytesLong;
 import android.annotation.Nullable;
 import android.media.tv.tuner.Tuner.Filter;
 
@@ -28,23 +29,27 @@
     private final int mStreamId;
     private final boolean mIsPtsPresent;
     private final long mPts;
-    private final int mDataLength;
+    private final long mDataLength;
+    private final long mOffset;
     private final Object mLinearBuffer;
     private final boolean mIsSecureMemory;
+    private final long mDataId;
     private final int mMpuSequenceNumber;
     private final boolean mIsPrivateData;
     private final AudioDescriptor mExtraMetaData;
 
     // This constructor is used by JNI code only
-    private MediaEvent(int streamId, boolean isPtsPresent, long pts, int dataLength, Object buffer,
-            boolean isSecureMemory, int mpuSequenceNumber, boolean isPrivateData,
-            AudioDescriptor extraMetaData) {
+    private MediaEvent(int streamId, boolean isPtsPresent, long pts, long dataLength, long offset,
+            Object buffer, boolean isSecureMemory, long dataId, int mpuSequenceNumber,
+            boolean isPrivateData, AudioDescriptor extraMetaData) {
         mStreamId = streamId;
         mIsPtsPresent = isPtsPresent;
         mPts = pts;
         mDataLength = dataLength;
+        mOffset = offset;
         mLinearBuffer = buffer;
         mIsSecureMemory = isSecureMemory;
+        mDataId = dataId;
         mMpuSequenceNumber = mpuSequenceNumber;
         mIsPrivateData = isPrivateData;
         mExtraMetaData = extraMetaData;
@@ -76,11 +81,20 @@
     /**
      * Gets data size in bytes of audio or video frame.
      */
-    public int getDataLength() {
+    @BytesLong
+    public long getDataLength() {
         return mDataLength;
     }
 
     /**
+     * The offset in the memory block which is shared among multiple Media Events.
+     */
+    @BytesLong
+    public long getOffset() {
+        return mOffset;
+    }
+
+    /**
      * Gets a linear buffer associated to the memory where audio or video data stays.
      * TODO: use LinearBuffer when it's ready.
      *
@@ -101,6 +115,15 @@
     }
 
     /**
+     * Gets the ID which is used by HAL to provide additional information for AV data.
+     *
+     * <p>For secure audio, it's the audio handle used by Audio Track.
+     */
+    public long getAvDataId() {
+        return mDataId;
+    }
+
+    /**
      * Gets MPU sequence number of filtered data.
      */
     public int getMpuSequenceNumber() {
diff --git a/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
index 9045ce6..248f23a 100644
--- a/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
@@ -16,19 +16,78 @@
 
 package android.media.tv.tuner.filter;
 
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.media.tv.tuner.TunerUtils;
+
 /**
  * Filter configuration for a MMTP filter.
  * @hide
  */
 public class MmtpFilterConfiguration extends FilterConfiguration {
-    private int mMmtpPid;
+    private final int mMmtpPid;
 
-    public MmtpFilterConfiguration(Settings settings) {
+    public MmtpFilterConfiguration(Settings settings, int mmtpPid) {
         super(settings);
+        mMmtpPid = mmtpPid;
     }
 
     @Override
     public int getType() {
         return FilterConfiguration.FILTER_TYPE_MMTP;
     }
+
+    /**
+     * Gets MMTP PID.
+     *
+     * <p>Packet ID is used to specify packets in MMTP.
+     */
+    public int getMmtpPid() {
+        return mMmtpPid;
+    }
+
+    /**
+     * Creates a builder for {@link IpFilterConfiguration}.
+     *
+     * @param context the context of the caller.
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @NonNull
+    public static Builder builder(@NonNull Context context) {
+        TunerUtils.checkTunerPermission(context);
+        return new Builder();
+    }
+
+    /**
+     * Builder for {@link IpFilterConfiguration}.
+     */
+    public static class Builder extends FilterConfiguration.Builder<Builder> {
+        private int mMmtpPid;
+
+        private Builder() {
+        }
+
+        /**
+         * Sets MMTP PID.
+         */
+        @NonNull
+        public Builder setMmtpPid(int mmtpPid) {
+            mMmtpPid = mmtpPid;
+            return this;
+        }
+
+        /**
+         * Builds a {@link IpFilterConfiguration} object.
+         */
+        @NonNull
+        public MmtpFilterConfiguration build() {
+            return new MmtpFilterConfiguration(mSettings, mMmtpPid);
+        }
+
+        @Override
+        Builder self() {
+            return this;
+        }
+    }
 }
diff --git a/media/java/android/media/tv/tuner/filter/PesSettings.java b/media/java/android/media/tv/tuner/filter/PesSettings.java
index bfa1f8c..0f83597 100644
--- a/media/java/android/media/tv/tuner/filter/PesSettings.java
+++ b/media/java/android/media/tv/tuner/filter/PesSettings.java
@@ -20,9 +20,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.Context;
-import android.media.tv.tuner.TunerConstants;
 import android.media.tv.tuner.TunerUtils;
-import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
 
 /**
  * Filter Settings for a PES Data.
@@ -34,8 +32,8 @@
     private final int mStreamId;
     private final boolean mIsRaw;
 
-    private PesSettings(@FilterType int mainType, int streamId, boolean isRaw) {
-        super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_PES));
+    private PesSettings(@Filter.Type int mainType, int streamId, boolean isRaw) {
+        super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_PES));
         mStreamId = streamId;
         mIsRaw = isRaw;
     }
@@ -65,7 +63,7 @@
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     @NonNull
-    public static Builder builder(@NonNull Context context, @FilterType int mainType) {
+    public static Builder builder(@NonNull Context context, @Filter.Type int mainType) {
         TunerUtils.checkTunerPermission(context);
         return new Builder(mainType);
     }
diff --git a/media/java/android/media/tv/tuner/filter/RecordSettings.java b/media/java/android/media/tv/tuner/filter/RecordSettings.java
index 701868a..4e9d67f 100644
--- a/media/java/android/media/tv/tuner/filter/RecordSettings.java
+++ b/media/java/android/media/tv/tuner/filter/RecordSettings.java
@@ -16,18 +16,230 @@
 
 package android.media.tv.tuner.filter;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.hardware.tv.tuner.V1_0.Constants;
 import android.media.tv.tuner.TunerConstants;
+import android.media.tv.tuner.TunerConstants.ScIndexType;
 import android.media.tv.tuner.TunerUtils;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * The Settings for the record in DVR.
  * @hide
  */
 public class RecordSettings extends Settings {
-    private int mIndexType;
-    private int mIndexMask;
+    /**
+     * Indexes can be tagged through TS (Transport Stream) header.
+     *
+     * @hide
+     */
+    @IntDef(flag = true,
+            prefix = "TS_INDEX_",
+            value = {TS_INDEX_FIRST_PACKET, TS_INDEX_PAYLOAD_UNIT_START_INDICATOR,
+                    TS_INDEX_CHANGE_TO_NOT_SCRAMBLED, TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED,
+                    TS_INDEX_CHANGE_TO_ODD_SCRAMBLED, TS_INDEX_DISCONTINUITY_INDICATOR,
+                    TS_INDEX_RANDOM_ACCESS_INDICATOR, TS_INDEX_PRIORITY_INDICATOR,
+                    TS_INDEX_PCR_FLAG, TS_INDEX_OPCR_FLAG, TS_INDEX_SPLICING_POINT_FLAG,
+                    TS_INDEX_PRIVATE_DATA, TS_INDEX_ADAPTATION_EXTENSION_FLAG})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TsIndexMask {}
 
-    public RecordSettings(int mainType) {
-        super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_RECORD));
+    /**
+     * TS index FIRST_PACKET.
+     * @hide
+     */
+    public static final int TS_INDEX_FIRST_PACKET = Constants.DemuxTsIndex.FIRST_PACKET;
+    /**
+     * TS index PAYLOAD_UNIT_START_INDICATOR.
+     * @hide
+     */
+    public static final int TS_INDEX_PAYLOAD_UNIT_START_INDICATOR =
+            Constants.DemuxTsIndex.PAYLOAD_UNIT_START_INDICATOR;
+    /**
+     * TS index CHANGE_TO_NOT_SCRAMBLED.
+     * @hide
+     */
+    public static final int TS_INDEX_CHANGE_TO_NOT_SCRAMBLED =
+            Constants.DemuxTsIndex.CHANGE_TO_NOT_SCRAMBLED;
+    /**
+     * TS index CHANGE_TO_EVEN_SCRAMBLED.
+     * @hide
+     */
+    public static final int TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED =
+            Constants.DemuxTsIndex.CHANGE_TO_EVEN_SCRAMBLED;
+    /**
+     * TS index CHANGE_TO_ODD_SCRAMBLED.
+     * @hide
+     */
+    public static final int TS_INDEX_CHANGE_TO_ODD_SCRAMBLED =
+            Constants.DemuxTsIndex.CHANGE_TO_ODD_SCRAMBLED;
+    /**
+     * TS index DISCONTINUITY_INDICATOR.
+     * @hide
+     */
+    public static final int TS_INDEX_DISCONTINUITY_INDICATOR =
+            Constants.DemuxTsIndex.DISCONTINUITY_INDICATOR;
+    /**
+     * TS index RANDOM_ACCESS_INDICATOR.
+     * @hide
+     */
+    public static final int TS_INDEX_RANDOM_ACCESS_INDICATOR =
+            Constants.DemuxTsIndex.RANDOM_ACCESS_INDICATOR;
+    /**
+     * TS index PRIORITY_INDICATOR.
+     * @hide
+     */
+    public static final int TS_INDEX_PRIORITY_INDICATOR = Constants.DemuxTsIndex.PRIORITY_INDICATOR;
+    /**
+     * TS index PCR_FLAG.
+     * @hide
+     */
+    public static final int TS_INDEX_PCR_FLAG = Constants.DemuxTsIndex.PCR_FLAG;
+    /**
+     * TS index OPCR_FLAG.
+     * @hide
+     */
+    public static final int TS_INDEX_OPCR_FLAG = Constants.DemuxTsIndex.OPCR_FLAG;
+    /**
+     * TS index SPLICING_POINT_FLAG.
+     * @hide
+     */
+    public static final int TS_INDEX_SPLICING_POINT_FLAG =
+            Constants.DemuxTsIndex.SPLICING_POINT_FLAG;
+    /**
+     * TS index PRIVATE_DATA.
+     * @hide
+     */
+    public static final int TS_INDEX_PRIVATE_DATA = Constants.DemuxTsIndex.PRIVATE_DATA;
+    /**
+     * TS index ADAPTATION_EXTENSION_FLAG.
+     * @hide
+     */
+    public static final int TS_INDEX_ADAPTATION_EXTENSION_FLAG =
+            Constants.DemuxTsIndex.ADAPTATION_EXTENSION_FLAG;
+    /**
+     * @hide
+     */
+    @IntDef(flag = true,
+            prefix = "SC_",
+            value = {
+                TunerConstants.SC_INDEX_I_FRAME,
+                TunerConstants.SC_INDEX_P_FRAME,
+                TunerConstants.SC_INDEX_B_FRAME,
+                TunerConstants.SC_INDEX_SEQUENCE,
+                TunerConstants.SC_HEVC_INDEX_SPS,
+                TunerConstants.SC_HEVC_INDEX_AUD,
+                TunerConstants.SC_HEVC_INDEX_SLICE_CE_BLA_W_LP,
+                TunerConstants.SC_HEVC_INDEX_SLICE_BLA_W_RADL,
+                TunerConstants.SC_HEVC_INDEX_SLICE_BLA_N_LP,
+                TunerConstants.SC_HEVC_INDEX_SLICE_IDR_W_RADL,
+                TunerConstants.SC_HEVC_INDEX_SLICE_IDR_N_LP,
+                TunerConstants.SC_HEVC_INDEX_SLICE_TRAIL_CRA,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ScIndexMask {}
+
+
+    private final int mTsIndexMask;
+    private final int mScIndexType;
+    private final int mScIndexMask;
+
+    private RecordSettings(int mainType, int tsIndexType, int scIndexType, int scIndexMask) {
+        super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_RECORD));
+        mTsIndexMask = tsIndexType;
+        mScIndexType = scIndexType;
+        mScIndexMask = scIndexMask;
     }
+
+    /**
+     * Gets TS index mask.
+     */
+    @TsIndexMask
+    public int getTsIndexMask() {
+        return mTsIndexMask;
+    }
+    /**
+     * Gets Start Code index type.
+     */
+    @ScIndexType
+    public int getScIndexType() {
+        return mScIndexType;
+    }
+    /**
+     * Gets Start Code index mask.
+     */
+    @ScIndexMask
+    public int getScIndexMask() {
+        return mScIndexMask;
+    }
+
+    /**
+     * Creates a builder for {@link RecordSettings}.
+     *
+     * @param context the context of the caller.
+     * @param mainType the filter main type.
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @NonNull
+    public static Builder builder(@NonNull Context context, @Filter.Type int mainType) {
+        TunerUtils.checkTunerPermission(context);
+        return new Builder(mainType);
+    }
+
+    /**
+     * Builder for {@link RecordSettings}.
+     */
+    public static class Builder extends Settings.Builder<Builder> {
+        private int mTsIndexMask;
+        private int mScIndexType;
+        private int mScIndexMask;
+
+        private Builder(int mainType) {
+            super(mainType);
+        }
+
+        /**
+         * Sets TS index mask.
+         */
+        @NonNull
+        public Builder setTsIndexMask(@TsIndexMask int indexMask) {
+            mTsIndexMask = indexMask;
+            return this;
+        }
+        /**
+         * Sets index type.
+         */
+        @NonNull
+        public Builder setScIndexType(@ScIndexType int indexType) {
+            mScIndexType = indexType;
+            return this;
+        }
+        /**
+         * Sets Start Code index mask.
+         */
+        @NonNull
+        public Builder setScIndexMask(@ScIndexMask int indexMask) {
+            mScIndexMask = indexMask;
+            return this;
+        }
+
+        /**
+         * Builds a {@link RecordSettings} object.
+         */
+        @NonNull
+        public RecordSettings build() {
+            return new RecordSettings(mMainType, mTsIndexMask, mScIndexType, mScIndexMask);
+        }
+
+        @Override
+        Builder self() {
+            return this;
+        }
+    }
+
 }
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettings.java b/media/java/android/media/tv/tuner/filter/SectionSettings.java
index 36e3d7c..b8d0fad 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettings.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettings.java
@@ -16,7 +16,6 @@
 
 package android.media.tv.tuner.filter;
 
-import android.media.tv.tuner.TunerConstants;
 import android.media.tv.tuner.TunerUtils;
 
 /**
@@ -26,6 +25,6 @@
 public class SectionSettings extends Settings {
 
     SectionSettings(int mainType) {
-        super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_SECTION));
+        super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_SECTION));
     }
 }
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
index 414ea67..a2d42d8 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
@@ -16,18 +16,115 @@
 
 package android.media.tv.tuner.filter;
 
-import java.util.List;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.media.tv.tuner.TunerUtils;
 
 /**
- * Bits Settings for Section Filter.
+ * Bits Settings for Section Filters.
  * @hide
  */
 public class SectionSettingsWithSectionBits extends SectionSettings {
-    private List<Byte> mFilter;
-    private List<Byte> mMask;
-    private List<Byte> mMode;
+    private final byte[] mFilter;
+    private final byte[] mMask;
+    private final byte[] mMode;
 
-    private SectionSettingsWithSectionBits(int mainType) {
+
+    private SectionSettingsWithSectionBits(int mainType, byte[] filter, byte[] mask, byte[] mode) {
         super(mainType);
+        mFilter = filter;
+        mMask = mask;
+        mMode = mode;
+    }
+
+    /**
+     * Gets the bytes configured for Section Filter
+     */
+    public byte[] getFilterBytes() {
+        return mFilter;
+    }
+    /**
+     * Gets bit mask.
+     *
+     * <p>The bits in the bytes are used for filtering.
+     */
+    public byte[] getMask() {
+        return mMask;
+    }
+    /**
+     * Gets mode.
+     *
+     * <p>Do positive match at the bit position of the configured bytes when the bit at same
+     * position of the mode is 0.
+     * <p>Do negative match at the bit position of the configured bytes when the bit at same
+     * position of the mode is 1.
+     */
+    public byte[] getMode() {
+        return mMode;
+    }
+
+    /**
+     * Creates a builder for {@link SectionSettingsWithSectionBits}.
+     *
+     * @param context the context of the caller.
+     * @param mainType the filter main type.
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @NonNull
+    public static Builder builder(@NonNull Context context, @Filter.Type int mainType) {
+        TunerUtils.checkTunerPermission(context);
+        return new Builder(mainType);
+    }
+
+    /**
+     * Builder for {@link SectionSettingsWithSectionBits}.
+     */
+    public static class Builder extends Settings.Builder<Builder> {
+        private byte[] mFilter;
+        private byte[] mMask;
+        private byte[] mMode;
+
+        private Builder(int mainType) {
+            super(mainType);
+        }
+
+        /**
+         * Sets filter bytes.
+         */
+        @NonNull
+        public Builder setFilter(byte[] filter) {
+            mFilter = filter;
+            return this;
+        }
+        /**
+         * Sets bit mask.
+         */
+        @NonNull
+        public Builder setMask(byte[] mask) {
+            mMask = mask;
+            return this;
+        }
+        /**
+         * Sets mode.
+         */
+        @NonNull
+        public Builder setMode(byte[] mode) {
+            mMode = mode;
+            return this;
+        }
+
+        /**
+         * Builds a {@link SectionSettingsWithSectionBits} object.
+         */
+        @NonNull
+        public SectionSettingsWithSectionBits build() {
+            return new SectionSettingsWithSectionBits(mMainType, mFilter, mMask, mMode);
+        }
+
+        @Override
+        Builder self() {
+            return this;
+        }
     }
 }
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
index 0df1d73..0c9cd2b 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
@@ -16,15 +16,91 @@
 
 package android.media.tv.tuner.filter;
 
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.media.tv.tuner.TunerUtils;
+
 /**
  * Table information for Section Filter.
  * @hide
  */
 public class SectionSettingsWithTableInfo extends SectionSettings {
-    private int mTableId;
-    private int mVersion;
+    private final int mTableId;
+    private final int mVersion;
 
-    private SectionSettingsWithTableInfo(int mainType) {
+    private SectionSettingsWithTableInfo(int mainType, int tableId, int version) {
         super(mainType);
+        mTableId = tableId;
+        mVersion = version;
     }
+
+    /**
+     * Gets table ID.
+     */
+    public int getTableId() {
+        return mTableId;
+    }
+    /**
+     * Gets version.
+     */
+    public int getVersion() {
+        return mVersion;
+    }
+
+    /**
+     * Creates a builder for {@link SectionSettingsWithTableInfo}.
+     *
+     * @param context the context of the caller.
+     * @param mainType the filter main type.
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @NonNull
+    public static Builder builder(@NonNull Context context, @Filter.Type int mainType) {
+        TunerUtils.checkTunerPermission(context);
+        return new Builder(mainType);
+    }
+
+    /**
+     * Builder for {@link SectionSettingsWithTableInfo}.
+     */
+    public static class Builder extends Settings.Builder<Builder> {
+        private int mTableId;
+        private int mVersion;
+
+        private Builder(int mainType) {
+            super(mainType);
+        }
+
+        /**
+         * Sets table ID.
+         */
+        @NonNull
+        public Builder setTableId(int tableId) {
+            mTableId = tableId;
+            return this;
+        }
+        /**
+         * Sets version.
+         */
+        @NonNull
+        public Builder setVersion(int version) {
+            mVersion = version;
+            return this;
+        }
+
+        /**
+         * Builds a {@link SectionSettingsWithTableInfo} object.
+         */
+        @NonNull
+        public SectionSettingsWithTableInfo build() {
+            return new SectionSettingsWithTableInfo(mMainType, mTableId, mVersion);
+        }
+
+        @Override
+        Builder self() {
+            return this;
+        }
+    }
+
 }
diff --git a/media/java/android/media/tv/tuner/filter/Settings.java b/media/java/android/media/tv/tuner/filter/Settings.java
index 9155926..d697280 100644
--- a/media/java/android/media/tv/tuner/filter/Settings.java
+++ b/media/java/android/media/tv/tuner/filter/Settings.java
@@ -39,4 +39,20 @@
     public int getType() {
         return mType;
     }
+
+
+    /**
+     * Builder for {@link Settings}.
+     *
+     * @param <T> The subclass to be built.
+     * @hide
+     */
+    public abstract static class Builder<T extends Builder<T>> {
+        /* package */ final int mMainType;
+
+        /* package */ Builder(int mainType) {
+            mMainType = mainType;
+        }
+        /* package */ abstract T self();
+    }
 }
diff --git a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
index de8ee75..eb97fc0 100644
--- a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
@@ -16,21 +16,118 @@
 
 package android.media.tv.tuner.filter;
 
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.media.tv.tuner.TunerUtils;
+
 /**
  * Filter configuration for a TLV filter.
  * @hide
  */
 public class TlvFilterConfiguration extends FilterConfiguration {
-    private int mPacketType;
-    private boolean mIsCompressedIpPacket;
-    private boolean mPassthrough;
+    private final int mPacketType;
+    private final boolean mIsCompressedIpPacket;
+    private final boolean mPassthrough;
 
-    public TlvFilterConfiguration(Settings settings) {
+    public TlvFilterConfiguration(Settings settings, int packetType, boolean isCompressed,
+            boolean passthrough) {
         super(settings);
+        mPacketType = packetType;
+        mIsCompressedIpPacket = isCompressed;
+        mPassthrough = passthrough;
     }
 
     @Override
     public int getType() {
         return FilterConfiguration.FILTER_TYPE_TLV;
     }
+
+    /**
+     * Gets packet type.
+     */
+    @FilterConfiguration.PacketType
+    public int getPacketType() {
+        return mPacketType;
+    }
+    /**
+     * Checks whether the data is compressed IP packet.
+     *
+     * @return {@code true} if the filtered data is compressed IP packet; {@code false} otherwise.
+     */
+    public boolean isCompressedIpPacket() {
+        return mIsCompressedIpPacket;
+    }
+    /**
+     * Checks whether it's passthrough.
+     *
+     * @return {@code true} if the data from TLV subtype go to next filter directly;
+     *         {@code false} otherwise.
+     */
+    public boolean isPassthrough() {
+        return mPassthrough;
+    }
+
+    /**
+     * Creates a builder for {@link TlvFilterConfiguration}.
+     *
+     * @param context the context of the caller.
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @NonNull
+    public static Builder builder(@NonNull Context context) {
+        TunerUtils.checkTunerPermission(context);
+        return new Builder();
+    }
+
+    /**
+     * Builder for {@link TlvFilterConfiguration}.
+     */
+    public static class Builder extends FilterConfiguration.Builder<Builder> {
+        private int mPacketType;
+        private boolean mIsCompressedIpPacket;
+        private boolean mPassthrough;
+
+        private Builder() {
+        }
+
+        /**
+         * Sets packet type.
+         */
+        @NonNull
+        public Builder setPacketType(@FilterConfiguration.PacketType int packetType) {
+            mPacketType = packetType;
+            return this;
+        }
+        /**
+         * Sets whether the data is compressed IP packet.
+         */
+        @NonNull
+        public Builder setIsCompressedIpPacket(boolean isCompressedIpPacket) {
+            mIsCompressedIpPacket = isCompressedIpPacket;
+            return this;
+        }
+        /**
+         * Sets whether it's passthrough.
+         */
+        @NonNull
+        public Builder setPassthrough(boolean passthrough) {
+            mPassthrough = passthrough;
+            return this;
+        }
+
+        /**
+         * Builds a {@link TlvFilterConfiguration} object.
+         */
+        @NonNull
+        public TlvFilterConfiguration build() {
+            return new TlvFilterConfiguration(
+                    mSettings, mPacketType, mIsCompressedIpPacket, mPassthrough);
+        }
+
+        @Override
+        Builder self() {
+            return this;
+        }
+    }
 }
diff --git a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
index fa4dd72..1b8485e 100644
--- a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
+++ b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
@@ -16,12 +16,8 @@
 
 package android.media.tv.tuner.filter;
 
-import android.annotation.IntDef;
 import android.media.tv.tuner.Tuner.Filter;
-import android.media.tv.tuner.TunerConstants;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 
 /**
  * Filter event sent from {@link Filter} objects for TS record data.
@@ -29,47 +25,17 @@
  * @hide
  */
 public class TsRecordEvent extends FilterEvent {
-    /**
-     * @hide
-     */
-    @IntDef(flag = true, value = {
-            TunerConstants.TS_INDEX_FIRST_PACKET,
-            TunerConstants.TS_INDEX_PAYLOAD_UNIT_START_INDICATOR,
-            TunerConstants.TS_INDEX_CHANGE_TO_NOT_SCRAMBLED,
-            TunerConstants.TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED,
-            TunerConstants.TS_INDEX_CHANGE_TO_ODD_SCRAMBLED,
-            TunerConstants.TS_INDEX_DISCONTINUITY_INDICATOR,
-            TunerConstants.TS_INDEX_RANDOM_ACCESS_INDICATOR,
-            TunerConstants.TS_INDEX_PRIORITY_INDICATOR,
-            TunerConstants.TS_INDEX_PCR_FLAG,
-            TunerConstants.TS_INDEX_OPCR_FLAG,
-            TunerConstants.TS_INDEX_SPLICING_POINT_FLAG,
-            TunerConstants.TS_INDEX_PRIVATE_DATA,
-            TunerConstants.TS_INDEX_ADAPTATION_EXTENSION_FLAG,
-            TunerConstants.SC_INDEX_I_FRAME,
-            TunerConstants.SC_INDEX_P_FRAME,
-            TunerConstants.SC_INDEX_B_FRAME,
-            TunerConstants.SC_INDEX_SEQUENCE,
-            TunerConstants.SC_HEVC_INDEX_SPS,
-            TunerConstants.SC_HEVC_INDEX_AUD,
-            TunerConstants.SC_HEVC_INDEX_SLICE_CE_BLA_W_LP,
-            TunerConstants.SC_HEVC_INDEX_SLICE_BLA_W_RADL,
-            TunerConstants.SC_HEVC_INDEX_SLICE_BLA_N_LP,
-            TunerConstants.SC_HEVC_INDEX_SLICE_IDR_W_RADL,
-            TunerConstants.SC_HEVC_INDEX_SLICE_IDR_N_LP,
-            TunerConstants.SC_HEVC_INDEX_SLICE_TRAIL_CRA,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface IndexMask {}
 
     private final int mPid;
-    private final int mIndexMask;
+    private final int mTsIndexMask;
+    private final int mScIndexMask;
     private final long mByteNumber;
 
     // This constructor is used by JNI code only
-    private TsRecordEvent(int pid, int indexMask, long byteNumber) {
+    private TsRecordEvent(int pid, int tsIndexMask, int scIndexMask, long byteNumber) {
         mPid = pid;
-        mIndexMask = indexMask;
+        mTsIndexMask = tsIndexMask;
+        mScIndexMask = scIndexMask;
         mByteNumber = byteNumber;
     }
 
@@ -81,13 +47,20 @@
     }
 
     /**
-     * Gets index mask.
-     *
-     * <p>The index type is one of TS, SC, and SC-HEVC, and is set when configuring the filter.
+     * Gets TS index mask.
      */
-    @IndexMask
-    public int getIndexMask() {
-        return mIndexMask;
+    @RecordSettings.TsIndexMask
+    public int getTsIndexMask() {
+        return mTsIndexMask;
+    }
+    /**
+     * Gets SC index mask.
+     *
+     * <p>The index type is SC or SC-HEVC, and is set when configuring the filter.
+     */
+    @RecordSettings.ScIndexMask
+    public int getScIndexMask() {
+        return mScIndexMask;
     }
 
     /**
diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java
index d92c786..aa64df5 100644
--- a/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java
+++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java
@@ -17,26 +17,31 @@
 package android.media.tv.tuner.frontend;
 
 /**
- * Analog Capabilities.
+ * Capabilities for analog tuners.
+ *
  * @hide
  */
 public class AnalogFrontendCapabilities extends FrontendCapabilities {
+    @AnalogFrontendSettings.SignalType
     private final int mTypeCap;
+    @AnalogFrontendSettings.SifStandard
     private final int mSifStandardCap;
 
-    AnalogFrontendCapabilities(int typeCap, int sifStandardCap) {
+    // Called by JNI code.
+    private AnalogFrontendCapabilities(int typeCap, int sifStandardCap) {
         mTypeCap = typeCap;
         mSifStandardCap = sifStandardCap;
     }
+
     /**
-     * Gets type capability.
+     * Gets analog signal type capability.
      */
     @AnalogFrontendSettings.SignalType
-    public int getTypeCapability() {
+    public int getSignalTypeCapability() {
         return mTypeCap;
     }
     /**
-     * Gets SIF standard capability.
+     * Gets Standard Interchange Format (SIF) capability.
      */
     @AnalogFrontendSettings.SifStandard
     public int getSifStandardCapability() {
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
index 5b3bffc4..44dbcc0 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
@@ -112,31 +112,31 @@
     public @interface Rolloff {}
 
     /**
-     * Roll Off undefined.
+     * Rolloff range undefined.
      */
     public static final int ROLLOFF_UNDEFINED = Constants.FrontendDvbsRolloff.UNDEFINED;
     /**
-     * Roll Off 0_35.
+     * Rolloff range 0,35.
      */
     public static final int ROLLOFF_0_35 = Constants.FrontendDvbsRolloff.ROLLOFF_0_35;
     /**
-     * Roll Off 0_25.
+     * Rolloff range 0,25.
      */
     public static final int ROLLOFF_0_25 = Constants.FrontendDvbsRolloff.ROLLOFF_0_25;
     /**
-     * Roll Off 0_2.
+     * Rolloff range 0,20.
      */
     public static final int ROLLOFF_0_20 = Constants.FrontendDvbsRolloff.ROLLOFF_0_20;
     /**
-     * Roll Off 0_15.
+     * Rolloff range 0,15.
      */
     public static final int ROLLOFF_0_15 = Constants.FrontendDvbsRolloff.ROLLOFF_0_15;
     /**
-     * Roll Off 0_1.
+     * Rolloff range 0,10.
      */
     public static final int ROLLOFF_0_10 = Constants.FrontendDvbsRolloff.ROLLOFF_0_10;
     /**
-     * Roll Off 0_5.
+     * Rolloff range 0,5.
      */
     public static final int ROLLOFF_0_5 = Constants.FrontendDvbsRolloff.ROLLOFF_0_5;
 
@@ -188,6 +188,25 @@
      */
     public static final int STANDARD_S2X = Constants.FrontendDvbsStandard.S2X;
 
+    /** @hide */
+    @IntDef(prefix = "VCM_MODE_",
+            value = {VCM_MODE_UNDEFINED, VCM_MODE_AUTO, VCM_MODE_MANUAL})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface VcmMode {}
+
+    /**
+     * VCM mode undefined.
+     */
+    public static final int VCM_MODE_UNDEFINED = Constants.FrontendDvbsVcmMode.UNDEFINED;
+    /**
+     * Auto VCM mode.
+     */
+    public static final int VCM_MODE_AUTO = Constants.FrontendDvbsVcmMode.AUTO;
+    /**
+     * Manual VCM mode.
+     */
+    public static final int VCM_MODE_MANUAL = Constants.FrontendDvbsVcmMode.MANUAL;
+
 
     private final int mModulation;
     private final DvbsCodeRate mCoderate;
@@ -196,9 +215,10 @@
     private final int mPilot;
     private final int mInputStreamId;
     private final int mStandard;
+    private final int mVcmMode;
 
     private DvbsFrontendSettings(int frequency, int modulation, DvbsCodeRate coderate,
-            int symbolRate, int rolloff, int pilot, int inputStreamId, int standard) {
+            int symbolRate, int rolloff, int pilot, int inputStreamId, int standard, int vcm) {
         super(frequency);
         mModulation = modulation;
         mCoderate = coderate;
@@ -207,6 +227,7 @@
         mPilot = pilot;
         mInputStreamId = inputStreamId;
         mStandard = standard;
+        mVcmMode = vcm;
     }
 
     /**
@@ -256,6 +277,13 @@
     public int getStandard() {
         return mStandard;
     }
+    /**
+     * Gets VCM mode.
+     */
+    @VcmMode
+    public int getVcmMode() {
+        return mVcmMode;
+    }
 
     /**
      * Creates a builder for {@link DvbsFrontendSettings}.
@@ -280,6 +308,7 @@
         private int mPilot;
         private int mInputStreamId;
         private int mStandard;
+        private int mVcmMode;
 
         private Builder() {
         }
@@ -340,6 +369,14 @@
             mStandard = standard;
             return this;
         }
+        /**
+         * Sets VCM mode.
+         */
+        @NonNull
+        public Builder setVcmMode(@VcmMode int vcm) {
+            mVcmMode = vcm;
+            return this;
+        }
 
         /**
          * Builds a {@link DvbsFrontendSettings} object.
@@ -347,7 +384,7 @@
         @NonNull
         public DvbsFrontendSettings build() {
             return new DvbsFrontendSettings(mFrequency, mModulation, mCoderate, mSymbolRate,
-                    mRolloff, mPilot, mInputStreamId, mStandard);
+                    mRolloff, mPilot, mInputStreamId, mStandard, mVcmMode);
         }
 
         @Override
diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
index f0469b7..9a82de0 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
@@ -110,7 +110,7 @@
      */
     public static final int BANDWIDTH_5MHZ = Constants.FrontendDvbtBandwidth.BANDWIDTH_5MHZ;
     /**
-     * 1.7 MHz bandwidth.
+     * 1,7 MHz bandwidth.
      */
     public static final int BANDWIDTH_1_7MHZ = Constants.FrontendDvbtBandwidth.BANDWIDTH_1_7MHZ;
     /**
@@ -232,39 +232,39 @@
      */
     public static final int CODERATE_AUTO = Constants.FrontendDvbtCoderate.AUTO;
     /**
-     * 1_2 code rate.
+     * 1/2 code rate.
      */
     public static final int CODERATE_1_2 = Constants.FrontendDvbtCoderate.CODERATE_1_2;
     /**
-     * 2_3 code rate.
+     * 2/3 code rate.
      */
     public static final int CODERATE_2_3 = Constants.FrontendDvbtCoderate.CODERATE_2_3;
     /**
-     * 3_4 code rate.
+     * 3/4 code rate.
      */
     public static final int CODERATE_3_4 = Constants.FrontendDvbtCoderate.CODERATE_3_4;
     /**
-     * 5_6 code rate.
+     * 5/6 code rate.
      */
     public static final int CODERATE_5_6 = Constants.FrontendDvbtCoderate.CODERATE_5_6;
     /**
-     * 7_8 code rate.
+     * 7/8 code rate.
      */
     public static final int CODERATE_7_8 = Constants.FrontendDvbtCoderate.CODERATE_7_8;
     /**
-     * 4_5 code rate.
+     * 4/5 code rate.
      */
     public static final int CODERATE_3_5 = Constants.FrontendDvbtCoderate.CODERATE_3_5;
     /**
-     * 4_5 code rate.
+     * 4/5 code rate.
      */
     public static final int CODERATE_4_5 = Constants.FrontendDvbtCoderate.CODERATE_4_5;
     /**
-     * 6_7 code rate.
+     * 6/7 code rate.
      */
     public static final int CODERATE_6_7 = Constants.FrontendDvbtCoderate.CODERATE_6_7;
     /**
-     * 8_9 code rate.
+     * 8/9 code rate.
      */
     public static final int CODERATE_8_9 = Constants.FrontendDvbtCoderate.CODERATE_8_9;
 
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java
index 7350bc0..e4f66b8 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java
@@ -17,7 +17,8 @@
 package android.media.tv.tuner.frontend;
 
 /**
- * Frontend Capabilities.
+ * Frontend capabilities.
+ *
  * @hide
  */
 public abstract class FrontendCapabilities {
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
index 99e8dd2..360c84a 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
@@ -16,77 +16,98 @@
 
 package android.media.tv.tuner.frontend;
 
+import android.annotation.NonNull;
 import android.media.tv.tuner.frontend.FrontendSettings.Type;
+import android.media.tv.tuner.frontend.FrontendStatus.FrontendStatusType;
+import android.util.Range;
 
 /**
- * Frontend info.
+ * This class is used to specify meta information of a frontend.
+ *
  * @hide
  */
 public class FrontendInfo {
     private final int mId;
     private final int mType;
-    private final int mMinFrequency;
-    private final int mMaxFrequency;
-    private final int mMinSymbolRate;
-    private final int mMaxSymbolRate;
+    private final Range<Integer> mFrequencyRange;
+    private final Range<Integer> mSymbolRateRange;
     private final int mAcquireRange;
     private final int mExclusiveGroupId;
     private final int[] mStatusCaps;
     private final FrontendCapabilities mFrontendCap;
 
-    FrontendInfo(int id, int type, int minFrequency, int maxFrequency, int minSymbolRate,
+    private FrontendInfo(int id, int type, int minFrequency, int maxFrequency, int minSymbolRate,
             int maxSymbolRate, int acquireRange, int exclusiveGroupId, int[] statusCaps,
             FrontendCapabilities frontendCap) {
         mId = id;
         mType = type;
-        mMinFrequency = minFrequency;
-        mMaxFrequency = maxFrequency;
-        mMinSymbolRate = minSymbolRate;
-        mMaxSymbolRate = maxSymbolRate;
+        mFrequencyRange = new Range<>(minFrequency, maxFrequency);
+        mSymbolRateRange = new Range<>(minSymbolRate, maxSymbolRate);
         mAcquireRange = acquireRange;
         mExclusiveGroupId = exclusiveGroupId;
         mStatusCaps = statusCaps;
         mFrontendCap = frontendCap;
     }
 
-    /** Gets frontend ID. */
+    /**
+     * Gets frontend ID.
+     */
     public int getId() {
         return mId;
     }
-    /** Gets frontend type. */
+    /**
+     * Gets frontend type.
+     */
     @Type
     public int getType() {
         return mType;
     }
-    /** Gets min frequency. */
-    public int getMinFrequency() {
-        return mMinFrequency;
+
+    /**
+     * Gets supported frequency range in Hz.
+     */
+    @NonNull
+    public Range<Integer> getFrequencyRange() {
+        return mFrequencyRange;
     }
-    /** Gets max frequency. */
-    public int getMaxFrequency() {
-        return mMaxFrequency;
+
+    /**
+     * Gets symbol rate range in symbols per second.
+     */
+    @NonNull
+    public Range<Integer> getSymbolRateRange() {
+        return mSymbolRateRange;
     }
-    /** Gets min symbol rate. */
-    public int getMinSymbolRate() {
-        return mMinSymbolRate;
-    }
-    /** Gets max symbol rate. */
-    public int getMaxSymbolRate() {
-        return mMaxSymbolRate;
-    }
-    /** Gets acquire range. */
+
+    /**
+     * Gets acquire range in Hz.
+     *
+     * <p>The maximum frequency difference the frontend can detect.
+     */
     public int getAcquireRange() {
         return mAcquireRange;
     }
-    /** Gets exclusive group ID. */
+    /**
+     * Gets exclusive group ID.
+     *
+     * <p>Frontends with the same exclusive group ID indicates they can't function at same time. For
+     * instance, they share some hardware modules.
+     */
     public int getExclusiveGroupId() {
         return mExclusiveGroupId;
     }
-    /** Gets status capabilities. */
+    /**
+     * Gets status capabilities.
+     *
+     * @return An array of supported status types.
+     */
+    @FrontendStatusType
     public int[] getStatusCapabilities() {
         return mStatusCaps;
     }
-    /** Gets frontend capability. */
+    /**
+     * Gets frontend capabilities.
+     */
     public FrontendCapabilities getFrontendCapability() {
         return mFrontendCap;
     }
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index f973d88..088adff 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -16,11 +16,15 @@
 
 package android.media.tv.tuner.frontend;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.hardware.tv.tuner.V1_0.Constants;
 import android.media.tv.tuner.Lnb;
-import android.media.tv.tuner.TunerConstants;
 import android.media.tv.tuner.TunerConstants.FrontendInnerFec;
 import android.media.tv.tuner.TunerConstants.FrontendModulation;
-import android.media.tv.tuner.TunerConstants.FrontendStatusType;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Frontend status
@@ -29,204 +33,409 @@
  */
 public class FrontendStatus {
 
-    private final int mType;
-    private final Object mValue;
+    /** @hide */
+    @IntDef({FRONTEND_STATUS_TYPE_DEMOD_LOCK, FRONTEND_STATUS_TYPE_SNR, FRONTEND_STATUS_TYPE_BER,
+            FRONTEND_STATUS_TYPE_PER, FRONTEND_STATUS_TYPE_PRE_BER,
+            FRONTEND_STATUS_TYPE_SIGNAL_QUALITY, FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH,
+            FRONTEND_STATUS_TYPE_SYMBOL_RATE, FRONTEND_STATUS_TYPE_FEC,
+            FRONTEND_STATUS_TYPE_MODULATION, FRONTEND_STATUS_TYPE_SPECTRAL,
+            FRONTEND_STATUS_TYPE_LNB_VOLTAGE, FRONTEND_STATUS_TYPE_PLP_ID,
+            FRONTEND_STATUS_TYPE_EWBS, FRONTEND_STATUS_TYPE_AGC, FRONTEND_STATUS_TYPE_LNA,
+            FRONTEND_STATUS_TYPE_LAYER_ERROR, FRONTEND_STATUS_TYPE_VBER_CN,
+            FRONTEND_STATUS_TYPE_LBER_CN, FRONTEND_STATUS_TYPE_XER_CN, FRONTEND_STATUS_TYPE_MER,
+            FRONTEND_STATUS_TYPE_FREQ_OFFSET, FRONTEND_STATUS_TYPE_HIERARCHY,
+            FRONTEND_STATUS_TYPE_RF_LOCK, FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendStatusType {}
 
-    private FrontendStatus(int type, Object value) {
-        mType = type;
-        mValue = value;
+    /**
+     * Lock status for Demod.
+     */
+    public static final int FRONTEND_STATUS_TYPE_DEMOD_LOCK =
+            Constants.FrontendStatusType.DEMOD_LOCK;
+    /**
+     * Signal to Noise Ratio.
+     */
+    public static final int FRONTEND_STATUS_TYPE_SNR = Constants.FrontendStatusType.SNR;
+    /**
+     * Bit Error Ratio.
+     */
+    public static final int FRONTEND_STATUS_TYPE_BER = Constants.FrontendStatusType.BER;
+    /**
+     * Packages Error Ratio.
+     */
+    public static final int FRONTEND_STATUS_TYPE_PER = Constants.FrontendStatusType.PER;
+    /**
+     * Bit Error Ratio before FEC.
+     */
+    public static final int FRONTEND_STATUS_TYPE_PRE_BER = Constants.FrontendStatusType.PRE_BER;
+    /**
+     * Signal Quality (0..100). Good data over total data in percent can be
+     * used as a way to present Signal Quality.
+     */
+    public static final int FRONTEND_STATUS_TYPE_SIGNAL_QUALITY =
+            Constants.FrontendStatusType.SIGNAL_QUALITY;
+    /**
+     * Signal Strength.
+     */
+    public static final int FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH =
+            Constants.FrontendStatusType.SIGNAL_STRENGTH;
+    /**
+     * Symbol Rate in symbols per second.
+     */
+    public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE =
+            Constants.FrontendStatusType.SYMBOL_RATE;
+    /**
+     * Forward Error Correction Type.
+     */
+    public static final int FRONTEND_STATUS_TYPE_FEC = Constants.FrontendStatusType.FEC;
+    /**
+     * Modulation Type.
+     */
+    public static final int FRONTEND_STATUS_TYPE_MODULATION =
+            Constants.FrontendStatusType.MODULATION;
+    /**
+     * Spectral Inversion Type.
+     */
+    public static final int FRONTEND_STATUS_TYPE_SPECTRAL = Constants.FrontendStatusType.SPECTRAL;
+    /**
+     * LNB Voltage.
+     */
+    public static final int FRONTEND_STATUS_TYPE_LNB_VOLTAGE =
+            Constants.FrontendStatusType.LNB_VOLTAGE;
+    /**
+     * Physical Layer Pipe ID.
+     */
+    public static final int FRONTEND_STATUS_TYPE_PLP_ID = Constants.FrontendStatusType.PLP_ID;
+    /**
+     * Status for Emergency Warning Broadcasting System.
+     */
+    public static final int FRONTEND_STATUS_TYPE_EWBS = Constants.FrontendStatusType.EWBS;
+    /**
+     * Automatic Gain Control.
+     */
+    public static final int FRONTEND_STATUS_TYPE_AGC = Constants.FrontendStatusType.AGC;
+    /**
+     * Low Noise Amplifier.
+     */
+    public static final int FRONTEND_STATUS_TYPE_LNA = Constants.FrontendStatusType.LNA;
+    /**
+     * Error status by layer.
+     */
+    public static final int FRONTEND_STATUS_TYPE_LAYER_ERROR =
+            Constants.FrontendStatusType.LAYER_ERROR;
+    /**
+     * CN value by VBER.
+     */
+    public static final int FRONTEND_STATUS_TYPE_VBER_CN = Constants.FrontendStatusType.VBER_CN;
+    /**
+     * CN value by LBER.
+     */
+    public static final int FRONTEND_STATUS_TYPE_LBER_CN = Constants.FrontendStatusType.LBER_CN;
+    /**
+     * CN value by XER.
+     */
+    public static final int FRONTEND_STATUS_TYPE_XER_CN = Constants.FrontendStatusType.XER_CN;
+    /**
+     * Modulation Error Ratio.
+     */
+    public static final int FRONTEND_STATUS_TYPE_MER = Constants.FrontendStatusType.MER;
+    /**
+     * Difference between tuning frequency and actual locked frequency.
+     */
+    public static final int FRONTEND_STATUS_TYPE_FREQ_OFFSET =
+            Constants.FrontendStatusType.FREQ_OFFSET;
+    /**
+     * Hierarchy for DVBT.
+     */
+    public static final int FRONTEND_STATUS_TYPE_HIERARCHY = Constants.FrontendStatusType.HIERARCHY;
+    /**
+     * Lock status for RF.
+     */
+    public static final int FRONTEND_STATUS_TYPE_RF_LOCK = Constants.FrontendStatusType.RF_LOCK;
+    /**
+     * PLP information in a frequency band for ATSC-3.0 frontend.
+     */
+    public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO =
+            Constants.FrontendStatusType.ATSC3_PLP_INFO;
+
+
+    private Boolean mIsDemodLocked;
+    private Integer mSnr;
+    private Integer mBer;
+    private Integer mPer;
+    private Integer mPerBer;
+    private Integer mSignalQuality;
+    private Integer mSignalStrength;
+    private Integer mSymbolRate;
+    private Long mInnerFec;
+    private Integer mModulation;
+    private Integer mInversion;
+    private Integer mLnbVoltage;
+    private Integer mPlpId;
+    private Boolean mIsEwbs;
+    private Integer mAgc;
+    private Boolean mIsLnaOn;
+    private boolean[] mIsLayerErrors;
+    private Integer mVberCn;
+    private Integer mLberCn;
+    private Integer mXerCn;
+    private Integer mMer;
+    private Integer mFreqOffset;
+    private Integer mHierarchy;
+    private Boolean mIsRfLocked;
+    private Atsc3PlpInfo[] mPlpInfo;
+
+    // Constructed and fields set by JNI code.
+    private FrontendStatus() {
     }
 
-    /** Gets frontend status type. */
-    @FrontendStatusType
-    public int getStatusType() {
-        return mType;
-    }
-    /** Lock status for Demod in True/False. */
-    public boolean getIsDemodLocked() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_DEMOD_LOCK) {
+    /**
+     * Lock status for Demod.
+     */
+    public boolean isDemodLocked() {
+        if (mIsDemodLocked == null) {
             throw new IllegalStateException();
         }
-        return (Boolean) mValue;
-    }
-    /** SNR value measured by 0.001 dB. */
-    public int getSnr() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_SNR) {
-            throw new IllegalStateException();
-        }
-        return (int) mValue;
-    }
-    /** The number of error bit per 1 billion bits. */
-    public int getBer() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_BER) {
-            throw new IllegalStateException();
-        }
-        return (int) mValue;
-    }
-    /** The number of error package per 1 billion packages. */
-    public int getPer() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_PER) {
-            throw new IllegalStateException();
-        }
-        return (int) mValue;
-    }
-    /** The number of error bit per 1 billion bits before FEC. */
-    public int getPerBer() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_PRE_BER) {
-            throw new IllegalStateException();
-        }
-        return (int) mValue;
-    }
-    /** Signal Quality in percent. */
-    public int getSignalQuality() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_SIGNAL_QUALITY) {
-            throw new IllegalStateException();
-        }
-        return (int) mValue;
-    }
-    /** Signal Strength measured by 0.001 dBm. */
-    public int getSignalStrength() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH) {
-            throw new IllegalStateException();
-        }
-        return (int) mValue;
-    }
-    /**  Symbols per second. */
-    public int getSymbolRate() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_SYMBOL_RATE) {
-            throw new IllegalStateException();
-        }
-        return (int) mValue;
+        return mIsDemodLocked;
     }
     /**
-     *  Inner Forward Error Correction type as specified in ETSI EN 300 468 V1.15.1
+     * Gets Signal to Noise Ratio in thousandths of a deciBel (0.001dB).
+     */
+    public int getSnr() {
+        if (mSnr == null) {
+            throw new IllegalStateException();
+        }
+        return mSnr;
+    }
+    /**
+     * Gets Bit Error Ratio.
+     *
+     * <p>The number of error bit per 1 billion bits.
+     */
+    public int getBer() {
+        if (mBer == null) {
+            throw new IllegalStateException();
+        }
+        return mBer;
+    }
+
+    /**
+     * Gets Packages Error Ratio.
+     *
+     * <p>The number of error package per 1 billion packages.
+     */
+    public int getPer() {
+        if (mPer == null) {
+            throw new IllegalStateException();
+        }
+        return mPer;
+    }
+    /**
+     * Gets Bit Error Ratio before Forward Error Correction (FEC).
+     *
+     * <p>The number of error bit per 1 billion bits before FEC.
+     */
+    public int getPerBer() {
+        if (mPerBer == null) {
+            throw new IllegalStateException();
+        }
+        return mPerBer;
+    }
+    /**
+     * Gets Signal Quality in percent.
+     */
+    public int getSignalQuality() {
+        if (mSignalQuality == null) {
+            throw new IllegalStateException();
+        }
+        return mSignalQuality;
+    }
+    /**
+     * Gets Signal Strength in thousandths of a dBm (0.001dBm).
+     */
+    public int getSignalStrength() {
+        if (mSignalStrength == null) {
+            throw new IllegalStateException();
+        }
+        return mSignalStrength;
+    }
+    /**
+     * Gets symbol rate in symbols per second.
+     */
+    public int getSymbolRate() {
+        if (mSymbolRate == null) {
+            throw new IllegalStateException();
+        }
+        return mSymbolRate;
+    }
+    /**
+     *  Gets Inner Forward Error Correction type as specified in ETSI EN 300 468 V1.15.1
      *  and ETSI EN 302 307-2 V1.1.1.
      */
     @FrontendInnerFec
     public long getFec() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_FEC) {
+        if (mInnerFec == null) {
             throw new IllegalStateException();
         }
-        return (long) mValue;
+        return mInnerFec;
     }
-    /** Modulation */
+    /**
+     * Gets modulation.
+     */
     @FrontendModulation
     public int getModulation() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_MODULATION) {
+        if (mModulation == null) {
             throw new IllegalStateException();
         }
-        return (int) mValue;
+        return mModulation;
     }
-    /** Spectral Inversion for DVBC. */
+    /**
+     * Gets Spectral Inversion for DVBC.
+     */
     @DvbcFrontendSettings.SpectralInversion
     public int getSpectralInversion() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_SPECTRAL) {
+        if (mInversion == null) {
             throw new IllegalStateException();
         }
-        return (int) mValue;
+        return mInversion;
     }
-    /** Power Voltage Type for LNB. */
+    /**
+     * Gets Power Voltage Type for LNB.
+     */
     @Lnb.Voltage
     public int getLnbVoltage() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_LNB_VOLTAGE) {
+        if (mLnbVoltage == null) {
             throw new IllegalStateException();
         }
-        return (int) mValue;
+        return mLnbVoltage;
     }
-    /** PLP ID */
-    public byte getPlpId() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_PLP_ID) {
+    /**
+     * Gets Physical Layer Pipe ID.
+     */
+    public int getPlpId() {
+        if (mPlpId == null) {
             throw new IllegalStateException();
         }
-        return (byte) mValue;
+        return mPlpId;
     }
-    /** Emergency Warning Broadcasting System */
-    public boolean getIsEwbs() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_EWBS) {
+    /**
+     * Checks whether it's Emergency Warning Broadcasting System
+     */
+    public boolean isEwbs() {
+        if (mIsEwbs == null) {
             throw new IllegalStateException();
         }
-        return (Boolean) mValue;
+        return mIsEwbs;
     }
-    /** AGC value is normalized from 0 to 255. */
-    public byte getAgc() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_AGC) {
+    /**
+     * Gets Automatic Gain Control value which is normalized from 0 to 255.
+     */
+    public int getAgc() {
+        if (mAgc == null) {
             throw new IllegalStateException();
         }
-        return (byte) mValue;
+        return mAgc;
     }
-    /** LNA(Low Noise Amplifier) is on or not. */
-    public boolean getLnaOn() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_LNA) {
+    /**
+     * Checks LNA (Low Noise Amplifier) is on or not.
+     */
+    public boolean isLnaOn() {
+        if (mIsLnaOn == null) {
             throw new IllegalStateException();
         }
-        return (Boolean) mValue;
+        return mIsLnaOn;
     }
-    /** Error status by layer. */
-    public boolean[] getIsLayerError() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_LAYER_ERROR) {
+    /**
+     * Gets Error status by layer.
+     */
+    @NonNull
+    public boolean[] getLayerErrors() {
+        if (mIsLayerErrors == null) {
             throw new IllegalStateException();
         }
-        return (boolean[]) mValue;
+        return mIsLayerErrors;
     }
-    /** CN value by VBER measured by 0.001 dB. */
+    /**
+     * Gets CN value by VBER in thousandths of a deciBel (0.001dB).
+     */
     public int getVberCn() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_VBER_CN) {
+        if (mVberCn == null) {
             throw new IllegalStateException();
         }
-        return (int) mValue;
+        return mVberCn;
     }
-    /** CN value by LBER measured by 0.001 dB. */
+    /**
+     * Gets CN value by LBER in thousandths of a deciBel (0.001dB).
+     */
     public int getLberCn() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_LBER_CN) {
+        if (mLberCn == null) {
             throw new IllegalStateException();
         }
-        return (int) mValue;
+        return mLberCn;
     }
-    /** CN value by XER measured by 0.001 dB. */
+    /**
+     * Gets CN value by XER in thousandths of a deciBel (0.001dB).
+     */
     public int getXerCn() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_XER_CN) {
+        if (mXerCn == null) {
             throw new IllegalStateException();
         }
-        return (int) mValue;
+        return mXerCn;
     }
-    /** MER value measured by 0.001 dB. */
+    /**
+     * Gets Modulation Error Ratio in thousandths of a deciBel (0.001dB).
+     */
     public int getMer() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_MER) {
+        if (mMer == null) {
             throw new IllegalStateException();
         }
-        return (int) mValue;
+        return mMer;
     }
-    /** Frequency difference in Hertz. */
+    /**
+     * Gets frequency difference in Hz.
+     *
+     * <p>Difference between tuning frequency and actual locked frequency.
+     */
     public int getFreqOffset() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_FREQ_OFFSET) {
+        if (mFreqOffset == null) {
             throw new IllegalStateException();
         }
-        return (int) mValue;
+        return mFreqOffset;
     }
-    /** Hierarchy Type for DVBT. */
+    /**
+     * Gets hierarchy Type for DVBT.
+     */
     @DvbtFrontendSettings.Hierarchy
     public int getHierarchy() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_HIERARCHY) {
+        if (mHierarchy == null) {
             throw new IllegalStateException();
         }
-        return (int) mValue;
+        return mHierarchy;
     }
-    /** Lock status for RF. */
-    public boolean getIsRfLock() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_RF_LOCK) {
+    /**
+     * Gets lock status for RF.
+     */
+    public boolean isRfLock() {
+        if (mIsRfLocked == null) {
             throw new IllegalStateException();
         }
-        return (Boolean) mValue;
+        return mIsRfLocked;
     }
-    /** A list of PLP status for tuned PLPs for ATSC3 frontend. */
+    /**
+     * Gets an array of PLP status for tuned PLPs for ATSC3 frontend.
+     */
+    @NonNull
     public Atsc3PlpInfo[] getAtsc3PlpInfo() {
-        if (mType != TunerConstants.FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO) {
+        if (mPlpInfo == null) {
             throw new IllegalStateException();
         }
-        return (Atsc3PlpInfo[]) mValue;
+        return mPlpInfo;
     }
 
-    /** Status for each tuning PLPs. */
+    /**
+     * Status for each tuning Physical Layer Pipes.
+     */
     public static class Atsc3PlpInfo {
         private final int mPlpId;
         private final boolean mIsLock;
@@ -238,17 +447,20 @@
             mUec = uec;
         }
 
-        /** Gets PLP IDs. */
+        /**
+         * Gets Physical Layer Pipe ID.
+         */
         public int getPlpId() {
             return mPlpId;
         }
-        /** Gets Demod Lock/Unlock status of this particular PLP. */
-        public boolean getIsLock() {
+        /**
+         * Gets Demod Lock/Unlock status of this particular PLP.
+         */
+        public boolean isLock() {
             return mIsLock;
         }
         /**
-         * Gets Uncorrectable Error Counts (UEC) of this particular PLP since last tune
-         * operation.
+         * Gets Uncorrectable Error Counts (UEC) of this particular PLP since last tune operation.
          */
         public int getUec() {
             return mUec;
diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
index 7e6f188..a83d771 100644
--- a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
@@ -87,47 +87,47 @@
      */
     public static final int CODERATE_AUTO = Constants.FrontendIsdbs3Coderate.AUTO;
     /**
-     * 1_3 code rate.
+     * 1/3 code rate.
      */
     public static final int CODERATE_1_3 = Constants.FrontendIsdbs3Coderate.CODERATE_1_3;
     /**
-     * 2_5 code rate.
+     * 2/5 code rate.
      */
     public static final int CODERATE_2_5 = Constants.FrontendIsdbs3Coderate.CODERATE_2_5;
     /**
-     * 1_2 code rate.
+     * 1/2 code rate.
      */
     public static final int CODERATE_1_2 = Constants.FrontendIsdbs3Coderate.CODERATE_1_2;
     /**
-     * 3_5 code rate.
+     * 3/5 code rate.
      */
     public static final int CODERATE_3_5 = Constants.FrontendIsdbs3Coderate.CODERATE_3_5;
     /**
-     * 2_3 code rate.
+     * 2/3 code rate.
      */
     public static final int CODERATE_2_3 = Constants.FrontendIsdbs3Coderate.CODERATE_2_3;
     /**
-     * 3_4 code rate.
+     * 3/4 code rate.
      */
     public static final int CODERATE_3_4 = Constants.FrontendIsdbs3Coderate.CODERATE_3_4;
     /**
-     * 7_9 code rate.
+     * 7/9 code rate.
      */
     public static final int CODERATE_7_9 = Constants.FrontendIsdbs3Coderate.CODERATE_7_9;
     /**
-     * 4_5 code rate.
+     * 4/5 code rate.
      */
     public static final int CODERATE_4_5 = Constants.FrontendIsdbs3Coderate.CODERATE_4_5;
     /**
-     * 5_6 code rate.
+     * 5/6 code rate.
      */
     public static final int CODERATE_5_6 = Constants.FrontendIsdbs3Coderate.CODERATE_5_6;
     /**
-     * 7_8 code rate.
+     * 7/8 code rate.
      */
     public static final int CODERATE_7_8 = Constants.FrontendIsdbs3Coderate.CODERATE_7_8;
     /**
-     * 9_10 code rate.
+     * 9/10 code rate.
      */
     public static final int CODERATE_9_10 = Constants.FrontendIsdbs3Coderate.CODERATE_9_10;
 
@@ -138,11 +138,11 @@
     public @interface Rolloff {}
 
     /**
-     * Roll off type undefined.
+     * Rolloff type undefined.
      */
     public static final int ROLLOFF_UNDEFINED = Constants.FrontendIsdbs3Rolloff.UNDEFINED;
     /**
-     * 0.03 roll off type.
+     * 0,03 Rolloff.
      */
     public static final int ROLLOFF_0_03 = Constants.FrontendIsdbs3Rolloff.ROLLOFF_0_03;
 
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
index fe100f8..bb809bf 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
@@ -96,23 +96,23 @@
      */
     public static final int CODERATE_AUTO = Constants.FrontendIsdbsCoderate.AUTO;
     /**
-     * 1_2 code rate.
+     * 1/2 code rate.
      */
     public static final int CODERATE_1_2 = Constants.FrontendIsdbsCoderate.CODERATE_1_2;
     /**
-     * 2_3 code rate.
+     * 2/3 code rate.
      */
     public static final int CODERATE_2_3 = Constants.FrontendIsdbsCoderate.CODERATE_2_3;
     /**
-     * 3_4 code rate.
+     * 3/4 code rate.
      */
     public static final int CODERATE_3_4 = Constants.FrontendIsdbsCoderate.CODERATE_3_4;
     /**
-     * 5_6 code rate.
+     * 5/6 code rate.
      */
     public static final int CODERATE_5_6 = Constants.FrontendIsdbsCoderate.CODERATE_5_6;
     /**
-     * 7_8 code rate.
+     * 7/8 code rate.
      */
     public static final int CODERATE_7_8 = Constants.FrontendIsdbsCoderate.CODERATE_7_8;
 
@@ -123,11 +123,11 @@
     public @interface Rolloff {}
 
     /**
-     * Roll off type undefined.
+     * Rolloff type undefined.
      */
     public static final int ROLLOFF_UNDEFINED = Constants.FrontendIsdbs3Rolloff.UNDEFINED;
     /**
-     * 0.35 roll off type.
+     * 0,35 rolloff.
      */
     public static final int ROLLOFF_0_35 = Constants.FrontendIsdbsRolloff.ROLLOFF_0_35;
 
diff --git a/media/java/android/media/tv/tuner/frontend/ScanCallback.java b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
index 5e7d218..0479e55 100644
--- a/media/java/android/media/tv/tuner/frontend/ScanCallback.java
+++ b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
@@ -16,6 +16,7 @@
 
 package android.media.tv.tuner.frontend;
 
+
 /**
  * Scan callback.
  *
@@ -31,11 +32,11 @@
     /** scan progress percent (0..100) */
     void onProgress(int percent);
 
-    /** Signal frequency in Hertz */
-    void onFrequencyReport(int frequency);
+    /** Signal frequencies in Hertz */
+    void onFrequenciesReport(int[] frequency);
 
     /** Symbols per second */
-    void onSymbolRate(int rate);
+    void onSymbolRates(int[] rate);
 
     /** Locked Plp Ids for DVBT2 frontend. */
     void onPlpIds(int[] plpIds);
@@ -46,15 +47,24 @@
     /** Stream Ids. */
     void onInputStreamIds(int[] inputStreamIds);
 
-    /** Locked signal standard. */
+    /** Locked signal standard for DVBS. */
     void onDvbsStandard(@DvbsFrontendSettings.Standard int dvbsStandandard);
 
-    /** Locked signal standard. */
+    /** Locked signal standard. for DVBT */
     void onDvbtStandard(@DvbtFrontendSettings.Standard int dvbtStandard);
 
+    /** Locked signal SIF standard for Analog. */
+    void onAnalogSifStandard(@AnalogFrontendSettings.SifStandard int sif);
+
     /** PLP status in a tuned frequency band for ATSC3 frontend. */
     void onAtsc3PlpInfos(Atsc3PlpInfo[] atsc3PlpInfos);
 
+    /** Frontend hierarchy. */
+    void onHierarchy(@DvbtFrontendSettings.Hierarchy int hierarchy);
+
+    /** Frontend hierarchy. */
+    void onSignalType(@AnalogFrontendSettings.SignalType int signalType);
+
     /** PLP information for ATSC3. */
     class Atsc3PlpInfo {
         private final int mPlpId;
diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
index ed93112..cc2d1b1 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -167,8 +167,7 @@
     }
 
     @Override
-    public void onCreateSession(String packageName, String routeId, String routeFeature,
-            long requestId) {
+    public void onCreateSession(String packageName, String routeId, long requestId) {
         MediaRoute2Info route = mRoutes.get(routeId);
         if (route == null || TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) {
             // Tell the router that session cannot be created by passing null as sessionInfo.
@@ -185,8 +184,7 @@
                 .build());
         mRouteIdToSessionId.put(routeId, sessionId);
 
-        RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
-                sessionId, packageName, routeFeature)
+        RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(sessionId, packageName)
                 .addSelectedRoute(routeId)
                 .addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
                 .addTransferrableRoute(ROUTE_ID5_TO_TRANSFER_TO)
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRoute2InfoTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRoute2InfoTest.java
new file mode 100644
index 0000000..c46966f
--- /dev/null
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRoute2InfoTest.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright 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.mediaroutertest;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertThrows;
+
+import android.media.MediaRoute2Info;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests {@link MediaRoute2Info} and its {@link MediaRoute2Info.Builder builder}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MediaRoute2InfoTest {
+
+    public static final String TEST_ID = "test_id";
+    public static final String TEST_NAME = "test_name";
+    public static final String TEST_ROUTE_TYPE_0 = "test_route_type_0";
+    public static final String TEST_ROUTE_TYPE_1 = "test_route_type_1";
+    public static final int TEST_DEVICE_TYPE = MediaRoute2Info.DEVICE_TYPE_REMOTE_SPEAKER;
+    public static final Uri TEST_ICON_URI = Uri.parse("https://developer.android.com");
+    public static final String TEST_DESCRIPTION = "test_description";
+    public static final int TEST_CONNECTION_STATE = MediaRoute2Info.CONNECTION_STATE_CONNECTING;
+    public static final String TEST_CLIENT_PACKAGE_NAME = "com.test.client.package.name";
+    public static final int TEST_VOLUME_HANDLING = MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
+    public static final int TEST_VOLUME_MAX = 100;
+    public static final int TEST_VOLUME = 65;
+
+    public static final String TEST_KEY = "test_key";
+    public static final String TEST_VALUE = "test_value";
+
+    @Test
+    public void testBuilderConstructorWithInvalidValues() {
+        final String nullId = null;
+        final String nullName = null;
+        final String emptyId = "";
+        final String emptyName = "";
+        final String validId = "valid_id";
+        final String validName = "valid_name";
+
+        // ID is invalid
+        assertThrows(IllegalArgumentException.class,
+                () -> new MediaRoute2Info.Builder(nullId, validName));
+        assertThrows(IllegalArgumentException.class,
+                () -> new MediaRoute2Info.Builder(emptyId, validName));
+
+        // name is invalid
+        assertThrows(IllegalArgumentException.class,
+                () -> new MediaRoute2Info.Builder(validId, nullName));
+        assertThrows(IllegalArgumentException.class,
+                () -> new MediaRoute2Info.Builder(validId, emptyName));
+
+        // Both are invalid
+        assertThrows(IllegalArgumentException.class,
+                () -> new MediaRoute2Info.Builder(nullId, nullName));
+        assertThrows(IllegalArgumentException.class,
+                () -> new MediaRoute2Info.Builder(nullId, emptyName));
+        assertThrows(IllegalArgumentException.class,
+                () -> new MediaRoute2Info.Builder(emptyId, nullName));
+        assertThrows(IllegalArgumentException.class,
+                () -> new MediaRoute2Info.Builder(emptyId, emptyName));
+
+
+        // Null RouteInfo (1-argument constructor)
+        final MediaRoute2Info nullRouteInfo = null;
+        assertThrows(NullPointerException.class,
+                () -> new MediaRoute2Info.Builder(nullRouteInfo));
+    }
+
+    @Test
+    public void testBuilderBuildWithEmptyRouteTypesShouldThrowIAE() {
+        MediaRoute2Info.Builder builder = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME);
+        assertThrows(IllegalArgumentException.class, () -> builder.build());
+    }
+
+    @Test
+    public void testBuilderAndGettersOfMediaRoute2Info() {
+        Bundle extras = new Bundle();
+        extras.putString(TEST_KEY, TEST_VALUE);
+
+        MediaRoute2Info routeInfo = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+                .addFeature(TEST_ROUTE_TYPE_0)
+                .addFeature(TEST_ROUTE_TYPE_1)
+                .setDeviceType(TEST_DEVICE_TYPE)
+                .setIconUri(TEST_ICON_URI)
+                .setDescription(TEST_DESCRIPTION)
+                .setConnectionState(TEST_CONNECTION_STATE)
+                .setClientPackageName(TEST_CLIENT_PACKAGE_NAME)
+                .setVolumeHandling(TEST_VOLUME_HANDLING)
+                .setVolumeMax(TEST_VOLUME_MAX)
+                .setVolume(TEST_VOLUME)
+                .setExtras(extras)
+                .build();
+
+        assertEquals(TEST_ID, routeInfo.getId());
+        assertEquals(TEST_NAME, routeInfo.getName());
+
+        assertEquals(2, routeInfo.getFeatures().size());
+        assertEquals(TEST_ROUTE_TYPE_0, routeInfo.getFeatures().get(0));
+        assertEquals(TEST_ROUTE_TYPE_1, routeInfo.getFeatures().get(1));
+
+        assertEquals(TEST_DEVICE_TYPE, routeInfo.getDeviceType());
+        assertEquals(TEST_ICON_URI, routeInfo.getIconUri());
+        assertEquals(TEST_DESCRIPTION, routeInfo.getDescription());
+        assertEquals(TEST_CONNECTION_STATE, routeInfo.getConnectionState());
+        assertEquals(TEST_CLIENT_PACKAGE_NAME, routeInfo.getClientPackageName());
+        assertEquals(TEST_VOLUME_HANDLING, routeInfo.getVolumeHandling());
+        assertEquals(TEST_VOLUME_MAX, routeInfo.getVolumeMax());
+        assertEquals(TEST_VOLUME, routeInfo.getVolume());
+
+        Bundle extrasOut = routeInfo.getExtras();
+        assertNotNull(extrasOut);
+        assertTrue(extrasOut.containsKey(TEST_KEY));
+        assertEquals(TEST_VALUE, extrasOut.getString(TEST_KEY));
+    }
+
+    @Test
+    public void testBuilderSetExtrasWithNull() {
+        MediaRoute2Info routeInfo = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+                .addFeature(TEST_ROUTE_TYPE_0)
+                .setExtras(null)
+                .build();
+
+        assertNull(routeInfo.getExtras());
+    }
+
+    @Test
+    public void testBuilderaddFeatures() {
+        List<String> routeTypes = new ArrayList<>();
+        routeTypes.add(TEST_ROUTE_TYPE_0);
+        routeTypes.add(TEST_ROUTE_TYPE_1);
+
+        MediaRoute2Info routeInfo = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+                .addFeatures(routeTypes)
+                .build();
+
+        assertEquals(routeTypes, routeInfo.getFeatures());
+    }
+
+    @Test
+    public void testBuilderclearFeatures() {
+        MediaRoute2Info routeInfo = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+                .addFeature(TEST_ROUTE_TYPE_0)
+                .addFeature(TEST_ROUTE_TYPE_1)
+                // clearFeatures should clear the route types.
+                .clearFeatures()
+                .addFeature(TEST_ROUTE_TYPE_1)
+                .build();
+
+        assertEquals(1, routeInfo.getFeatures().size());
+        assertEquals(TEST_ROUTE_TYPE_1, routeInfo.getFeatures().get(0));
+    }
+
+    @Test
+    public void testhasAnyFeaturesReturnsFalse() {
+        MediaRoute2Info routeInfo = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+                .addFeature(TEST_ROUTE_TYPE_0)
+                .addFeature(TEST_ROUTE_TYPE_1)
+                .build();
+
+        List<String> testRouteTypes = new ArrayList<>();
+        testRouteTypes.add("non_matching_route_type_1");
+        testRouteTypes.add("non_matching_route_type_2");
+        testRouteTypes.add("non_matching_route_type_3");
+        testRouteTypes.add("non_matching_route_type_4");
+
+        assertFalse(routeInfo.hasAnyFeatures(testRouteTypes));
+    }
+
+    @Test
+    public void testhasAnyFeaturesReturnsTrue() {
+        MediaRoute2Info routeInfo = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+                .addFeature(TEST_ROUTE_TYPE_0)
+                .addFeature(TEST_ROUTE_TYPE_1)
+                .build();
+
+        List<String> testRouteTypes = new ArrayList<>();
+        testRouteTypes.add("non_matching_route_type_1");
+        testRouteTypes.add("non_matching_route_type_2");
+        testRouteTypes.add("non_matching_route_type_3");
+        testRouteTypes.add(TEST_ROUTE_TYPE_1);
+
+        assertTrue(routeInfo.hasAnyFeatures(testRouteTypes));
+    }
+
+    @Test
+    public void testEqualsCreatedWithSameArguments() {
+        Bundle extras = new Bundle();
+        extras.putString(TEST_KEY, TEST_VALUE);
+
+        MediaRoute2Info routeInfo1 = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+                .addFeature(TEST_ROUTE_TYPE_0)
+                .addFeature(TEST_ROUTE_TYPE_1)
+                .setDeviceType(TEST_DEVICE_TYPE)
+                .setIconUri(TEST_ICON_URI)
+                .setDescription(TEST_DESCRIPTION)
+                .setConnectionState(TEST_CONNECTION_STATE)
+                .setClientPackageName(TEST_CLIENT_PACKAGE_NAME)
+                .setVolumeHandling(TEST_VOLUME_HANDLING)
+                .setVolumeMax(TEST_VOLUME_MAX)
+                .setVolume(TEST_VOLUME)
+                .setExtras(extras)
+                .build();
+
+        MediaRoute2Info routeInfo2 = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+                .addFeature(TEST_ROUTE_TYPE_0)
+                .addFeature(TEST_ROUTE_TYPE_1)
+                .setDeviceType(TEST_DEVICE_TYPE)
+                .setIconUri(TEST_ICON_URI)
+                .setDescription(TEST_DESCRIPTION)
+                .setConnectionState(TEST_CONNECTION_STATE)
+                .setClientPackageName(TEST_CLIENT_PACKAGE_NAME)
+                .setVolumeHandling(TEST_VOLUME_HANDLING)
+                .setVolumeMax(TEST_VOLUME_MAX)
+                .setVolume(TEST_VOLUME)
+                .setExtras(extras)
+                .build();
+
+        assertEquals(routeInfo1, routeInfo2);
+        assertEquals(routeInfo1.hashCode(), routeInfo2.hashCode());
+    }
+
+    @Test
+    public void testEqualsCreatedWithBuilderCopyConstructor() {
+        Bundle extras = new Bundle();
+        extras.putString(TEST_KEY, TEST_VALUE);
+
+        MediaRoute2Info routeInfo1 = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+                .addFeature(TEST_ROUTE_TYPE_0)
+                .addFeature(TEST_ROUTE_TYPE_1)
+                .setDeviceType(TEST_DEVICE_TYPE)
+                .setIconUri(TEST_ICON_URI)
+                .setDescription(TEST_DESCRIPTION)
+                .setConnectionState(TEST_CONNECTION_STATE)
+                .setClientPackageName(TEST_CLIENT_PACKAGE_NAME)
+                .setVolumeHandling(TEST_VOLUME_HANDLING)
+                .setVolumeMax(TEST_VOLUME_MAX)
+                .setVolume(TEST_VOLUME)
+                .setExtras(extras)
+                .build();
+
+        MediaRoute2Info routeInfo2 = new MediaRoute2Info.Builder(routeInfo1).build();
+
+        assertEquals(routeInfo1, routeInfo2);
+        assertEquals(routeInfo1.hashCode(), routeInfo2.hashCode());
+    }
+
+    @Test
+    public void testEqualsReturnFalse() {
+        Bundle extras = new Bundle();
+        extras.putString(TEST_KEY, TEST_VALUE);
+
+        MediaRoute2Info routeInfo = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+                .addFeature(TEST_ROUTE_TYPE_0)
+                .addFeature(TEST_ROUTE_TYPE_1)
+                .setDeviceType(TEST_DEVICE_TYPE)
+                .setIconUri(TEST_ICON_URI)
+                .setDescription(TEST_DESCRIPTION)
+                .setConnectionState(TEST_CONNECTION_STATE)
+                .setClientPackageName(TEST_CLIENT_PACKAGE_NAME)
+                .setVolumeHandling(TEST_VOLUME_HANDLING)
+                .setVolumeMax(TEST_VOLUME_MAX)
+                .setVolume(TEST_VOLUME)
+                .setExtras(extras)
+                .build();
+
+        // Now, we will use copy constructor
+        assertNotEquals(routeInfo, new MediaRoute2Info.Builder(routeInfo)
+                .addFeature("randomRouteType")
+                .build());
+        assertNotEquals(routeInfo, new MediaRoute2Info.Builder(routeInfo)
+                .setDeviceType(TEST_DEVICE_TYPE + 1)
+                .build());
+        assertNotEquals(routeInfo, new MediaRoute2Info.Builder(routeInfo)
+                .setIconUri(Uri.parse("randomUri"))
+                .build());
+        assertNotEquals(routeInfo, new MediaRoute2Info.Builder(routeInfo)
+                .setDescription("randomDescription")
+                .build());
+        assertNotEquals(routeInfo, new MediaRoute2Info.Builder(routeInfo)
+                .setConnectionState(TEST_CONNECTION_STATE + 1)
+                .build());
+        assertNotEquals(routeInfo, new MediaRoute2Info.Builder(routeInfo)
+                .setClientPackageName("randomPackageName")
+                .build());
+        assertNotEquals(routeInfo, new MediaRoute2Info.Builder(routeInfo)
+                .setVolumeHandling(TEST_VOLUME_HANDLING + 1)
+                .build());
+        assertNotEquals(routeInfo, new MediaRoute2Info.Builder(routeInfo)
+                .setVolumeMax(TEST_VOLUME_MAX + 100)
+                .build());
+        assertNotEquals(routeInfo, new MediaRoute2Info.Builder(routeInfo)
+                .setVolume(TEST_VOLUME + 10)
+                .build());
+        // Note: Extras will not affect the equals.
+    }
+
+    @Test
+    public void testParcelingAndUnParceling() {
+        Bundle extras = new Bundle();
+        extras.putString(TEST_KEY, TEST_VALUE);
+
+        MediaRoute2Info routeInfo = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+                .addFeature(TEST_ROUTE_TYPE_0)
+                .addFeature(TEST_ROUTE_TYPE_1)
+                .setDeviceType(TEST_DEVICE_TYPE)
+                .setIconUri(TEST_ICON_URI)
+                .setDescription(TEST_DESCRIPTION)
+                .setConnectionState(TEST_CONNECTION_STATE)
+                .setClientPackageName(TEST_CLIENT_PACKAGE_NAME)
+                .setVolumeHandling(TEST_VOLUME_HANDLING)
+                .setVolumeMax(TEST_VOLUME_MAX)
+                .setVolume(TEST_VOLUME)
+                .setExtras(extras)
+                .build();
+
+        Parcel parcel = Parcel.obtain();
+        routeInfo.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+
+        MediaRoute2Info routeInfoFromParcel = MediaRoute2Info.CREATOR.createFromParcel(parcel);
+        assertEquals(routeInfo, routeInfoFromParcel);
+        assertEquals(routeInfo.hashCode(), routeInfoFromParcel.hashCode());
+
+        // Check extras
+        Bundle extrasOut = routeInfoFromParcel.getExtras();
+        assertNotNull(extrasOut);
+        assertTrue(extrasOut.containsKey(TEST_KEY));
+        assertEquals(TEST_VALUE, extrasOut.getString(TEST_KEY));
+    }
+}
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
index 007229a..e782aae 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
@@ -16,17 +16,13 @@
 
 package com.android.mediaroutertest;
 
-import static android.media.MediaRoute2Info.CONNECTION_STATE_CONNECTED;
 import static android.media.MediaRoute2Info.CONNECTION_STATE_CONNECTING;
 import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_SPEAKER;
-import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV;
-import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
 import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
 
 import static com.android.mediaroutertest.MediaRouterManagerTest.FEATURES_ALL;
 import static com.android.mediaroutertest.MediaRouterManagerTest.FEATURES_SPECIAL;
 import static com.android.mediaroutertest.MediaRouterManagerTest.FEATURE_SAMPLE;
-import static com.android.mediaroutertest.MediaRouterManagerTest.FEATURE_SPECIAL;
 import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID1;
 import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID2;
 import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID3_SESSION_CREATION_FAILED;
@@ -132,57 +128,6 @@
     }
 
     @Test
-    public void testRouteInfoInequality() {
-        MediaRoute2Info route = new MediaRoute2Info.Builder("id", "name")
-                .setDescription("description")
-                .setClientPackageName("com.android.mediaroutertest")
-                .setConnectionState(CONNECTION_STATE_CONNECTING)
-                .setIconUri(new Uri.Builder().path("icon").build())
-                .addFeature(FEATURE_SAMPLE)
-                .setVolume(5)
-                .setVolumeMax(20)
-                .setVolumeHandling(PLAYBACK_VOLUME_VARIABLE)
-                .setDeviceType(DEVICE_TYPE_REMOTE_SPEAKER)
-                .build();
-
-        MediaRoute2Info routeDescription = new MediaRoute2Info.Builder(route)
-                .setDescription("another description").build();
-        assertNotEquals(route, routeDescription);
-
-        MediaRoute2Info routeConnectionState = new MediaRoute2Info.Builder(route)
-                .setConnectionState(CONNECTION_STATE_CONNECTED).build();
-        assertNotEquals(route, routeConnectionState);
-
-        MediaRoute2Info routeIcon = new MediaRoute2Info.Builder(route)
-                .setIconUri(new Uri.Builder().path("new icon").build()).build();
-        assertNotEquals(route, routeIcon);
-
-        MediaRoute2Info routeClient = new MediaRoute2Info.Builder(route)
-                .setClientPackageName("another.client.package").build();
-        assertNotEquals(route, routeClient);
-
-        MediaRoute2Info routeType = new MediaRoute2Info.Builder(route)
-                .addFeature(FEATURE_SPECIAL).build();
-        assertNotEquals(route, routeType);
-
-        MediaRoute2Info routeVolume = new MediaRoute2Info.Builder(route)
-                .setVolume(10).build();
-        assertNotEquals(route, routeVolume);
-
-        MediaRoute2Info routeVolumeMax = new MediaRoute2Info.Builder(route)
-                .setVolumeMax(30).build();
-        assertNotEquals(route, routeVolumeMax);
-
-        MediaRoute2Info routeVolumeHandling = new MediaRoute2Info.Builder(route)
-                .setVolumeHandling(PLAYBACK_VOLUME_FIXED).build();
-        assertNotEquals(route, routeVolumeHandling);
-
-        MediaRoute2Info routeDeviceType = new MediaRoute2Info.Builder(route)
-                .setVolume(DEVICE_TYPE_REMOTE_TV).build();
-        assertNotEquals(route, routeDeviceType);
-    }
-
-    @Test
     public void testControlVolumeWithRouter() throws Exception {
         Map<String, MediaRoute2Info> routes = waitAndGetRoutes(FEATURES_ALL);
 
@@ -227,19 +172,8 @@
     }
 
     @Test
-    public void testRequestCreateSessionWithInvalidArguments() {
-        MediaRoute2Info route = new MediaRoute2Info.Builder("id", "name").build();
-        String routeFeature = "routeFeature";
-
-        // Tests null route
-        assertThrows(NullPointerException.class,
-                () -> mRouter2.requestCreateSession(null, routeFeature));
-
-        // Tests null or empty route feature
-        assertThrows(IllegalArgumentException.class,
-                () -> mRouter2.requestCreateSession(route, null));
-        assertThrows(IllegalArgumentException.class,
-                () -> mRouter2.requestCreateSession(route, ""));
+    public void testRequestCreateSessionWithNullRoute() {
+        assertThrows(NullPointerException.class, () -> mRouter2.requestCreateSession(null));
     }
 
     @Test
@@ -261,14 +195,12 @@
             public void onSessionCreated(RoutingController controller) {
                 assertNotNull(controller);
                 assertTrue(createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1));
-                assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature()));
                 controllers.add(controller);
                 successLatch.countDown();
             }
 
             @Override
-            public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
-                    String requestedRouteFeature) {
+            public void onSessionCreationFailed(MediaRoute2Info requestedRoute) {
                 failureLatch.countDown();
             }
         };
@@ -279,7 +211,7 @@
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(route, FEATURE_SAMPLE);
+            mRouter2.requestCreateSession(route);
             assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             // onSessionCreationFailed should not be called.
@@ -313,10 +245,8 @@
             }
 
             @Override
-            public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
-                    String requestedRouteFeature) {
+            public void onSessionCreationFailed(MediaRoute2Info requestedRoute) {
                 assertEquals(route, requestedRoute);
-                assertTrue(TextUtils.equals(FEATURE_SAMPLE, requestedRouteFeature));
                 failureLatch.countDown();
             }
         };
@@ -327,7 +257,7 @@
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(route, FEATURE_SAMPLE);
+            mRouter2.requestCreateSession(route);
             assertTrue(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             // onSessionCreated should not be called.
@@ -357,8 +287,7 @@
             }
 
             @Override
-            public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
-                    String requestedRouteFeature) {
+            public void onSessionCreationFailed(MediaRoute2Info requestedRoute) {
                 failureLatch.countDown();
             }
         };
@@ -375,8 +304,8 @@
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(route1, FEATURE_SAMPLE);
-            mRouter2.requestCreateSession(route2, FEATURE_SAMPLE);
+            mRouter2.requestCreateSession(route1);
+            mRouter2.requestCreateSession(route2);
             assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             // onSessionCreationFailed should not be called.
@@ -390,8 +319,6 @@
             assertNotEquals(controller1.getSessionId(), controller2.getSessionId());
             assertTrue(createRouteMap(controller1.getSelectedRoutes()).containsKey(ROUTE_ID1));
             assertTrue(createRouteMap(controller2.getSelectedRoutes()).containsKey(ROUTE_ID2));
-            assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller1.getRouteFeature()));
-            assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller2.getRouteFeature()));
 
         } finally {
             releaseControllers(createdControllers);
@@ -422,8 +349,7 @@
             }
 
             @Override
-            public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
-                    String requestedRouteFeature) {
+            public void onSessionCreationFailed(MediaRoute2Info requestedRoute) {
                 failureLatch.countDown();
             }
         };
@@ -434,7 +360,7 @@
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(route, FEATURE_SAMPLE);
+            mRouter2.requestCreateSession(route);
 
             // Unregisters session callback
             mRouter2.unregisterSessionCallback(sessionCallback);
@@ -470,7 +396,6 @@
             public void onSessionCreated(RoutingController controller) {
                 assertNotNull(controller);
                 assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
-                assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature()));
                 controllers.add(controller);
                 onSessionCreatedLatch.countDown();
             }
@@ -523,7 +448,7 @@
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(routeToCreateSessionWith, FEATURE_SAMPLE);
+            mRouter2.requestCreateSession(routeToCreateSessionWith);
             assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             assertEquals(1, controllers.size());
@@ -568,7 +493,6 @@
             public void onSessionCreated(RoutingController controller) {
                 assertNotNull(controller);
                 assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
-                assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature()));
                 controllers.add(controller);
                 onSessionCreatedLatch.countDown();
             }
@@ -605,7 +529,7 @@
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(routeToCreateSessionWith, FEATURE_SAMPLE);
+            mRouter2.requestCreateSession(routeToCreateSessionWith);
             assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             assertEquals(1, controllers.size());
@@ -647,7 +571,6 @@
             public void onSessionCreated(RoutingController controller) {
                 assertNotNull(controller);
                 assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
-                assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature()));
                 controllers.add(controller);
                 onSessionCreatedLatch.countDown();
             }
@@ -670,7 +593,7 @@
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(routeToCreateSessionWith, FEATURE_SAMPLE);
+            mRouter2.requestCreateSession(routeToCreateSessionWith);
             assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             assertEquals(1, controllers.size());
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index ae15b91..83dd0c0 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -20,7 +20,6 @@
 import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
@@ -143,20 +142,6 @@
         clearCallbacks();
     }
 
-    //TODO: Move to a separate file
-    @Test
-    public void testMediaRoute2Info() {
-        MediaRoute2Info routeInfo1 = new MediaRoute2Info.Builder("id", "name")
-                .build();
-        MediaRoute2Info routeInfo2 = new MediaRoute2Info.Builder(routeInfo1).build();
-
-        MediaRoute2Info routeInfo3 = new MediaRoute2Info.Builder(routeInfo1)
-                .setClientPackageName(mPackageName).build();
-
-        assertEquals(routeInfo1, routeInfo2);
-        assertNotEquals(routeInfo1, routeInfo3);
-    }
-
     /**
      * Tests if routes are added correctly when a new callback is registered.
      */
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java
index 3f59736..704dca0 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java
@@ -57,65 +57,32 @@
     public void testBuilderConstructorWithInvalidValues() {
         final String nullId = null;
         final String nullClientPackageName = null;
-        final String nullRouteFeature = null;
 
         final String emptyId = "";
         // Note: An empty string as client package name is valid.
-        final String emptyRouteFeature = "";
 
         final String validId = TEST_ID;
         final String validClientPackageName = TEST_CLIENT_PACKAGE_NAME;
-        final String validRouteFeature = TEST_ROUTE_FEATURE;
 
         // ID is invalid
         assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
-                nullId, validClientPackageName, validRouteFeature));
+                nullId, validClientPackageName));
         assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
-                emptyId, validClientPackageName, validRouteFeature));
+                emptyId, validClientPackageName));
 
         // client package name is invalid (null)
         assertThrows(NullPointerException.class, () -> new RoutingSessionInfo.Builder(
-                validId, nullClientPackageName, validRouteFeature));
+                validId, nullClientPackageName));
 
-        // route feature is invalid
+        // Both are invalid
         assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
-                validId, validClientPackageName, nullRouteFeature));
+                nullId, nullClientPackageName));
         assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
-                validId, validClientPackageName, emptyRouteFeature));
+                emptyId, nullClientPackageName));
+    }
 
-        // Two arguments are invalid - (1) ID and clientPackageName
-        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
-                nullId, nullClientPackageName, validRouteFeature));
-        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
-                emptyId, nullClientPackageName, validRouteFeature));
-
-        // Two arguments are invalid - (2) ID and routeFeature
-        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
-                nullId, validClientPackageName, nullRouteFeature));
-        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
-                nullId, validClientPackageName, emptyRouteFeature));
-        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
-                emptyId, validClientPackageName, nullRouteFeature));
-        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
-                emptyId, validClientPackageName, emptyRouteFeature));
-
-        // Two arguments are invalid - (3) clientPackageName and routeFeature
-        // Note that this throws NullPointerException.
-        assertThrows(NullPointerException.class, () -> new RoutingSessionInfo.Builder(
-                validId, nullClientPackageName, nullRouteFeature));
-        assertThrows(NullPointerException.class, () -> new RoutingSessionInfo.Builder(
-                validId, nullClientPackageName, emptyRouteFeature));
-
-        // All arguments are invalid
-        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
-                nullId, nullClientPackageName, nullRouteFeature));
-        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
-                nullId, nullClientPackageName, emptyRouteFeature));
-        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
-                emptyId, nullClientPackageName, nullRouteFeature));
-        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
-                emptyId, nullClientPackageName, emptyRouteFeature));
-
+    @Test
+    public void testBuilderCopyConstructorWithNull() {
         // Null RouteInfo (1-argument constructor)
         final RoutingSessionInfo nullRoutingSessionInfo = null;
         assertThrows(NullPointerException.class,
@@ -127,13 +94,13 @@
         // An empty string for client package name is valid. (for unknown cases)
         // Creating builder with it should not throw any exception.
         RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
-                TEST_ID, "" /* clientPackageName*/, TEST_ROUTE_FEATURE);
+                TEST_ID, "" /* clientPackageName*/);
     }
 
     @Test
     public void testBuilderBuildWithEmptySelectedRoutesThrowsIAE() {
         RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
-                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE);
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME);
         // Note: Calling build() without adding any selected routes.
         assertThrows(IllegalArgumentException.class, () -> builder.build());
     }
@@ -141,7 +108,7 @@
     @Test
     public void testBuilderAddRouteMethodsWithIllegalArgumentsThrowsIAE() {
         RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
-                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE);
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME);
 
         final String nullRouteId = null;
         final String emptyRouteId = "";
@@ -168,7 +135,7 @@
     @Test
     public void testBuilderRemoveRouteMethodsWithIllegalArgumentsThrowsIAE() {
         RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
-                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE);
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME);
 
         final String nullRouteId = null;
         final String emptyRouteId = "";
@@ -198,7 +165,7 @@
         controlHints.putString(TEST_KEY, TEST_VALUE);
 
         RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
-                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME)
                 .addSelectedRoute(TEST_ROUTE_ID_0)
                 .addSelectedRoute(TEST_ROUTE_ID_1)
                 .addSelectableRoute(TEST_ROUTE_ID_2)
@@ -212,7 +179,6 @@
 
         assertEquals(TEST_ID, sessionInfo.getId());
         assertEquals(TEST_CLIENT_PACKAGE_NAME, sessionInfo.getClientPackageName());
-        assertEquals(TEST_ROUTE_FEATURE, sessionInfo.getRouteFeature());
 
         assertEquals(2, sessionInfo.getSelectedRoutes().size());
         assertEquals(TEST_ROUTE_ID_0, sessionInfo.getSelectedRoutes().get(0));
@@ -239,7 +205,7 @@
     @Test
     public void testBuilderAddRouteMethodsWithBuilderCopyConstructor() {
         RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
-                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME)
                 .addSelectedRoute(TEST_ROUTE_ID_0)
                 .addSelectableRoute(TEST_ROUTE_ID_2)
                 .addDeselectableRoute(TEST_ROUTE_ID_4)
@@ -273,7 +239,7 @@
     @Test
     public void testBuilderRemoveRouteMethods() {
         RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
-                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME)
                 .addSelectedRoute(TEST_ROUTE_ID_0)
                 .addSelectedRoute(TEST_ROUTE_ID_1)
                 .removeSelectedRoute(TEST_ROUTE_ID_1)
@@ -308,7 +274,7 @@
     @Test
     public void testBuilderRemoveRouteMethodsWithBuilderCopyConstructor() {
         RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
-                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME)
                 .addSelectedRoute(TEST_ROUTE_ID_0)
                 .addSelectedRoute(TEST_ROUTE_ID_1)
                 .addSelectableRoute(TEST_ROUTE_ID_2)
@@ -342,7 +308,7 @@
     @Test
     public void testBuilderClearRouteMethods() {
         RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
-                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME)
                 .addSelectedRoute(TEST_ROUTE_ID_0)
                 .addSelectedRoute(TEST_ROUTE_ID_1)
                 .clearSelectedRoutes()
@@ -374,7 +340,7 @@
     @Test
     public void testBuilderClearRouteMethodsWithBuilderCopyConstructor() {
         RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
-                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME)
                 .addSelectedRoute(TEST_ROUTE_ID_0)
                 .addSelectedRoute(TEST_ROUTE_ID_1)
                 .addSelectableRoute(TEST_ROUTE_ID_2)
@@ -408,7 +374,7 @@
         controlHints.putString(TEST_KEY, TEST_VALUE);
 
         RoutingSessionInfo sessionInfo1 = new RoutingSessionInfo.Builder(
-                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME)
                 .addSelectedRoute(TEST_ROUTE_ID_0)
                 .addSelectedRoute(TEST_ROUTE_ID_1)
                 .addSelectableRoute(TEST_ROUTE_ID_2)
@@ -421,7 +387,7 @@
                 .build();
 
         RoutingSessionInfo sessionInfo2 = new RoutingSessionInfo.Builder(
-                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME)
                 .addSelectedRoute(TEST_ROUTE_ID_0)
                 .addSelectedRoute(TEST_ROUTE_ID_1)
                 .addSelectableRoute(TEST_ROUTE_ID_2)
@@ -443,7 +409,7 @@
         controlHints.putString(TEST_KEY, TEST_VALUE);
 
         RoutingSessionInfo sessionInfo1 = new RoutingSessionInfo.Builder(
-                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME)
                 .addSelectedRoute(TEST_ROUTE_ID_0)
                 .addSelectedRoute(TEST_ROUTE_ID_1)
                 .addSelectableRoute(TEST_ROUTE_ID_2)
@@ -467,7 +433,7 @@
         controlHints.putString(TEST_KEY, TEST_VALUE);
 
         RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
-                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME)
                 .addSelectedRoute(TEST_ROUTE_ID_0)
                 .addSelectedRoute(TEST_ROUTE_ID_1)
                 .addSelectableRoute(TEST_ROUTE_ID_2)
@@ -530,7 +496,7 @@
         controlHints.putString(TEST_KEY, TEST_VALUE);
 
         RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
-                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME)
                 .addSelectedRoute(TEST_ROUTE_ID_0)
                 .addSelectedRoute(TEST_ROUTE_ID_1)
                 .addSelectableRoute(TEST_ROUTE_ID_2)
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 41a9b24..5054281 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -49,7 +49,6 @@
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
-import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.TrafficStatsConstants;
 
@@ -207,7 +206,7 @@
         if (TextUtils.isEmpty(url)) url = mCm.getCaptivePortalServerUrl();
         final CarrierConfigManager configManager = getApplicationContext()
                 .getSystemService(CarrierConfigManager.class);
-        final int subId = getIntent().getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+        final int subId = getIntent().getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
                 SubscriptionManager.getDefaultVoiceSubscriptionId());
         final String[] portalURLs = configManager.getConfigForSubId(subId).getStringArray(
                 CarrierConfigManager.KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY);
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index beb3aff..9ccb837 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -417,7 +417,9 @@
                 break;
 
             case STATUS_READY:
-                builder.setContentText(getString(R.string.notification_install_completed));
+                String msgCompleted = getString(R.string.notification_install_completed);
+                builder.setContentText(msgCompleted)
+                        .setStyle(new Notification.BigTextStyle().bigText(msgCompleted));
 
                 builder.addAction(new Notification.Action.Builder(
                         null, getString(R.string.notification_action_discard),
@@ -430,7 +432,9 @@
                 break;
 
             case STATUS_IN_USE:
-                builder.setContentText(getString(R.string.notification_dynsystem_in_use));
+                String msgInUse = getString(R.string.notification_dynsystem_in_use);
+                builder.setContentText(msgInUse)
+                        .setStyle(new Notification.BigTextStyle().bigText(msgInUse));
 
                 builder.addAction(new Notification.Action.Builder(
                         null, getString(R.string.notification_action_uninstall),
@@ -460,7 +464,49 @@
     }
 
     private void postStatus(int status, int cause, Throwable detail) {
-        Log.d(TAG, "postStatus(): statusCode=" + status + ", causeCode=" + cause);
+        String statusString;
+        String causeString;
+
+        switch (status) {
+            case STATUS_NOT_STARTED:
+                statusString = "NOT_STARTED";
+                break;
+            case STATUS_IN_PROGRESS:
+                statusString = "IN_PROGRESS";
+                break;
+            case STATUS_READY:
+                statusString = "READY";
+                break;
+            case STATUS_IN_USE:
+                statusString = "IN_USE";
+                break;
+            default:
+                statusString = "UNKNOWN";
+                break;
+        }
+
+        switch (cause) {
+            case CAUSE_INSTALL_COMPLETED:
+                causeString = "INSTALL_COMPLETED";
+                break;
+            case CAUSE_INSTALL_CANCELLED:
+                causeString = "INSTALL_CANCELLED";
+                break;
+            case CAUSE_ERROR_IO:
+                causeString = "ERROR_IO";
+                break;
+            case CAUSE_ERROR_INVALID_URL:
+                causeString = "ERROR_INVALID_URL";
+                break;
+            case CAUSE_ERROR_EXCEPTION:
+                causeString = "ERROR_EXCEPTION";
+                break;
+            default:
+                causeString = "CAUSE_NOT_SPECIFIED";
+                break;
+        }
+
+        Log.d(TAG, "status=" + statusString + ", cause=" + causeString);
 
         boolean notifyOnNotificationBar = true;
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
index 440315f..2d7d59c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
@@ -118,7 +118,7 @@
             notifyChanged();
         }
 
-        setSummary(mWifiEntry.getSummary());
+        setSummary(mWifiEntry.getSummary(false /* concise */));
         mContentDescription = buildContentDescription();
     }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
index 752a549..0f1e0ff 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
@@ -67,7 +67,7 @@
         MockitoAnnotations.initMocks(this);
 
         when(mMockWifiEntry.getTitle()).thenReturn(MOCK_TITLE);
-        when(mMockWifiEntry.getSummary()).thenReturn(MOCK_SUMMARY);
+        when(mMockWifiEntry.getSummary(false /* concise */)).thenReturn(MOCK_SUMMARY);
 
         when(mMockIconInjector.getIcon(0)).thenReturn(mMockDrawable0);
         when(mMockIconInjector.getIcon(1)).thenReturn(mMockDrawable1);
@@ -112,7 +112,7 @@
         final WifiEntryPreference pref =
                 new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
         final String updatedSummary = "updated summary";
-        when(mMockWifiEntry.getSummary()).thenReturn(updatedSummary);
+        when(mMockWifiEntry.getSummary(false /* concise */)).thenReturn(updatedSummary);
 
         pref.refresh();
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 4443524..c913999 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -367,7 +367,9 @@
                 String value = getSettingValue(args);
                 String tag = getSettingTag(args);
                 final boolean makeDefault = getSettingMakeDefault(args);
-                insertGlobalSetting(name, value, tag, makeDefault, requestingUserId, false);
+                final boolean overrideableByRestore = getSettingOverrideableByRestore(args);
+                insertGlobalSetting(name, value, tag, makeDefault, requestingUserId, false,
+                        overrideableByRestore);
                 break;
             }
 
@@ -375,13 +377,16 @@
                 String value = getSettingValue(args);
                 String tag = getSettingTag(args);
                 final boolean makeDefault = getSettingMakeDefault(args);
-                insertSecureSetting(name, value, tag, makeDefault, requestingUserId, false);
+                final boolean overrideableByRestore = getSettingOverrideableByRestore(args);
+                insertSecureSetting(name, value, tag, makeDefault, requestingUserId, false,
+                        overrideableByRestore);
                 break;
             }
 
             case Settings.CALL_METHOD_PUT_SYSTEM: {
                 String value = getSettingValue(args);
-                insertSystemSetting(name, value, requestingUserId);
+                boolean overrideableByRestore = getSettingOverrideableByRestore(args);
+                insertSystemSetting(name, value, requestingUserId, overrideableByRestore);
                 break;
             }
 
@@ -576,20 +581,23 @@
         switch (table) {
             case TABLE_GLOBAL: {
                 if (insertGlobalSetting(name, value, null, false,
-                        UserHandle.getCallingUserId(), false)) {
+                        UserHandle.getCallingUserId(), false,
+                        /* overrideableByRestore */ false)) {
                     return Uri.withAppendedPath(Settings.Global.CONTENT_URI, name);
                 }
             } break;
 
             case TABLE_SECURE: {
                 if (insertSecureSetting(name, value, null, false,
-                        UserHandle.getCallingUserId(), false)) {
+                        UserHandle.getCallingUserId(), false,
+                        /* overrideableByRestore */ false)) {
                     return Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name);
                 }
             } break;
 
             case TABLE_SYSTEM: {
-                if (insertSystemSetting(name, value, UserHandle.getCallingUserId())) {
+                if (insertSystemSetting(name, value, UserHandle.getCallingUserId(),
+                        /* overridableByRestore */ false)) {
                     return Uri.withAppendedPath(Settings.System.CONTENT_URI, name);
                 }
             } break;
@@ -1075,7 +1083,8 @@
                 case MUTATION_OPERATION_INSERT: {
                     return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG,
                             UserHandle.USER_SYSTEM, name, value, null, makeDefault, true,
-                            resolveCallingPackage(), false, null);
+                            resolveCallingPackage(), false, null,
+                            /* overrideableByRestore */ false);
                 }
 
                 case MUTATION_OPERATION_DELETE: {
@@ -1179,14 +1188,15 @@
     }
 
     private boolean insertGlobalSetting(String name, String value, String tag,
-            boolean makeDefault, int requestingUserId, boolean forceNotify) {
+            boolean makeDefault, int requestingUserId, boolean forceNotify,
+            boolean overrideableByRestore) {
         if (DEBUG) {
             Slog.v(LOG_TAG, "insertGlobalSetting(" + name + ", " + value  + ", "
                     + ", " + tag + ", " + makeDefault + ", " + requestingUserId
                     + ", " + forceNotify + ")");
         }
         return mutateGlobalSetting(name, value, tag, makeDefault, requestingUserId,
-                MUTATION_OPERATION_INSERT, forceNotify, 0);
+                MUTATION_OPERATION_INSERT, forceNotify, 0, overrideableByRestore);
     }
 
     private boolean deleteGlobalSetting(String name, int requestingUserId, boolean forceNotify) {
@@ -1221,6 +1231,15 @@
     private boolean mutateGlobalSetting(String name, String value, String tag,
             boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
             int mode) {
+        // overrideableByRestore = false as by default settings values shouldn't be overrideable by
+        // restore.
+        return mutateGlobalSetting(name, value, tag, makeDefault, requestingUserId, operation,
+                forceNotify, mode, /* overrideableByRestore */ false);
+    }
+
+    private boolean mutateGlobalSetting(String name, String value, String tag,
+            boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
+            int mode, boolean overrideableByRestore) {
         // Make sure the caller can change the settings - treated as secure.
         enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
 
@@ -1239,7 +1258,8 @@
                 case MUTATION_OPERATION_INSERT: {
                     return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_GLOBAL,
                             UserHandle.USER_SYSTEM, name, value, tag, makeDefault,
-                            getCallingPackage(), forceNotify, CRITICAL_GLOBAL_SETTINGS);
+                            getCallingPackage(), forceNotify,
+                            CRITICAL_GLOBAL_SETTINGS, overrideableByRestore);
                 }
 
                 case MUTATION_OPERATION_DELETE: {
@@ -1475,7 +1495,7 @@
             ) {
                 @Override
                 public boolean update(String value, boolean setDefault, String packageName,
-                        String tag, boolean forceNonSystemPackage) {
+                        String tag, boolean forceNonSystemPackage, boolean overrideableByRestore) {
                     Slog.wtf(LOG_TAG, "update shouldn't be called on this instance.");
                     return false;
                 }
@@ -1484,14 +1504,15 @@
     }
 
     private boolean insertSecureSetting(String name, String value, String tag,
-            boolean makeDefault, int requestingUserId, boolean forceNotify) {
+            boolean makeDefault, int requestingUserId, boolean forceNotify,
+            boolean overrideableByRestore) {
         if (DEBUG) {
             Slog.v(LOG_TAG, "insertSecureSetting(" + name + ", " + value + ", "
                     + ", " + tag  + ", " + makeDefault + ", "  + requestingUserId
                     + ", " + forceNotify + ")");
         }
         return mutateSecureSetting(name, value, tag, makeDefault, requestingUserId,
-                MUTATION_OPERATION_INSERT, forceNotify, 0);
+                MUTATION_OPERATION_INSERT, forceNotify, 0, overrideableByRestore);
     }
 
     private boolean deleteSecureSetting(String name, int requestingUserId, boolean forceNotify) {
@@ -1529,6 +1550,15 @@
     private boolean mutateSecureSetting(String name, String value, String tag,
             boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
             int mode) {
+        // overrideableByRestore = false as by default settings values shouldn't be overrideable by
+        // restore.
+        return mutateSecureSetting(name, value, tag, makeDefault, requestingUserId, operation,
+                forceNotify, mode, /* overrideableByRestore */ false);
+    }
+
+    private boolean mutateSecureSetting(String name, String value, String tag,
+            boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
+            int mode, boolean overrideableByRestore) {
         // Make sure the caller can change the settings.
         enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
 
@@ -1561,7 +1591,8 @@
                 case MUTATION_OPERATION_INSERT: {
                     return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE,
                             owningUserId, name, value, tag, makeDefault,
-                            getCallingPackage(), forceNotify, CRITICAL_SECURE_SETTINGS);
+                            getCallingPackage(), forceNotify, CRITICAL_SECURE_SETTINGS,
+                            overrideableByRestore);
                 }
 
                 case MUTATION_OPERATION_DELETE: {
@@ -1637,13 +1668,15 @@
         }
     }
 
-    private boolean insertSystemSetting(String name, String value, int requestingUserId) {
+    private boolean insertSystemSetting(String name, String value, int requestingUserId,
+            boolean overrideableByRestore) {
         if (DEBUG) {
             Slog.v(LOG_TAG, "insertSystemSetting(" + name + ", " + value + ", "
                     + requestingUserId + ")");
         }
 
-        return mutateSystemSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT);
+        return mutateSystemSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT,
+                overrideableByRestore);
     }
 
     private boolean deleteSystemSetting(String name, int requestingUserId) {
@@ -1663,8 +1696,15 @@
         return mutateSystemSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE);
     }
 
-    private boolean mutateSystemSetting(String name, String value, int runAsUserId,
-            int operation) {
+    private boolean mutateSystemSetting(String name, String value, int runAsUserId, int operation) {
+        // overrideableByRestore = false as by default settings values shouldn't be overrideable by
+        // restore.
+        return mutateSystemSetting(name, value, runAsUserId, operation,
+                /* overrideableByRestore */ false);
+    }
+
+    private boolean mutateSystemSetting(String name, String value, int runAsUserId, int operation,
+            boolean overrideableByRestore) {
         if (!hasWriteSecureSettingsPermission()) {
             // If the caller doesn't hold WRITE_SECURE_SETTINGS, we verify whether this
             // operation is allowed for the calling package through appops.
@@ -1714,7 +1754,7 @@
                     validateSystemSettingValue(name, value);
                     return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM,
                             owningUserId, name, value, null, false, getCallingPackage(),
-                            false, null);
+                            false, null, overrideableByRestore);
                 }
 
                 case MUTATION_OPERATION_DELETE: {
@@ -2051,7 +2091,8 @@
         }
         return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE,
                 owningUserId, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, newProviders, tag,
-                makeDefault, getCallingPackage(), forceNotify, CRITICAL_SECURE_SETTINGS);
+                makeDefault, getCallingPackage(), forceNotify, CRITICAL_SECURE_SETTINGS,
+                /* overrideableByRestore */ false);
     }
 
     private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(
@@ -2145,6 +2186,10 @@
         return (args != null) && args.getBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY);
     }
 
+    private static boolean getSettingOverrideableByRestore(Bundle args) {
+        return (args != null) && args.getBoolean(Settings.CALL_METHOD_OVERRIDEABLE_BY_RESTORE_KEY);
+    }
+
     private static int getResetModeEnforcingPermission(Bundle args) {
         final int mode = (args != null) ? args.getInt(Settings.CALL_METHOD_RESET_MODE_KEY) : 0;
         switch (mode) {
@@ -2642,21 +2687,21 @@
 
         public boolean insertSettingLocked(int type, int userId, String name, String value,
                 String tag, boolean makeDefault, String packageName, boolean forceNotify,
-                Set<String> criticalSettings) {
+                Set<String> criticalSettings, boolean overrideableByRestore) {
             return insertSettingLocked(type, userId, name, value, tag, makeDefault, false,
-                    packageName, forceNotify, criticalSettings);
+                    packageName, forceNotify, criticalSettings, overrideableByRestore);
         }
 
         public boolean insertSettingLocked(int type, int userId, String name, String value,
                 String tag, boolean makeDefault, boolean forceNonSystemPackage, String packageName,
-                boolean forceNotify, Set<String> criticalSettings) {
+                boolean forceNotify, Set<String> criticalSettings, boolean overrideableByRestore) {
             final int key = makeKey(type, userId);
 
             boolean success = false;
             SettingsState settingsState = peekSettingsStateLocked(key);
             if (settingsState != null) {
                 success = settingsState.insertSettingLocked(name, value,
-                        tag, makeDefault, forceNonSystemPackage, packageName);
+                        tag, makeDefault, forceNonSystemPackage, packageName, overrideableByRestore);
             }
 
             if (success && criticalSettings != null && criticalSettings.contains(name)) {
@@ -3391,6 +3436,10 @@
              * for this user from the old to the new version. When you add a new
              * upgrade step you *must* update SETTINGS_VERSION.
              *
+             * All settings modifications should be made through
+             * {@link SettingsState#insertSettingOverrideableByRestoreLocked(String, String, String,
+             * boolean, String)} so that restore can override those values if needed.
+             *
              * This is an example of moving a setting from secure to global.
              *
              * // v119: Example settings changes.
@@ -3436,7 +3485,8 @@
                 // v120: Add double tap to wake setting.
                 if (currentVersion == 119) {
                     SettingsState secureSettings = getSecureSettingsLocked(userId);
-                    secureSettings.insertSettingLocked(Settings.Secure.DOUBLE_TAP_TO_WAKE,
+                    secureSettings.insertSettingOverrideableByRestoreLocked(
+                            Settings.Secure.DOUBLE_TAP_TO_WAKE,
                             getContext().getResources().getBoolean(
                                     R.bool.def_double_tap_to_wake) ? "1" : "0", null, true,
                             SettingsState.SYSTEM_PACKAGE_NAME);
@@ -3461,7 +3511,7 @@
                             Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT);
                     if (defaultComponent != null && !defaultComponent.isEmpty() &&
                         currentSetting.isNull()) {
-                        secureSettings.insertSettingLocked(
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
                                 Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
                                 defaultComponent, null, true, SettingsState.SYSTEM_PACKAGE_NAME);
                     }
@@ -3476,7 +3526,7 @@
                         Setting currentSetting = globalSettings.getSettingLocked(
                                 Settings.Global.ADD_USERS_WHEN_LOCKED);
                         if (currentSetting.isNull()) {
-                            globalSettings.insertSettingLocked(
+                            globalSettings.insertSettingOverrideableByRestoreLocked(
                                     Settings.Global.ADD_USERS_WHEN_LOCKED,
                                     getContext().getResources().getBoolean(
                                             R.bool.def_add_users_from_lockscreen) ? "1" : "0",
@@ -3490,8 +3540,9 @@
                     final SettingsState globalSettings = getGlobalSettingsLocked();
                     String defaultDisabledProfiles = (getContext().getResources().getString(
                             R.string.def_bluetooth_disabled_profiles));
-                    globalSettings.insertSettingLocked(Settings.Global.BLUETOOTH_DISABLED_PROFILES,
-                            defaultDisabledProfiles, null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+                    globalSettings.insertSettingOverrideableByRestoreLocked(
+                            Settings.Global.BLUETOOTH_DISABLED_PROFILES, defaultDisabledProfiles,
+                            null, true, SettingsState.SYSTEM_PACKAGE_NAME);
                     currentVersion = 124;
                 }
 
@@ -3502,7 +3553,7 @@
                     Setting currentSetting = secureSettings.getSettingLocked(
                             Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
                     if (currentSetting.isNull()) {
-                        secureSettings.insertSettingLocked(
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
                                 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
                                 getContext().getResources().getBoolean(
                                         R.bool.def_show_ime_with_hard_keyboard) ? "1" : "0",
@@ -3531,7 +3582,7 @@
                                 b.append(c.flattenToString());
                                 start = false;
                             }
-                            secureSettings.insertSettingLocked(
+                            secureSettings.insertSettingOverrideableByRestoreLocked(
                                     Settings.Secure.ENABLED_VR_LISTENERS, b.toString(),
                                     null, true, SettingsState.SYSTEM_PACKAGE_NAME);
                         }
@@ -3551,7 +3602,7 @@
                                 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
                         if (!showNotifications.isNull()) {
                             final SettingsState secureSettings = getSecureSettingsLocked(userId);
-                            secureSettings.insertSettingLocked(
+                            secureSettings.insertSettingOverrideableByRestoreLocked(
                                     Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
                                     showNotifications.getValue(), null, true,
                                     SettingsState.SYSTEM_PACKAGE_NAME);
@@ -3561,7 +3612,7 @@
                                 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
                         if (!allowPrivate.isNull()) {
                             final SettingsState secureSettings = getSecureSettingsLocked(userId);
-                            secureSettings.insertSettingLocked(
+                            secureSettings.insertSettingOverrideableByRestoreLocked(
                                     Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
                                     allowPrivate.getValue(), null, true,
                                     SettingsState.SYSTEM_PACKAGE_NAME);
@@ -3588,7 +3639,7 @@
                     final String oldValue = systemSecureSettings.getSettingLocked(
                             Settings.Secure.LONG_PRESS_TIMEOUT).getValue();
                     if (TextUtils.equals("500", oldValue)) {
-                        systemSecureSettings.insertSettingLocked(
+                        systemSecureSettings.insertSettingOverrideableByRestoreLocked(
                                 Settings.Secure.LONG_PRESS_TIMEOUT,
                                 String.valueOf(getContext().getResources().getInteger(
                                         R.integer.def_long_press_timeout_millis)),
@@ -3604,10 +3655,12 @@
                             getSettingLocked(Settings.Secure.DOZE_ENABLED).getValue());
 
                     if (dozeExplicitlyDisabled) {
-                        secureSettings.insertSettingLocked(Settings.Secure.DOZE_PICK_UP_GESTURE,
-                                "0", null, true, SettingsState.SYSTEM_PACKAGE_NAME);
-                        secureSettings.insertSettingLocked(Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
-                                "0", null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
+                                Settings.Secure.DOZE_PICK_UP_GESTURE, "0", null, true,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
+                                Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, "0", null, true,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
                     }
                     currentVersion = 131;
                 }
@@ -3618,7 +3671,7 @@
                     final String oldValue = systemSecureSettings.getSettingLocked(
                             Settings.Secure.MULTI_PRESS_TIMEOUT).getValue();
                     if (TextUtils.equals(null, oldValue)) {
-                        systemSecureSettings.insertSettingLocked(
+                        systemSecureSettings.insertSettingOverrideableByRestoreLocked(
                                 Settings.Secure.MULTI_PRESS_TIMEOUT,
                                 String.valueOf(getContext().getResources().getInteger(
                                         R.integer.def_multi_press_timeout_millis)),
@@ -3633,7 +3686,7 @@
                     final SettingsState systemSecureSettings = getSecureSettingsLocked(userId);
                     String defaultSyncParentSounds = (getContext().getResources()
                             .getBoolean(R.bool.def_sync_parent_sounds) ? "1" : "0");
-                    systemSecureSettings.insertSettingLocked(
+                    systemSecureSettings.insertSettingOverrideableByRestoreLocked(
                             Settings.Secure.SYNC_PARENT_SOUNDS, defaultSyncParentSounds,
                             null, true, SettingsState.SYSTEM_PACKAGE_NAME);
                     currentVersion = 133;
@@ -3646,9 +3699,9 @@
                             .isNull()) {
                         String defaultEndButtonBehavior = Integer.toString(getContext()
                                 .getResources().getInteger(R.integer.def_end_button_behavior));
-                        systemSettings.insertSettingLocked(Settings.System.END_BUTTON_BEHAVIOR,
-                                defaultEndButtonBehavior, null, true,
-                                SettingsState.SYSTEM_PACKAGE_NAME);
+                        systemSettings.insertSettingOverrideableByRestoreLocked(
+                                Settings.System.END_BUTTON_BEHAVIOR, defaultEndButtonBehavior, null,
+                                true, SettingsState.SYSTEM_PACKAGE_NAME);
                     }
                     currentVersion = 134;
                 }
@@ -3706,8 +3759,8 @@
 
                             if (ssaid.isNull() || ssaid.getValue() == null) {
                                 // Android Id doesn't exist for this package so create it.
-                                ssaidSettings.insertSettingLocked(uid, legacySsaid, null, true,
-                                        info.packageName);
+                                ssaidSettings.insertSettingOverrideableByRestoreLocked(uid,
+                                        legacySsaid, null, true, info.packageName);
                                 if (DEBUG) {
                                     Slog.d(LOG_TAG, "Keep the legacy ssaid for uid=" + uid);
                                 }
@@ -3727,13 +3780,14 @@
                             && secureSetting.getSettingLocked(
                             Settings.Secure.INSTALL_NON_MARKET_APPS).getValue().equals("0")) {
 
-                        secureSetting.insertSettingLocked(Settings.Secure.INSTALL_NON_MARKET_APPS,
-                                "1", null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+                        secureSetting.insertSettingOverrideableByRestoreLocked(
+                                Settings.Secure.INSTALL_NON_MARKET_APPS, "1", null, true,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
                         // For managed profiles with profile owners, DevicePolicyManagerService
                         // may want to set the user restriction in this case
-                        secureSetting.insertSettingLocked(
-                                Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, "1", null, true,
-                                SettingsState.SYSTEM_PACKAGE_NAME);
+                        secureSetting.insertSettingOverrideableByRestoreLocked(
+                                Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, "1", null,
+                                true, SettingsState.SYSTEM_PACKAGE_NAME);
                     }
                     currentVersion = 138;
                 }
@@ -3774,7 +3828,7 @@
                         Setting currentSetting = globalSettings.getSettingLocked(
                                 Settings.Global.WIFI_WAKEUP_ENABLED);
                         if (currentSetting.isNull()) {
-                            globalSettings.insertSettingLocked(
+                            globalSettings.insertSettingOverrideableByRestoreLocked(
                                     Settings.Global.WIFI_WAKEUP_ENABLED,
                                     getContext().getResources().getBoolean(
                                             R.bool.def_wifi_wakeup_enabled) ? "1" : "0",
@@ -3796,8 +3850,9 @@
                         if (defaultValue != null) {
                             Slog.d(LOG_TAG, "Setting [" + defaultValue + "] as Autofill Service "
                                     + "for user " + userId);
-                            secureSettings.insertSettingLocked(Settings.Secure.AUTOFILL_SERVICE,
-                                    defaultValue, null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+                            secureSettings.insertSettingOverrideableByRestoreLocked(
+                                    Settings.Secure.AUTOFILL_SERVICE, defaultValue, null, true,
+                                    SettingsState.SYSTEM_PACKAGE_NAME);
                         }
                     }
 
@@ -3848,7 +3903,7 @@
                         final Setting currentSetting = globalSettings.getSettingLocked(
                                 Global.DEFAULT_RESTRICT_BACKGROUND_DATA);
                         if (currentSetting.isNull()) {
-                            globalSettings.insertSettingLocked(
+                            globalSettings.insertSettingOverrideableByRestoreLocked(
                                     Global.DEFAULT_RESTRICT_BACKGROUND_DATA,
                                     getContext().getResources().getBoolean(
                                             R.bool.def_restrict_background_data) ? "1" : "0",
@@ -3867,7 +3922,7 @@
                         final String defaultValue = getContext().getResources().getString(
                                 R.string.def_backup_manager_constants);
                         if (!TextUtils.isEmpty(defaultValue)) {
-                            systemSecureSettings.insertSettingLocked(
+                            systemSecureSettings.insertSettingOverrideableByRestoreLocked(
                                     Settings.Secure.BACKUP_MANAGER_CONSTANTS, defaultValue, null,
                                     true, SettingsState.SYSTEM_PACKAGE_NAME);
                         }
@@ -3881,7 +3936,7 @@
                     final Setting currentSetting = globalSettings.getSettingLocked(
                             Settings.Global.MOBILE_DATA_ALWAYS_ON);
                     if (currentSetting.isNull()) {
-                        globalSettings.insertSettingLocked(
+                        globalSettings.insertSettingOverrideableByRestoreLocked(
                                 Settings.Global.MOBILE_DATA_ALWAYS_ON,
                                 getContext().getResources().getBoolean(
                                         R.bool.def_mobile_data_always_on) ? "1" : "0",
@@ -3917,7 +3972,7 @@
                     if (showNotificationBadges.isNull()) {
                         final boolean defaultValue = getContext().getResources().getBoolean(
                                 com.android.internal.R.bool.config_notificationBadging);
-                        systemSecureSettings.insertSettingLocked(
+                        systemSecureSettings.insertSettingOverrideableByRestoreLocked(
                                 Secure.NOTIFICATION_BADGING,
                                 defaultValue ? "1" : "0",
                                 null, true, SettingsState.SYSTEM_PACKAGE_NAME);
@@ -3934,7 +3989,7 @@
                         final String defaultValue = getContext().getResources().getString(
                                 R.string.def_backup_local_transport_parameters);
                         if (!TextUtils.isEmpty(defaultValue)) {
-                            systemSecureSettings.insertSettingLocked(
+                            systemSecureSettings.insertSettingOverrideableByRestoreLocked(
                                     Settings.Secure.BACKUP_LOCAL_TRANSPORT_PARAMETERS, defaultValue,
                                     null, true, SettingsState.SYSTEM_PACKAGE_NAME);
                         }
@@ -3957,7 +4012,7 @@
                     if (currentSetting.isNull()) {
                         String defaultZenDuration = Integer.toString(getContext()
                                 .getResources().getInteger(R.integer.def_zen_duration));
-                        globalSettings.insertSettingLocked(
+                        globalSettings.insertSettingOverrideableByRestoreLocked(
                                 Global.ZEN_DURATION, defaultZenDuration,
                                 null, true, SettingsState.SYSTEM_PACKAGE_NAME);
                     }
@@ -3973,7 +4028,7 @@
                         final String defaultValue = getContext().getResources().getString(
                                 R.string.def_backup_agent_timeout_parameters);
                         if (!TextUtils.isEmpty(defaultValue)) {
-                            globalSettings.insertSettingLocked(
+                            globalSettings.insertSettingOverrideableByRestoreLocked(
                                     Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS, defaultValue,
                                     null, true,
                                     SettingsState.SYSTEM_PACKAGE_NAME);
@@ -4002,7 +4057,7 @@
                             Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
                         // The default value is "1", check if user has turned it off.
                         if ("0".equals(showNotifications.getValue())) {
-                            secureSettings.insertSettingLocked(
+                            secureSettings.insertSettingOverrideableByRestoreLocked(
                                 Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, "0",
                                 null /* tag */, false /* makeDefault */,
                                 SettingsState.SYSTEM_PACKAGE_NAME);
@@ -4023,7 +4078,7 @@
                     String oldValue = globalSettings.getSettingLocked(
                             Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY).getValue();
                     if (TextUtils.equals(null, oldValue)) {
-                        globalSettings.insertSettingLocked(
+                        globalSettings.insertSettingOverrideableByRestoreLocked(
                                 Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
                                 Integer.toString(getContext().getResources().getInteger(
                                         R.integer.def_max_sound_trigger_detection_service_ops_per_day)),
@@ -4033,7 +4088,7 @@
                     oldValue = globalSettings.getSettingLocked(
                             Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT).getValue();
                     if (TextUtils.equals(null, oldValue)) {
-                        globalSettings.insertSettingLocked(
+                        globalSettings.insertSettingOverrideableByRestoreLocked(
                                 Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT,
                                 Integer.toString(getContext().getResources().getInteger(
                                         R.integer.def_sound_trigger_detection_service_op_timeout)),
@@ -4048,7 +4103,7 @@
                     final Setting currentSetting = secureSettings.getSettingLocked(
                             Secure.VOLUME_HUSH_GESTURE);
                     if (currentSetting.isNull()) {
-                        secureSettings.insertSettingLocked(
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
                                 Secure.VOLUME_HUSH_GESTURE,
                                 Integer.toString(Secure.VOLUME_HUSH_VIBRATE),
                                 null, true, SettingsState.SYSTEM_PACKAGE_NAME);
@@ -4069,7 +4124,7 @@
                     final Setting currentSetting = settings.getSettingLocked(
                             Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY);
                     if (currentSetting.isDefaultFromSystem()) {
-                        settings.insertSettingLocked(
+                        settings.insertSettingOverrideableByRestoreLocked(
                                 Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
                                 Integer.toString(getContext().getResources().getInteger(
                                         R.integer
@@ -4098,7 +4153,7 @@
                     Setting currentHushUsedSetting = secureSettings.getSettingLocked(
                             Secure.HUSH_GESTURE_USED);
                     if (currentHushUsedSetting.isNull()) {
-                        secureSettings.insertSettingLocked(
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
                                 Settings.Secure.HUSH_GESTURE_USED, "0", null, true,
                                 SettingsState.SYSTEM_PACKAGE_NAME);
                     }
@@ -4106,7 +4161,7 @@
                     Setting currentRingerToggleCountSetting = secureSettings.getSettingLocked(
                             Secure.MANUAL_RINGER_TOGGLE_COUNT);
                     if (currentRingerToggleCountSetting.isNull()) {
-                        secureSettings.insertSettingLocked(
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
                                 Settings.Secure.MANUAL_RINGER_TOGGLE_COUNT, "0", null, true,
                                 SettingsState.SYSTEM_PACKAGE_NAME);
                     }
@@ -4125,7 +4180,7 @@
                     final Setting currentSetting = systemSettings.getSettingLocked(
                             Settings.System.VIBRATE_WHEN_RINGING);
                     if (currentSetting.isNull()) {
-                        systemSettings.insertSettingLocked(
+                        systemSettings.insertSettingOverrideableByRestoreLocked(
                                 Settings.System.VIBRATE_WHEN_RINGING,
                                 getContext().getResources().getBoolean(
                                         R.bool.def_vibrate_when_ringing) ? "1" : "0",
@@ -4149,18 +4204,18 @@
 
                     // ZEN_DURATION
                     if (!globalZenDuration.isNull()) {
-                        secureSettings.insertSettingLocked(
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
                                 Secure.ZEN_DURATION, globalZenDuration.getValue(), null, false,
                                 SettingsState.SYSTEM_PACKAGE_NAME);
 
                         // set global zen duration setting to null since it's deprecated
-                        globalSettings.insertSettingLocked(
+                        globalSettings.insertSettingOverrideableByRestoreLocked(
                                 Global.ZEN_DURATION, null, null, true,
                                 SettingsState.SYSTEM_PACKAGE_NAME);
                     } else if (secureZenDuration.isNull()) {
                         String defaultZenDuration = Integer.toString(getContext()
                                 .getResources().getInteger(R.integer.def_zen_duration));
-                        secureSettings.insertSettingLocked(
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
                                 Secure.ZEN_DURATION, defaultZenDuration, null, true,
                                 SettingsState.SYSTEM_PACKAGE_NAME);
                     }
@@ -4169,7 +4224,7 @@
                     final Setting currentShowZenSettingSuggestion = secureSettings.getSettingLocked(
                             Secure.SHOW_ZEN_SETTINGS_SUGGESTION);
                     if (currentShowZenSettingSuggestion.isNull()) {
-                        secureSettings.insertSettingLocked(
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
                                 Secure.SHOW_ZEN_SETTINGS_SUGGESTION, "1",
                                 null, true, SettingsState.SYSTEM_PACKAGE_NAME);
                     }
@@ -4178,7 +4233,7 @@
                     final Setting currentUpdatedSetting = secureSettings.getSettingLocked(
                             Secure.ZEN_SETTINGS_UPDATED);
                     if (currentUpdatedSetting.isNull()) {
-                        secureSettings.insertSettingLocked(
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
                                 Secure.ZEN_SETTINGS_UPDATED, "0",
                                 null, true, SettingsState.SYSTEM_PACKAGE_NAME);
                     }
@@ -4187,7 +4242,7 @@
                     final Setting currentSettingSuggestionViewed = secureSettings.getSettingLocked(
                             Secure.ZEN_SETTINGS_SUGGESTION_VIEWED);
                     if (currentSettingSuggestionViewed.isNull()) {
-                        secureSettings.insertSettingLocked(
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
                                 Secure.ZEN_SETTINGS_SUGGESTION_VIEWED, "0",
                                 null, true, SettingsState.SYSTEM_PACKAGE_NAME);
                     }
@@ -4210,20 +4265,20 @@
 
                     if (!globalChargingSoundEnabled.isNull()) {
                         if (secureChargingSoundsEnabled.isNull()) {
-                            secureSettings.insertSettingLocked(
+                            secureSettings.insertSettingOverrideableByRestoreLocked(
                                     Secure.CHARGING_SOUNDS_ENABLED,
                                     globalChargingSoundEnabled.getValue(), null, false,
                                     SettingsState.SYSTEM_PACKAGE_NAME);
                         }
 
                         // set global charging_sounds_enabled setting to null since it's deprecated
-                        globalSettings.insertSettingLocked(
+                        globalSettings.insertSettingOverrideableByRestoreLocked(
                                 Global.CHARGING_SOUNDS_ENABLED, null, null, true,
                                 SettingsState.SYSTEM_PACKAGE_NAME);
                     } else if (secureChargingSoundsEnabled.isNull()) {
                         String defChargingSoundsEnabled = getContext().getResources()
                                 .getBoolean(R.bool.def_charging_sounds_enabled) ? "1" : "0";
-                        secureSettings.insertSettingLocked(
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
                                 Secure.CHARGING_SOUNDS_ENABLED, defChargingSoundsEnabled, null,
                                 true, SettingsState.SYSTEM_PACKAGE_NAME);
                     }
@@ -4235,7 +4290,7 @@
                     if (secureChargingVibrationEnabled.isNull()) {
                         String defChargingVibrationEnabled = getContext().getResources()
                                 .getBoolean(R.bool.def_charging_vibration_enabled) ? "1" : "0";
-                        secureSettings.insertSettingLocked(
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
                                 Secure.CHARGING_VIBRATION_ENABLED, defChargingVibrationEnabled,
                                 null, true, SettingsState.SYSTEM_PACKAGE_NAME);
                     }
@@ -4255,7 +4310,7 @@
                                     currentSetting.getValue());
                             if ((currentSettingIntegerValue
                                  & (1 << AudioManager.STREAM_VOICE_CALL)) == 0) {
-                                systemSettings.insertSettingLocked(
+                                systemSettings.insertSettingOverrideableByRestoreLocked(
                                     Settings.System.MUTE_STREAMS_AFFECTED,
                                     Integer.toString(
                                         currentSettingIntegerValue
@@ -4296,7 +4351,7 @@
                                             ? Secure.LOCATION_MODE_ON
                                             : Secure.LOCATION_MODE_OFF;
                         }
-                        secureSettings.insertSettingLocked(
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
                                 Secure.LOCATION_MODE, Integer.toString(defLocationMode),
                                 null, true, SettingsState.SYSTEM_PACKAGE_NAME);
                     }
@@ -4318,7 +4373,7 @@
                     Setting currentRampingRingerSetting = globalSettings.getSettingLocked(
                             Settings.Global.APPLY_RAMPING_RINGER);
                     if (currentRampingRingerSetting.isNull()) {
-                        globalSettings.insertSettingLocked(
+                        globalSettings.insertSettingOverrideableByRestoreLocked(
                                 Settings.Global.APPLY_RAMPING_RINGER,
                                 getContext().getResources().getBoolean(
                                         R.bool.def_apply_ramping_ringer) ? "1" : "0", null,
@@ -4344,7 +4399,7 @@
 
                     if (!notificationVibrationIntensity.isNull()
                             && ringVibrationIntensity.isNull()) {
-                        systemSettings.insertSettingLocked(
+                        systemSettings.insertSettingOverrideableByRestoreLocked(
                                 Settings.System.RING_VIBRATION_INTENSITY,
                                 notificationVibrationIntensity.getValue(),
                                 null , true, SettingsState.SYSTEM_PACKAGE_NAME);
@@ -4388,7 +4443,7 @@
                     if (awareEnabled.isNull()) {
                         final boolean defAwareEnabled = getContext().getResources().getBoolean(
                                 R.bool.def_aware_enabled);
-                        secureSettings.insertSettingLocked(
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
                                 Secure.AWARE_ENABLED, defAwareEnabled ? "1" : "0",
                                 null, true, SettingsState.SYSTEM_PACKAGE_NAME);
                     }
@@ -4408,7 +4463,7 @@
                     if (skipGesture.isNull()) {
                         final boolean defSkipGesture = getContext().getResources().getBoolean(
                                 R.bool.def_skip_gesture);
-                        secureSettings.insertSettingLocked(
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
                                 Secure.SKIP_GESTURE, defSkipGesture ? "1" : "0",
                                 null, true, SettingsState.SYSTEM_PACKAGE_NAME);
                     }
@@ -4419,7 +4474,7 @@
                     if (silenceGesture.isNull()) {
                         final boolean defSilenceGesture = getContext().getResources().getBoolean(
                                 R.bool.def_silence_gesture);
-                        secureSettings.insertSettingLocked(
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
                                 Secure.SILENCE_GESTURE, defSilenceGesture ? "1" : "0",
                                 null, true, SettingsState.SYSTEM_PACKAGE_NAME);
                     }
@@ -4447,7 +4502,7 @@
                     if (awareLockEnabled.isNull()) {
                         final boolean defAwareLockEnabled = getContext().getResources().getBoolean(
                                 R.bool.def_aware_lock_enabled);
-                        secureSettings.insertSettingLocked(
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
                                 Secure.AWARE_LOCK_ENABLED, defAwareLockEnabled ? "1" : "0",
                                 null, true, SettingsState.SYSTEM_PACKAGE_NAME);
                     }
@@ -4467,7 +4522,7 @@
                                     currentSetting.getValue());
                             if ((currentSettingIntegerValue
                                     & (1 << AudioManager.STREAM_BLUETOOTH_SCO)) == 0) {
-                                systemSettings.insertSettingLocked(
+                                systemSettings.insertSettingOverrideableByRestoreLocked(
                                         Settings.System.MUTE_STREAMS_AFFECTED,
                                         Integer.toString(
                                         currentSettingIntegerValue
@@ -4513,13 +4568,13 @@
                     if (oldValueWireless == null
                             || TextUtils.equals(oldValueWireless, defaultValueWired)) {
                         if (!TextUtils.isEmpty(defaultValueWireless)) {
-                            globalSettings.insertSettingLocked(
+                            globalSettings.insertSettingOverrideableByRestoreLocked(
                                     Global.WIRELESS_CHARGING_STARTED_SOUND, defaultValueWireless,
                                     null /* tag */, true /* makeDefault */,
                                     SettingsState.SYSTEM_PACKAGE_NAME);
                         } else if (!TextUtils.isEmpty(defaultValueWired)) {
                             // if the wireless sound is empty, use the wired charging sound
-                            globalSettings.insertSettingLocked(
+                            globalSettings.insertSettingOverrideableByRestoreLocked(
                                     Global.WIRELESS_CHARGING_STARTED_SOUND, defaultValueWired,
                                     null /* tag */, true /* makeDefault */,
                                     SettingsState.SYSTEM_PACKAGE_NAME);
@@ -4528,7 +4583,7 @@
 
                     // wired charging sound
                     if (oldValueWired == null && !TextUtils.isEmpty(defaultValueWired)) {
-                        globalSettings.insertSettingLocked(
+                        globalSettings.insertSettingOverrideableByRestoreLocked(
                                 Global.CHARGING_STARTED_SOUND, defaultValueWired,
                                 null /* tag */, true /* makeDefault */,
                                 SettingsState.SYSTEM_PACKAGE_NAME);
@@ -4540,8 +4595,8 @@
                     // Version 184: Reset the default for Global Settings: NOTIFICATION_BUBBLES
                     // This is originally set in version 182, however, the default value changed
                     // so this step is to ensure the value is updated to the correct default.
-                    getGlobalSettingsLocked().insertSettingLocked(Global.NOTIFICATION_BUBBLES,
-                            getContext().getResources().getBoolean(
+                    getGlobalSettingsLocked().insertSettingOverrideableByRestoreLocked(
+                            Global.NOTIFICATION_BUBBLES, getContext().getResources().getBoolean(
                                     R.bool.def_notification_bubbles) ? "1" : "0", null /* tag */,
                             true /* makeDefault */, SettingsState.SYSTEM_PACKAGE_NAME);
 
@@ -4617,7 +4672,7 @@
                     final boolean systemSet = SettingsState.isSystemPackage(getContext(),
                             setting.getPackageName(), callingUid, userId);
                     if (systemSet) {
-                        settings.insertSettingLocked(name, setting.getValue(),
+                        settings.insertSettingOverrideableByRestoreLocked(name, setting.getValue(),
                                 setting.getTag(), true, setting.getPackageName());
                     } else if (setting.getDefaultValue() != null && setting.isDefaultFromSystem()) {
                         // We had a bug where changes by non-system packages were marked
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 5b1b530..db18213a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -117,6 +117,8 @@
     private static final String ATTR_NAMESPACE = "namespace";
     private static final String ATTR_BANNED_HASH = "bannedHash";
 
+    private static final String ATTR_PRESERVE_IN_RESTORE = "preserve_in_restore";
+
     /**
      * Non-binary value will be written in this attributes.
      */
@@ -388,15 +390,25 @@
 
     // The settings provider must hold its lock when calling here.
     @GuardedBy("mLock")
-    public boolean insertSettingLocked(String name, String value, String tag,
+    public boolean insertSettingOverrideableByRestoreLocked(String name, String value, String tag,
             boolean makeDefault, String packageName) {
-        return insertSettingLocked(name, value, tag, makeDefault, false, packageName);
+        return insertSettingLocked(name, value, tag, makeDefault, false, packageName,
+                /* overrideableByRestore */ true);
     }
 
     // The settings provider must hold its lock when calling here.
     @GuardedBy("mLock")
     public boolean insertSettingLocked(String name, String value, String tag,
-            boolean makeDefault, boolean forceNonSystemPackage, String packageName) {
+            boolean makeDefault, String packageName) {
+        return insertSettingLocked(name, value, tag, makeDefault, false, packageName,
+                /* overrideableByRestore */ false);
+    }
+
+    // The settings provider must hold its lock when calling here.
+    @GuardedBy("mLock")
+    public boolean insertSettingLocked(String name, String value, String tag,
+            boolean makeDefault, boolean forceNonSystemPackage, String packageName,
+            boolean overrideableByRestore) {
         if (TextUtils.isEmpty(name)) {
             return false;
         }
@@ -407,7 +419,8 @@
         Setting newState;
 
         if (oldState != null) {
-            if (!oldState.update(value, makeDefault, packageName, tag, forceNonSystemPackage)) {
+            if (!oldState.update(value, makeDefault, packageName, tag, forceNonSystemPackage,
+                    overrideableByRestore)) {
                 return false;
             }
             newState = oldState;
@@ -495,7 +508,8 @@
                 changedKeys.add(key); // key was added
             } else if (state.value != value) {
                 oldValue = state.value;
-                state.update(value, false, packageName, null, true);
+                state.update(value, false, packageName, null, true,
+                        /* overrideableByRestore */ false);
                 changedKeys.add(key); // key was updated
             } else {
                 // this key/value already exists, no change and no logging necessary
@@ -797,7 +811,8 @@
 
                     writeSingleSetting(mVersion, serializer, setting.getId(), setting.getName(),
                             setting.getValue(), setting.getDefaultValue(), setting.getPackageName(),
-                            setting.getTag(), setting.isDefaultFromSystem());
+                            setting.getTag(), setting.isDefaultFromSystem(),
+                            setting.isValuePreservedInRestore());
 
                     if (DEBUG_PERSISTENCE) {
                         Slog.i(LOG_TAG, "[PERSISTED]" + setting.getName() + "="
@@ -886,7 +901,8 @@
 
     static void writeSingleSetting(int version, XmlSerializer serializer, String id,
             String name, String value, String defaultValue, String packageName,
-            String tag, boolean defaultSysSet) throws IOException {
+            String tag, boolean defaultSysSet, boolean isValuePreservedInRestore)
+            throws IOException {
         if (id == null || isBinary(id) || name == null || isBinary(name)
                 || packageName == null || isBinary(packageName)) {
             // This shouldn't happen.
@@ -905,6 +921,9 @@
             setValueAttribute(ATTR_TAG, ATTR_TAG_BASE64,
                     version, serializer, tag);
         }
+        if (isValuePreservedInRestore) {
+            serializer.attribute(null, ATTR_PRESERVE_IN_RESTORE, Boolean.toString(true));
+        }
         serializer.endTag(null, TAG_SETTING);
     }
 
@@ -1041,6 +1060,10 @@
                 String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
                 String defaultValue = getValueAttribute(parser, ATTR_DEFAULT_VALUE,
                         ATTR_DEFAULT_VALUE_BASE64);
+                String isPreservedInRestoreString = parser.getAttributeValue(null,
+                        ATTR_PRESERVE_IN_RESTORE);
+                boolean isPreservedInRestore = isPreservedInRestoreString != null
+                        && Boolean.parseBoolean(isPreservedInRestoreString);
                 String tag = null;
                 boolean fromSystem = false;
                 if (defaultValue != null) {
@@ -1049,7 +1072,7 @@
                     tag = getValueAttribute(parser, ATTR_TAG, ATTR_TAG_BASE64);
                 }
                 mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag,
-                        fromSystem, id));
+                        fromSystem, id, isPreservedInRestore));
 
                 if (DEBUG_PERSISTENCE) {
                     Slog.i(LOG_TAG, "[RESTORED] " + name + "=" + value);
@@ -1133,6 +1156,8 @@
         private String tag;
         // Whether the default is set by the system
         private boolean defaultFromSystem;
+        // Whether the value of this setting will be preserved when restore happens.
+        private boolean isValuePreservedInRestore;
 
         public Setting(Setting other) {
             name = other.name;
@@ -1142,25 +1167,38 @@
             id = other.id;
             defaultFromSystem = other.defaultFromSystem;
             tag = other.tag;
+            isValuePreservedInRestore = other.isValuePreservedInRestore;
         }
 
         public Setting(String name, String value, boolean makeDefault, String packageName,
                 String tag) {
             this.name = name;
-            update(value, makeDefault, packageName, tag, false);
+            // overrideableByRestore = true as the first initialization isn't considered a
+            // modification.
+            update(value, makeDefault, packageName, tag, false,
+                    /* overrideableByRestore */ true);
         }
 
         public Setting(String name, String value, String defaultValue,
                 String packageName, String tag, boolean fromSystem, String id) {
+            this(name, value, defaultValue, packageName, tag, fromSystem, id,
+                    /* isOverrideableByRestore */ false);
+        }
+
+        Setting(String name, String value, String defaultValue,
+                String packageName, String tag, boolean fromSystem, String id,
+                boolean isValuePreservedInRestore) {
             mNextId = Math.max(mNextId, Long.parseLong(id) + 1);
             if (NULL_VALUE.equals(value)) {
                 value = null;
             }
-            init(name, value, tag, defaultValue, packageName, fromSystem, id);
+            init(name, value, tag, defaultValue, packageName, fromSystem, id,
+                    isValuePreservedInRestore);
         }
 
         private void init(String name, String value, String tag, String defaultValue,
-                String packageName, boolean fromSystem, String id) {
+                String packageName, boolean fromSystem, String id,
+                boolean isValuePreservedInRestore) {
             this.name = name;
             this.value = value;
             this.tag = tag;
@@ -1168,6 +1206,7 @@
             this.packageName = packageName;
             this.id = id;
             this.defaultFromSystem = fromSystem;
+            this.isValuePreservedInRestore = isValuePreservedInRestore;
         }
 
         public String getName() {
@@ -1198,6 +1237,10 @@
             return defaultFromSystem;
         }
 
+        public boolean isValuePreservedInRestore() {
+            return isValuePreservedInRestore;
+        }
+
         public String getId() {
             return id;
         }
@@ -1208,7 +1251,9 @@
 
         /** @return whether the value changed */
         public boolean reset() {
-            return update(this.defaultValue, false, packageName, null, true);
+            // overrideableByRestore = true as resetting to default value isn't considered a
+            // modification.
+            return update(this.defaultValue, false, packageName, null, true, true);
         }
 
         public boolean isTransient() {
@@ -1220,7 +1265,7 @@
         }
 
         public boolean update(String value, boolean setDefault, String packageName, String tag,
-                boolean forceNonSystemPackage) {
+                boolean forceNonSystemPackage, boolean overrideableByRestore) {
             if (NULL_VALUE.equals(value)) {
                 value = null;
             }
@@ -1253,17 +1298,22 @@
                 }
             }
 
+            // isValuePreservedInRestore shouldn't change back to false if it has been set to true.
+            boolean isPreserved = this.isValuePreservedInRestore || !overrideableByRestore;
+
             // Is something gonna change?
             if (Objects.equals(value, this.value)
                     && Objects.equals(defaultValue, this.defaultValue)
                     && Objects.equals(packageName, this.packageName)
                     && Objects.equals(tag, this.tag)
-                    && defaultFromSystem == this.defaultFromSystem) {
+                    && defaultFromSystem == this.defaultFromSystem
+                    && isPreserved == this.isValuePreservedInRestore) {
                 return false;
             }
 
             init(name, value, tag, defaultValue, packageName, defaultFromSystem,
-                    String.valueOf(mNextId++));
+                    String.valueOf(mNextId++), isPreserved);
+
             return true;
         }
 
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index 3f68554..b855d87 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -46,6 +46,18 @@
             "\uD800ab\uDC00 " + // broken surrogate pairs
             "日本語";
 
+    private static final String TEST_PACKAGE = "package";
+    private static final String SETTING_NAME = "test_setting";
+
+    private final Object mLock = new Object();
+
+    private File mSettingsFile;
+
+    @Override
+    protected void setUp() {
+        mSettingsFile = new File(getContext().getCacheDir(), "setting.xml");
+        mSettingsFile.delete();
+    }
 
     public void testIsBinary() {
         assertFalse(SettingsState.isBinary(" abc 日本語"));
@@ -99,10 +111,10 @@
         checkWriteSingleSetting(serializer, CRAZY_STRING, null);
         SettingsState.writeSingleSetting(
                 SettingsState.SETTINGS_VERSION_NEW_ENCODING,
-                serializer, null, "k", "v", null, "package", null, false);
+                serializer, null, "k", "v", null, "package", null, false, false);
         SettingsState.writeSingleSetting(
                 SettingsState.SETTINGS_VERSION_NEW_ENCODING,
-                serializer, "1", "k", "v", null, null, null, false);
+                serializer, "1", "k", "v", null, null, null, false, false);
     }
 
     private void checkWriteSingleSetting(XmlSerializer serializer, String key, String value)
@@ -115,7 +127,7 @@
         // Make sure the XML serializer won't crash.
         SettingsState.writeSingleSetting(
                 SettingsState.SETTINGS_VERSION_NEW_ENCODING,
-                serializer, "1", key, value, null, "package", null, false);
+                serializer, "1", key, value, null, "package", null, false, false);
     }
 
     /**
@@ -182,4 +194,57 @@
             assertEquals("p2", s.getPackageName());
         }
     }
+
+    public void testInitializeSetting_preserveFlagNotSet() {
+        SettingsState settingsWriter = getSettingStateObject();
+        settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
+        settingsWriter.persistSyncLocked();
+
+        SettingsState settingsReader = getSettingStateObject();
+        assertFalse(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
+    }
+
+    public void testModifySetting_preserveFlagSet() {
+        SettingsState settingsWriter = getSettingStateObject();
+        settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
+        settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, TEST_PACKAGE);
+        settingsWriter.persistSyncLocked();
+
+        SettingsState settingsReader = getSettingStateObject();
+        assertTrue(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
+    }
+
+    public void testModifySettingOverrideableByRestore_preserveFlagNotSet() {
+        SettingsState settingsWriter = getSettingStateObject();
+        settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
+        settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, false, TEST_PACKAGE,
+                /* overrideableByRestore */ true);
+        settingsWriter.persistSyncLocked();
+
+        SettingsState settingsReader = getSettingStateObject();
+        assertFalse(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
+    }
+
+    public void testModifySettingOverrideableByRestore_preserveFlagAlreadySet_flagValueUnchanged() {
+        SettingsState settingsWriter = getSettingStateObject();
+        // Init the setting.
+        settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
+        // This modification will set isValuePreservedInRestore = true.
+        settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
+        // This modification shouldn't change the value of isValuePreservedInRestore since it's
+        // already been set to true.
+        settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, false, TEST_PACKAGE,
+                /* overrideableByRestore */ true);
+        settingsWriter.persistSyncLocked();
+
+        SettingsState settingsReader = getSettingStateObject();
+        assertTrue(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
+    }
+
+    private SettingsState getSettingStateObject() {
+        SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1,
+                SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+        settingsState.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING);
+        return settingsState;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index a58e3d7..65fc215 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -31,8 +31,8 @@
 import static android.os.BatteryManager.EXTRA_PLUGGED;
 import static android.os.BatteryManager.EXTRA_STATUS;
 import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
+import static android.telephony.TelephonyManager.MODEM_COUNT_DUAL_MODEM;
 
-import static com.android.internal.telephony.PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -92,7 +92,6 @@
 import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settingslib.WirelessUtils;
@@ -446,7 +445,7 @@
      */
     public List<SubscriptionInfo> getFilteredSubscriptionInfo(boolean forceReload) {
         List<SubscriptionInfo> subscriptions = getSubscriptionInfo(false);
-        if (subscriptions.size() == MAX_PHONE_COUNT_DUAL_SIM) {
+        if (subscriptions.size() == MODEM_COUNT_DUAL_MODEM) {
             SubscriptionInfo info1 = subscriptions.get(0);
             SubscriptionInfo info2 = subscriptions.get(1);
             if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) {
@@ -1074,7 +1073,7 @@
                 mHandler.sendEmptyMessage(MSG_AIRPLANE_MODE_CHANGED);
             } else if (Intent.ACTION_SERVICE_STATE.equals(action)) {
                 ServiceState serviceState = ServiceState.newFromBundle(intent.getExtras());
-                int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+                int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
                 if (DEBUG) {
                     Log.v(TAG, "action " + action + " serviceState=" + serviceState + " subId="
@@ -1236,8 +1235,8 @@
                 throw new IllegalArgumentException("only handles intent ACTION_SIM_STATE_CHANGED");
             }
             String stateExtra = intent.getStringExtra(Intent.EXTRA_SIM_STATE);
-            int slotId = intent.getIntExtra(PhoneConstants.PHONE_KEY, 0);
-            int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+            int slotId = intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 0);
+            int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
                     SubscriptionManager.INVALID_SUBSCRIPTION_ID);
             if (Intent.SIM_STATE_ABSENT.equals(stateExtra)) {
                 final String absentReason = intent
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 895207d..898cd13 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -17,6 +17,8 @@
 package com.android.systemui.accessibility;
 
 import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.os.Handler;
 import android.provider.Settings;
@@ -35,10 +37,25 @@
     private WindowMagnificationController mWindowMagnificationController;
     private final Handler mHandler;
 
+    private Configuration mLastConfiguration;
+
     @Inject
     public WindowMagnification(Context context, @Main Handler mainHandler) {
         super(context);
         mHandler = mainHandler;
+        mLastConfiguration = new Configuration(context.getResources().getConfiguration());
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        final int configDiff = newConfig.diff(mLastConfiguration);
+        if ((configDiff & ActivityInfo.CONFIG_DENSITY) == 0) {
+            return;
+        }
+        mLastConfiguration.setTo(newConfig);
+        if (mWindowMagnificationController != null) {
+            mWindowMagnificationController.onConfigurationChanged(configDiff);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index bfac4fc..c243309 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -113,6 +113,7 @@
         if (mMirrorView != null) {
             return;
         }
+        setInitialStartBounds();
         createOverlayWindow();
     }
 
@@ -178,9 +179,20 @@
         }
     }
 
-    private void createMirrorWindow() {
-        setInitialStartBounds();
+    /**
+     * Called when the configuration has changed, and it updates window magnification UI.
+     *
+     * @param configDiff a bit mask of the differences between the configurations
+     */
+    void onConfigurationChanged(int configDiff) {
+        // TODO(b/145780606): update toggle button UI.
+        if (mMirrorView != null) {
+            mWm.removeView(mMirrorView);
+            createMirrorWindow();
+        }
+    }
 
+    private void createMirrorWindow() {
         // The window should be the size the mirrored surface will be but also add room for the
         // border and the drag handle.
         int dragViewHeight = (int) mContext.getResources().getDimension(
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 8987683..15c1c55 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -73,7 +73,6 @@
 import com.android.systemui.bubbles.animation.ExpandedAnimationController;
 import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
 import com.android.systemui.bubbles.animation.StackAnimationController;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -716,22 +715,6 @@
         return mExpandedBubble;
     }
 
-    /**
-     * Sets the bubble that should be expanded and expands if needed.
-     *
-     * @param key the {@link NotificationEntry#key} associated with the bubble to expand.
-     * @deprecated replaced by setSelectedBubble(Bubble) + setExpanded(true)
-     */
-    @Deprecated
-    void setExpandedBubble(String key) {
-        Bubble bubbleToExpand = mBubbleData.getBubbleWithKey(key);
-        if (bubbleToExpand != null) {
-            setSelectedBubble(bubbleToExpand);
-            bubbleToExpand.setShowInShade(false);
-            setExpanded(true);
-        }
-    }
-
     // via BubbleData.Listener
     void addBubble(Bubble bubble) {
         if (DEBUG_BUBBLE_STACK_VIEW) {
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 820c042..f7fe064 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
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.collection
 
+import android.app.NotificationChannel
 import android.app.NotificationManager.IMPORTANCE_HIGH
 import android.app.NotificationManager.IMPORTANCE_MIN
 import android.service.notification.NotificationListenerService.Ranking
@@ -191,9 +192,9 @@
     }
 
     private fun NotificationEntry.isPeopleNotification() =
-            sbn.isPeopleNotification()
-    private fun StatusBarNotification.isPeopleNotification() =
-            peopleNotificationIdentifier.isPeopleNotification(this)
+            sbn.isPeopleNotification(channel)
+    private fun StatusBarNotification.isPeopleNotification(channel: NotificationChannel) =
+            peopleNotificationIdentifier.isPeopleNotification(this, channel)
 
     private fun NotificationEntry.isHighPriority() =
             highPriorityProvider.isHighPriority(this)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
index 2c6a165..2eec68b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification.collection.coalescer;
 
+import androidx.annotation.Nullable;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -35,6 +37,8 @@
      */
     final List<CoalescedEvent> mMembers = new ArrayList<>();
 
+    @Nullable Runnable mCancelShortTimeout;
+
     EventBatch(long createdTimestamp, String groupKey) {
         mCreatedTimestamp = createdTimestamp;
         this.mGroupKey = groupKey;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index 8076616..f589038 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.collection.coalescer;
 
+import static com.android.systemui.statusbar.notification.logging.NotifEvent.BATCH_MAX_TIMEOUT;
 import static com.android.systemui.statusbar.notification.logging.NotifEvent.COALESCED_EVENT;
 import static com.android.systemui.statusbar.notification.logging.NotifEvent.EARLY_BATCH_EMIT;
 import static com.android.systemui.statusbar.notification.logging.NotifEvent.EMIT_EVENT_BATCH;
@@ -71,7 +72,8 @@
     private final DelayableExecutor mMainExecutor;
     private final SystemClock mClock;
     private final NotifLog mLog;
-    private final long mGroupLingerDuration;
+    private final long mMinGroupLingerDuration;
+    private final long mMaxGroupLingerDuration;
 
     private BatchableNotificationHandler mHandler;
 
@@ -82,22 +84,28 @@
     public GroupCoalescer(
             @Main DelayableExecutor mainExecutor,
             SystemClock clock, NotifLog log) {
-        this(mainExecutor, clock, log, GROUP_LINGER_DURATION);
+        this(mainExecutor, clock, log, MIN_GROUP_LINGER_DURATION, MAX_GROUP_LINGER_DURATION);
     }
 
     /**
-     * @param groupLingerDuration How long, in ms, that notifications that are members of a group
-     *                            are delayed within the GroupCoalescer before being posted
+     * @param minGroupLingerDuration How long, in ms, to wait for another notification from the same
+     *                               group to arrive before emitting all pending events for that
+     *                               group. Each subsequent arrival of a group member resets the
+     *                               timer for that group.
+     * @param maxGroupLingerDuration The maximum time, in ms, that a group can linger in the
+     *                               coalescer before it's force-emitted.
      */
     GroupCoalescer(
             @Main DelayableExecutor mainExecutor,
             SystemClock clock,
             NotifLog log,
-            long groupLingerDuration) {
+            long minGroupLingerDuration,
+            long maxGroupLingerDuration) {
         mMainExecutor = mainExecutor;
         mClock = clock;
         mLog = log;
-        mGroupLingerDuration = groupLingerDuration;
+        mMinGroupLingerDuration = minGroupLingerDuration;
+        mMaxGroupLingerDuration = maxGroupLingerDuration;
     }
 
     /**
@@ -115,7 +123,7 @@
     private final NotificationHandler mListener = new NotificationHandler() {
         @Override
         public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
-            maybeEmitBatch(sbn.getKey());
+            maybeEmitBatch(sbn);
             applyRanking(rankingMap);
 
             final boolean shouldCoalesce = handleNotificationPosted(sbn, rankingMap);
@@ -130,7 +138,7 @@
 
         @Override
         public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
-            maybeEmitBatch(sbn.getKey());
+            maybeEmitBatch(sbn);
             applyRanking(rankingMap);
             mHandler.onNotificationRemoved(sbn, rankingMap);
         }
@@ -140,7 +148,7 @@
                 StatusBarNotification sbn,
                 RankingMap rankingMap,
                 int reason) {
-            maybeEmitBatch(sbn.getKey());
+            maybeEmitBatch(sbn);
             applyRanking(rankingMap);
             mHandler.onNotificationRemoved(sbn, rankingMap, reason);
         }
@@ -152,13 +160,20 @@
         }
     };
 
-    private void maybeEmitBatch(String memberKey) {
-        CoalescedEvent event = mCoalescedEvents.get(memberKey);
+    private void maybeEmitBatch(StatusBarNotification sbn) {
+        final CoalescedEvent event = mCoalescedEvents.get(sbn.getKey());
+        final EventBatch batch = mBatches.get(sbn.getGroupKey());
         if (event != null) {
             mLog.log(EARLY_BATCH_EMIT,
                     String.format("Modification of %s triggered early emit of batched group %s",
-                            memberKey, requireNonNull(event.getBatch()).mGroupKey));
+                            sbn.getKey(), requireNonNull(event.getBatch()).mGroupKey));
             emitBatch(requireNonNull(event.getBatch()));
+        } else if (batch != null
+                && mClock.uptimeMillis() - batch.mCreatedTimestamp >= mMaxGroupLingerDuration) {
+            mLog.log(BATCH_MAX_TIMEOUT,
+                    String.format("Modification of %s triggered timeout emit of batched group %s",
+                            sbn.getKey(), batch.mGroupKey));
+            emitBatch(batch);
         }
     }
 
@@ -175,7 +190,8 @@
         }
 
         if (sbn.isGroup()) {
-            EventBatch batch = startBatchingGroup(sbn.getGroupKey());
+            final EventBatch batch = getOrBuildBatch(sbn.getGroupKey());
+
             CoalescedEvent event =
                     new CoalescedEvent(
                             sbn.getKey(),
@@ -183,10 +199,10 @@
                             sbn,
                             requireRanking(rankingMap, sbn.getKey()),
                             batch);
+            mCoalescedEvents.put(event.getKey(), event);
 
             batch.mMembers.add(event);
-
-            mCoalescedEvents.put(event.getKey(), event);
+            resetShortTimeout(batch);
 
             return true;
         } else {
@@ -194,27 +210,39 @@
         }
     }
 
-    private EventBatch startBatchingGroup(final String groupKey) {
+    private EventBatch getOrBuildBatch(final String groupKey) {
         EventBatch batch = mBatches.get(groupKey);
         if (batch == null) {
-            final EventBatch newBatch = new EventBatch(mClock.uptimeMillis(), groupKey);
-            mBatches.put(groupKey, newBatch);
-            mMainExecutor.executeDelayed(() -> emitBatch(newBatch), mGroupLingerDuration);
-
-            batch = newBatch;
+            batch = new EventBatch(mClock.uptimeMillis(), groupKey);
+            mBatches.put(groupKey, batch);
         }
         return batch;
     }
 
+    private void resetShortTimeout(EventBatch batch) {
+        if (batch.mCancelShortTimeout != null) {
+            batch.mCancelShortTimeout.run();
+        }
+        batch.mCancelShortTimeout =
+                mMainExecutor.executeDelayed(
+                        () -> {
+                            batch.mCancelShortTimeout = null;
+                            emitBatch(batch);
+                        },
+                        mMinGroupLingerDuration);
+    }
+
     private void emitBatch(EventBatch batch) {
         if (batch != mBatches.get(batch.mGroupKey)) {
-            // If we emit a batch early, we don't want to emit it a second time when its timeout
-            // expires.
-            return;
+            throw new IllegalStateException("Cannot emit out-of-date batch " + batch.mGroupKey);
         }
         if (batch.mMembers.isEmpty()) {
             throw new IllegalStateException("Batch " + batch.mGroupKey + " cannot be empty");
         }
+        if (batch.mCancelShortTimeout != null) {
+            batch.mCancelShortTimeout.run();
+            batch.mCancelShortTimeout = null;
+        }
 
         mBatches.remove(batch.mGroupKey);
 
@@ -299,5 +327,6 @@
         void onNotificationBatchPosted(List<CoalescedEvent> events);
     }
 
-    private static final int GROUP_LINGER_DURATION = 500;
+    private static final int MIN_GROUP_LINGER_DURATION = 50;
+    private static final int MAX_GROUP_LINGER_DURATION = 500;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
index 3cc5e62..ccd7fa3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
@@ -99,7 +99,8 @@
     }
 
     private boolean isPeopleNotification(NotificationEntry entry) {
-        return mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn());
+        return mPeopleNotificationIdentifier.isPeopleNotification(
+                entry.getSbn(), entry.getChannel());
     }
 
     private boolean hasUserSetImportance(NotificationEntry entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
index 2374cde..9adceb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
@@ -156,7 +156,8 @@
                     // GroupCoalescer labels:
                     "CoalescedEvent",
                     "EarlyBatchEmit",
-                    "EmitEventBatch"
+                    "EmitEventBatch",
+                    "BatchMaxTimeout"
             };
 
     private static final int TOTAL_EVENT_LABELS = EVENT_LABELS.length;
@@ -206,5 +207,6 @@
     public static final int COALESCED_EVENT = COALESCER_EVENT_START_INDEX;
     public static final int EARLY_BATCH_EMIT = COALESCER_EVENT_START_INDEX + 1;
     public static final int EMIT_EVENT_BATCH = COALESCER_EVENT_START_INDEX + 2;
+    public static final int BATCH_MAX_TIMEOUT = COALESCER_EVENT_START_INDEX + 3;
     private static final int TOTAL_COALESCER_EVENT_TYPES = 3;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
index 452d1eb..5c90211 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -18,13 +18,14 @@
 
 import android.app.Notification
 import android.content.Context
+import android.app.NotificationChannel
 import android.service.notification.StatusBarNotification
 import android.util.FeatureFlagUtils
 import javax.inject.Inject
 import javax.inject.Singleton
 
 interface PeopleNotificationIdentifier {
-    fun isPeopleNotification(sbn: StatusBarNotification): Boolean
+    fun isPeopleNotification(sbn: StatusBarNotification, channel: NotificationChannel): Boolean
 }
 
 @Singleton
@@ -33,12 +34,13 @@
     private val context: Context
 ) : PeopleNotificationIdentifier {
 
-    override fun isPeopleNotification(sbn: StatusBarNotification) =
-            (sbn.notification.notificationStyle == Notification.MessagingStyle::class.java &&
+    override fun isPeopleNotification(sbn: StatusBarNotification, channel: NotificationChannel) =
+            ((sbn.notification.notificationStyle == Notification.MessagingStyle::class.java &&
                     (sbn.notification.shortcutId != null ||
                             FeatureFlagUtils.isEnabled(
                                     context,
                                     FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ
                             ))) ||
-                    personExtractor.isPersonNotification(sbn)
+                    personExtractor.isPersonNotification(sbn)) &&
+                    !channel.isDemoted
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index ec420f3..3b106cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -25,6 +25,7 @@
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
 
 import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_BUBBLE;
+import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_DEMOTE;
 import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_FAVORITE;
 import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_HOME;
 import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_MUTE;
@@ -61,6 +62,7 @@
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.Button;
+import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -151,6 +153,11 @@
       closeControls(v, true);
     };
 
+    private OnClickListener mOnDemoteClick = v -> {
+        mSelectedAction = ACTION_DEMOTE;
+        closeControls(v, true);
+    };
+
     public NotificationConversationInfo(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -295,6 +302,8 @@
                     mContext.getDrawable(R.drawable.ic_notifications_alert), null, null, null);
         }
 
+        ImageButton demote = findViewById(R.id.demote);
+        demote.setOnClickListener(mOnDemoteClick);
     }
 
     private void bindHeader() {
@@ -609,7 +618,7 @@
                         }
                         break;
                     case ACTION_DEMOTE:
-                        // TODO: when demotion status field exists on notificationchannel
+                        mChannelToUpdate.setDemoted(!mChannelToUpdate.isDemoted());
                         break;
 
                 }
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 f05b585..cca100f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -26,6 +26,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.provider.Settings.Global;
+import android.telephony.Annotation;
 import android.telephony.CellSignalStrength;
 import android.telephony.CellSignalStrengthCdma;
 import android.telephony.NetworkRegistrationInfo;
@@ -37,7 +38,6 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.TelephonyIntents;
@@ -53,7 +53,9 @@
 
 import java.io.PrintWriter;
 import java.util.BitSet;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.Executor;
 import java.util.regex.Matcher;
@@ -77,12 +79,14 @@
     final SubscriptionInfo mSubscriptionInfo;
 
     // @VisibleForDemoMode
-    final SparseArray<MobileIconGroup> mNetworkToIconLookup;
+    final Map<String, MobileIconGroup> mNetworkToIconLookup;
 
     // Since some pieces of the phone state are interdependent we store it locally,
     // this could potentially become part of MobileState for simplification/complication
     // of code.
     private int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+    private boolean mCA = false;
+    private boolean mCAPlus = false;
     private int mDataState = TelephonyManager.DATA_DISCONNECTED;
     private ServiceState mServiceState;
     private SignalStrength mSignalStrength;
@@ -93,9 +97,6 @@
     boolean mInflateSignalStrengths = false;
     @VisibleForTesting
     boolean mIsShowingIconGracefully = false;
-    // Some specific carriers have 5GE network which is special LTE CA network.
-    private static final int NETWORK_TYPE_LTE_CA_5GE =
-            TelephonyManager.getAllNetworkTypes().length + 1;
 
     // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
     // need listener lists anymore.
@@ -106,7 +107,7 @@
         super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
                 NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler,
                 networkController);
-        mNetworkToIconLookup = new SparseArray<>();
+        mNetworkToIconLookup = new HashMap<>();
         mConfig = config;
         mPhone = phone;
         mDefaults = defaults;
@@ -213,29 +214,38 @@
     private void mapIconSets() {
         mNetworkToIconLookup.clear();
 
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyIcons.THREE_G);
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyIcons.THREE_G);
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_B, TelephonyIcons.THREE_G);
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyIcons.THREE_G);
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UMTS, TelephonyIcons.THREE_G);
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_TD_SCDMA, TelephonyIcons.THREE_G);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EVDO_0),
+                TelephonyIcons.THREE_G);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EVDO_A),
+                TelephonyIcons.THREE_G);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EVDO_B),
+                TelephonyIcons.THREE_G);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EHRPD),
+                TelephonyIcons.THREE_G);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UMTS),
+                TelephonyIcons.THREE_G);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_TD_SCDMA),
+                TelephonyIcons.THREE_G);
 
         if (!mConfig.showAtLeast3G) {
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UNKNOWN),
                     TelephonyIcons.UNKNOWN);
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE, TelephonyIcons.E);
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA, TelephonyIcons.ONE_X);
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyIcons.ONE_X);
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EDGE),
+                    TelephonyIcons.E);
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_CDMA),
+                    TelephonyIcons.ONE_X);
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_1xRTT),
+                    TelephonyIcons.ONE_X);
 
             mDefaultIcons = TelephonyIcons.G;
         } else {
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UNKNOWN),
                     TelephonyIcons.THREE_G);
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE,
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EDGE),
                     TelephonyIcons.THREE_G);
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA,
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_CDMA),
                     TelephonyIcons.THREE_G);
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT,
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_1xRTT),
                     TelephonyIcons.THREE_G);
             mDefaultIcons = TelephonyIcons.THREE_G;
         }
@@ -250,33 +260,59 @@
             hPlusGroup = TelephonyIcons.H_PLUS;
         }
 
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSDPA, hGroup);
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSUPA, hGroup);
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPA, hGroup);
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPAP, hPlusGroup);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSDPA), hGroup);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSUPA), hGroup);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSPA), hGroup);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSPAP), hPlusGroup);
 
         if (mConfig.show4gForLte) {
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.FOUR_G);
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_LTE),
+                    TelephonyIcons.FOUR_G);
             if (mConfig.hideLtePlus) {
-                mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+                mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
                         TelephonyIcons.FOUR_G);
             } else {
-                mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+                mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
                         TelephonyIcons.FOUR_G_PLUS);
             }
         } else {
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.LTE);
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_LTE),
+                    TelephonyIcons.LTE);
             if (mConfig.hideLtePlus) {
-                mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+                mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
                         TelephonyIcons.LTE);
             } else {
-                mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+                mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
                         TelephonyIcons.LTE_PLUS);
             }
         }
-        mNetworkToIconLookup.put(NETWORK_TYPE_LTE_CA_5GE,
+        mNetworkToIconLookup.put(toIconKeyCAPlus(TelephonyManager.NETWORK_TYPE_LTE),
                 TelephonyIcons.LTE_CA_5G_E);
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_IWLAN, TelephonyIcons.WFC);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_IWLAN),
+                TelephonyIcons.WFC);
+    }
+
+    private String getIconKey() {
+        if (mCA) {
+            return toIconKeyCA(mDataNetType);
+        } else if (mCAPlus) {
+            return toIconKeyCAPlus(mDataNetType);
+        } else {
+            return toIconKey(mDataNetType);
+        }
+    }
+
+    // Some specific carriers have 5GE network which is special CA network.
+    private String toIconKeyCAPlus(@Annotation.NetworkType int networkType) {
+        return toIconKeyCA(networkType) + "_Plus";
+    }
+
+    private String toIconKeyCA(@Annotation.NetworkType int networkType) {
+        return toIconKey(networkType) + "_CA";
+    }
+
+    private String toIconKey(@Annotation.NetworkType int networkType) {
+        return Integer.toString(networkType);
     }
 
     private void updateInflateSignalStrength() {
@@ -523,10 +559,11 @@
             nr5GIconGroup = adjustNr5GIconGroupByDisplayGraceTime(nr5GIconGroup);
         }
 
+        String iconKey = getIconKey();
         if (nr5GIconGroup != null) {
             mCurrentState.iconGroup = nr5GIconGroup;
-        } else if (mNetworkToIconLookup.indexOfKey(mDataNetType) >= 0) {
-            mCurrentState.iconGroup = mNetworkToIconLookup.get(mDataNetType);
+        } else if (mNetworkToIconLookup.get(iconKey) != null) {
+            mCurrentState.iconGroup = mNetworkToIconLookup.get(iconKey);
         } else {
             mCurrentState.iconGroup = mDefaultIcons;
         }
@@ -679,6 +716,8 @@
         pw.println("  mSignalStrength=" + mSignalStrength + ",");
         pw.println("  mDataState=" + mDataState + ",");
         pw.println("  mDataNetType=" + mDataNetType + ",");
+        pw.println("  mCA=" + mCA + ",");
+        pw.println("  mCAPlus=" + mCAPlus + ",");
         pw.println("  mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
         pw.println("  isDataDisabled=" + isDataDisabled() + ",");
         pw.println("  mIsShowingIconGracefully=" + mIsShowingIconGracefully + ",");
@@ -729,11 +768,13 @@
 
         private void updateDataNetType(int networkType) {
             mDataNetType = networkType;
+            mCA = false;
+            mCAPlus = false;
             if (mDataNetType == TelephonyManager.NETWORK_TYPE_LTE) {
                 if (isCarrierSpecificDataIcon()) {
-                    mDataNetType = NETWORK_TYPE_LTE_CA_5GE;
+                    mCAPlus = true;
                 } else if (mServiceState != null && mServiceState.isUsingCarrierAggregation()) {
-                    mDataNetType = TelephonyManager.NETWORK_TYPE_LTE_CA;
+                    mCA = true;
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 679fa7e..6b3c5dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -22,8 +22,7 @@
 import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE;
 import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT;
 import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
-
-import static com.android.internal.telephony.PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM;
+import static android.telephony.TelephonyManager.MODEM_COUNT_DUAL_MODEM;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -57,7 +56,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.settingslib.net.DataUsageController;
 import com.android.systemui.DemoMode;
@@ -547,7 +545,7 @@
                 mReceiverHandler.post(this::handleConfigurationChanged);
                 break;
             default:
-                int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+                int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
                 if (SubscriptionManager.isValidSubscriptionId(subId)) {
                     if (mMobileSignalControllers.indexOfKey(subId) >= 0) {
@@ -582,7 +580,7 @@
     }
 
     private void filterMobileSubscriptionInSameGroup(List<SubscriptionInfo> subscriptions) {
-        if (subscriptions.size() == MAX_PHONE_COUNT_DUAL_SIM) {
+        if (subscriptions.size() == MODEM_COUNT_DUAL_MODEM) {
             SubscriptionInfo info1 = subscriptions.get(0);
             SubscriptionInfo info2 = subscriptions.get(1);
             if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/Assert.java b/packages/SystemUI/src/com/android/systemui/util/Assert.java
index 096ac3f..f6e921e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Assert.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Assert.java
@@ -18,7 +18,7 @@
 
 import android.os.Looper;
 
-import com.android.internal.annotations.VisibleForTesting;
+import androidx.annotation.VisibleForTesting;
 
 /**
  * Helper providing common assertions.
@@ -30,7 +30,9 @@
 
     public static void isMainThread() {
         if (!sMainLooper.isCurrentThread()) {
-            throw new IllegalStateException("should be called from the main thread.");
+            throw new IllegalStateException("should be called from the main thread."
+                    + " sMainLooper.threadName=" + sMainLooper.getThread().getName()
+                    + " Thread.currentThread()=" + Thread.currentThread().getName());
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 2e0fb3b..12da006 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -57,7 +57,6 @@
 import android.testing.TestableContext;
 import android.testing.TestableLooper;
 
-import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.systemui.DumpController;
 import com.android.systemui.SysuiTestCase;
@@ -524,9 +523,9 @@
         int subscription = simInited
                 ? 1/* mock subid=1 */ : SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE;
         if (data != null) intent.putExtras(data);
-        intent.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone");
-        intent.putExtra("subscription", subscription);
-        intent.putExtra("slot", 0/* SLOT 1 */);
+
+        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subscription);
+        intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 0);
         return intent;
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index e0b4b81..c3df3f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -356,8 +356,7 @@
 
         // Switch which bubble is expanded
         mBubbleController.selectBubble(mRow.getEntry().getKey());
-        stackView.setExpandedBubble(mRow.getEntry().getKey());
-        assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
+        mBubbleController.expandStack();
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow.getEntry().getKey()));
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index cc5514f..d852fa1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -571,7 +571,7 @@
     }
 
     private NotificationEntry createNotification() {
-        Notification.Builder n = new Notification.Builder(mContext, "")
+        Notification.Builder n = new Notification.Builder(mContext, "id")
                 .setSmallIcon(R.drawable.ic_person)
                 .setContentTitle("Title")
                 .setContentText("Text");
@@ -582,6 +582,7 @@
                 .setUid(TEST_UID)
                 .setId(mId++)
                 .setNotification(n.build())
+                .setChannel(new NotificationChannel("id", "", IMPORTANCE_DEFAULT))
                 .setUser(new UserHandle(ActivityManager.getCurrentUser()))
                 .build();
     }
@@ -616,7 +617,7 @@
     @Test
     public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications() {
         Assert.sMainLooper = TestableLooper.get(this).getLooper();
-        Notification.Builder n = new Notification.Builder(mContext, "")
+        Notification.Builder n = new Notification.Builder(mContext, "di")
                 .setSmallIcon(R.drawable.ic_person)
                 .setContentTitle("Title")
                 .setContentText("Text");
@@ -628,6 +629,7 @@
                 .setId(mId++)
                 .setNotification(n.build())
                 .setUser(new UserHandle(ActivityManager.getCurrentUser()))
+                .setChannel(new NotificationChannel("id", "", IMPORTANCE_DEFAULT))
                 .build();
 
         mEntryManager.addActiveNotificationForTest(mEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
index 93909dc..7c3665b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
@@ -60,7 +60,8 @@
         final NotificationEntry entry = new NotificationEntryBuilder()
                 .setImportance(IMPORTANCE_HIGH)
                 .build();
-        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+                .thenReturn(false);
 
         // THEN it has high priority
         assertTrue(mHighPriorityProvider.isHighPriority(entry));
@@ -75,7 +76,8 @@
                 .setNotification(notification)
                 .setImportance(IMPORTANCE_LOW)
                 .build();
-        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(true);
+        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+                .thenReturn(true);
 
         // THEN it has high priority
         assertTrue(mHighPriorityProvider.isHighPriority(entry));
@@ -90,7 +92,8 @@
         final NotificationEntry entry = new NotificationEntryBuilder()
                 .setNotification(notification)
                 .build();
-        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+                .thenReturn(false);
 
         // THEN it has high priority
         assertTrue(mHighPriorityProvider.isHighPriority(entry));
@@ -106,7 +109,8 @@
                 .setNotification(notification)
                 .setImportance(IMPORTANCE_LOW)
                 .build();
-        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+                .thenReturn(false);
 
         // THEN it has high priority
         assertTrue(mHighPriorityProvider.isHighPriority(entry));
@@ -122,7 +126,8 @@
                 .setNotification(notification)
                 .setImportance(IMPORTANCE_MIN)
                 .build();
-        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+                .thenReturn(false);
 
         // THEN it does NOT have high priority
         assertFalse(mHighPriorityProvider.isHighPriority(entry));
@@ -144,7 +149,8 @@
                 .setNotification(notification)
                 .setChannel(channel)
                 .build();
-        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(true);
+        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+                .thenReturn(true);
 
         // THEN it does NOT have high priority
         assertFalse(mHighPriorityProvider.isHighPriority(entry));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index e273191..7ab4846 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.collection
 
 import android.app.Notification
+import android.app.NotificationChannel
 import android.app.NotificationManager.IMPORTANCE_DEFAULT
 import android.app.NotificationManager.IMPORTANCE_HIGH
 import android.app.NotificationManager.IMPORTANCE_LOW
@@ -81,6 +82,7 @@
                 .setNotification(
                         Notification.Builder(mContext, "test")
                                 .build())
+                .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
                 .setUser(mContext.getUser())
                 .setOverrideGroupKey("")
                 .build()
@@ -94,6 +96,7 @@
                 .setNotification(
                         Notification.Builder(mContext, "test")
                                 .build())
+                .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
                 .setUser(mContext.getUser())
                 .setOverrideGroupKey("")
                 .build()
@@ -116,6 +119,7 @@
                 .setOpPkg("pkg")
                 .setTag("tag")
                 .setNotification(aN)
+                .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
                 .setUser(mContext.getUser())
                 .setOverrideGroupKey("")
                 .build()
@@ -130,6 +134,7 @@
                 .setOpPkg("pkg2")
                 .setTag("tag")
                 .setNotification(bN)
+                .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
                 .setUser(mContext.getUser())
                 .setOverrideGroupKey("")
                 .build()
@@ -149,6 +154,7 @@
                 .setTag("tag")
                 .setNotification(notif)
                 .setUser(mContext.user)
+                .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
                 .setOverrideGroupKey("")
                 .build()
 
@@ -168,6 +174,7 @@
                 .setTag("tag")
                 .setNotification(notif)
                 .setUser(mContext.user)
+                .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
                 .setOverrideGroupKey("")
                 .build()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
index 5e0baf2..86c1eb97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
@@ -80,7 +80,8 @@
                         mExecutor,
                         mClock,
                         mLog,
-                        LINGER_DURATION);
+                        MIN_LINGER_DURATION,
+                        MAX_LINGER_DURATION);
         mCoalescer.setNotificationHandler(mListener);
         mCoalescer.attach(mListenerService);
 
@@ -96,7 +97,7 @@
                 new NotificationEntryBuilder()
                         .setId(0)
                         .setPkg(TEST_PACKAGE_A));
-        mClock.advanceTime(LINGER_DURATION);
+        mClock.advanceTime(MIN_LINGER_DURATION);
 
         // THEN the event is passed through to the handler
         verify(mListener).onNotificationPosted(notif1.sbn, notif1.rankingMap);
@@ -144,12 +145,16 @@
                 .setId(1)
                 .setGroup(mContext, GROUP_1));
 
+        mClock.advanceTime(2);
+
         NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setPkg(TEST_PACKAGE_A)
                 .setId(2)
                 .setGroup(mContext, GROUP_1)
                 .setGroupSummary(mContext, true));
 
+        mClock.advanceTime(3);
+
         NotifEvent notif3 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setPkg(TEST_PACKAGE_A)
                 .setId(3)
@@ -161,7 +166,7 @@
         verify(mListener, never()).onNotificationBatchPosted(anyList());
 
         // WHEN enough time passes
-        mClock.advanceTime(LINGER_DURATION);
+        mClock.advanceTime(MIN_LINGER_DURATION);
 
         // THEN the coalesced notifs are applied. The summary is sorted to the front.
         verify(mListener).onNotificationBatchPosted(Arrays.asList(
@@ -212,7 +217,7 @@
 
         // WHEN the time runs out on the remainder of the group
         clearInvocations(mListener);
-        mClock.advanceTime(LINGER_DURATION);
+        mClock.advanceTime(MIN_LINGER_DURATION);
 
         // THEN no lingering batch is applied
         verify(mListener, never()).onNotificationBatchPosted(anyList());
@@ -225,11 +230,13 @@
                 .setPkg(TEST_PACKAGE_A)
                 .setId(1)
                 .setGroup(mContext, GROUP_1));
+        mClock.advanceTime(2);
         NotifEvent notif2a = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setPkg(TEST_PACKAGE_A)
                 .setId(2)
                 .setContentTitle(mContext, "Version 1")
                 .setGroup(mContext, GROUP_1));
+        mClock.advanceTime(4);
 
         // WHEN one of them gets updated
         NotifEvent notif2b = mNoMan.postNotif(new NotificationEntryBuilder()
@@ -248,7 +255,7 @@
                 any(RankingMap.class));
 
         // THEN second, the update is emitted
-        mClock.advanceTime(LINGER_DURATION);
+        mClock.advanceTime(MIN_LINGER_DURATION);
         verify(mListener).onNotificationBatchPosted(Collections.singletonList(
                 new CoalescedEvent(notif2b.key, 0, notif2b.sbn, notif2b.ranking, null)
         ));
@@ -308,14 +315,61 @@
                 .setId(17));
 
         // THEN they have the new rankings when they are eventually emitted
-        mClock.advanceTime(LINGER_DURATION);
+        mClock.advanceTime(MIN_LINGER_DURATION);
         verify(mListener).onNotificationBatchPosted(Arrays.asList(
                 new CoalescedEvent(notif1.key, 0, notif1.sbn, ranking1b, null),
                 new CoalescedEvent(notif2.key, 1, notif2.sbn, ranking2b, null)
         ));
     }
 
-    private static final long LINGER_DURATION = 4700;
+    @Test
+    public void testMaxLingerDuration() {
+        // GIVEN five coalesced notifications that have collectively taken 20ms to arrive, 2ms
+        // longer than the max linger duration
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(1)
+                .setGroup(mContext, GROUP_1));
+        mClock.advanceTime(4);
+        NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(2)
+                .setGroup(mContext, GROUP_1));
+        mClock.advanceTime(4);
+        NotifEvent notif3 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(3)
+                .setGroup(mContext, GROUP_1));
+        mClock.advanceTime(4);
+        NotifEvent notif4 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(4)
+                .setGroup(mContext, GROUP_1));
+        mClock.advanceTime(4);
+        NotifEvent notif5 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(5)
+                .setGroup(mContext, GROUP_1));
+        mClock.advanceTime(4);
+
+        // WHEN a sixth notification arrives
+        NotifEvent notif6 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(6)
+                .setGroup(mContext, GROUP_1));
+
+        // THEN the first five notifications are emitted in a batch
+        verify(mListener).onNotificationBatchPosted(Arrays.asList(
+                new CoalescedEvent(notif1.key, 0, notif1.sbn, notif1.ranking, null),
+                new CoalescedEvent(notif2.key, 1, notif2.sbn, notif2.ranking, null),
+                new CoalescedEvent(notif3.key, 2, notif3.sbn, notif3.ranking, null),
+                new CoalescedEvent(notif4.key, 3, notif4.sbn, notif4.ranking, null),
+                new CoalescedEvent(notif5.key, 4, notif5.sbn, notif5.ranking, null)
+        ));
+    }
+
+    private static final long MIN_LINGER_DURATION = 5;
+    private static final long MAX_LINGER_DURATION = 18;
 
     private static final String TEST_PACKAGE_A = "com.test.package_a";
     private static final String TEST_PACKAGE_B = "com.test.package_b";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 0bf458c..61f0b26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -62,6 +62,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.Button;
+import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -684,6 +685,34 @@
     }
 
     @Test
+    public void testDemote() throws Exception {
+        mNotificationInfo.bindNotification(
+                mShortcutManager,
+                mLauncherApps,
+                mMockPackageManager,
+                mMockINotificationManager,
+                mVisualStabilityManager,
+                TEST_PACKAGE_NAME,
+                mNotificationChannel,
+                mEntry,
+                null,
+                null,
+                null,
+                true);
+
+
+        ImageButton demote = mNotificationInfo.findViewById(R.id.demote);
+        demote.performClick();
+        mTestableLooper.processAllMessages();
+
+        ArgumentCaptor<NotificationChannel> captor =
+                ArgumentCaptor.forClass(NotificationChannel.class);
+        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+                anyString(), anyInt(), captor.capture());
+        assertTrue(captor.getValue().isDemoted());
+    }
+
+    @Test
     public void testMute_mute() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
         mConversationChannel.setImportance(IMPORTANCE_DEFAULT);
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 068707f..21100458ad 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -237,6 +237,9 @@
     // Inform the user that the current network may not support using a randomized MAC address.
     NOTE_NETWORK_NO_MAC_RANDOMIZATION_SUPPORT = 56;
 
+    // Inform the user that EAP failure occurs
+    NOTE_WIFI_EAP_FAILURE = 57;
+
     // ADD_NEW_IDS_ABOVE_THIS_LINE
     // Legacy IDs with arbitrary values appear below
     // Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 36a9e0eb..f3a415e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -94,6 +94,7 @@
 import android.view.accessibility.IAccessibilityInteractionConnection;
 import android.view.accessibility.IAccessibilityManager;
 import android.view.accessibility.IAccessibilityManagerClient;
+import android.view.accessibility.IWindowMagnificationConnection;
 
 import com.android.internal.R;
 import com.android.internal.accessibility.AccessibilityShortcutController;
@@ -106,6 +107,7 @@
 import com.android.internal.util.IntPair;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.accessibility.magnification.WindowMagnificationManager;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
@@ -204,6 +206,8 @@
 
     private AccessibilityInputFilter mInputFilter;
 
+    private WindowMagnificationManager mWindowMagnificationMgr;
+
     private boolean mHasInputFilter;
 
     private KeyEventDispatcher mKeyEventDispatcher;
@@ -877,11 +881,8 @@
      */
     @Override
     public void notifyAccessibilityButtonVisibilityChanged(boolean shown) {
-        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);
-        }
+        mSecurityPolicy.enforceCallingOrSelfPermission(
+                android.Manifest.permission.STATUS_BAR_SERVICE);
         synchronized (mLock) {
             notifyAccessibilityButtonVisibilityChangedLocked(shown);
         }
@@ -1147,8 +1148,8 @@
             @ShortcutType int shortcutType) {
         Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        intent.putExtra(AccessibilityManager.EXTRA_SHORTCUT_TYPE, shortcutType);
         final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle();
-        bundle.putInt(AccessibilityManager.EXTRA_SHORTCUT_TYPE, shortcutType);
         mContext.startActivityAsUser(intent, bundle, UserHandle.of(mCurrentUserId));
     }
 
@@ -1883,11 +1884,12 @@
     }
 
     private boolean readAccessibilityShortcutKeySettingLocked(AccessibilityUserState userState) {
+        final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userState.mUserId);
         final Set<String> targetsFromSetting = new ArraySet<>();
-        readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
-                userState.mUserId, targetsFromSetting, str -> str);
-        if (targetsFromSetting.isEmpty()) {
-            // Fall back to device's default a11y service.
+        readColonDelimitedStringToSet(settingValue, targetsFromSetting, false, str -> str);
+        // Fall back to device's default a11y service, only when setting is never updated.
+        if (settingValue == null) {
             final String defaultService = mContext.getString(
                     R.string.config_defaultAccessibilityService);
             if (!TextUtils.isEmpty(defaultService)) {
@@ -2588,6 +2590,24 @@
     }
 
     @Override
+    public void setWindowMagnificationConnection(
+            IWindowMagnificationConnection connection) throws RemoteException {
+        mSecurityPolicy.enforceCallingOrSelfPermission(
+                android.Manifest.permission.STATUS_BAR_SERVICE);
+
+        getWindowMagnificationMgr().setConnection(connection);
+    }
+
+    WindowMagnificationManager getWindowMagnificationMgr() {
+        synchronized (mLock) {
+            if (mWindowMagnificationMgr == null) {
+                mWindowMagnificationMgr = new WindowMagnificationManager();
+            }
+            return mWindowMagnificationMgr;
+        }
+    }
+
+    @Override
     public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
         synchronized (mLock) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index 7dbec7c..7a42cd1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -550,4 +550,17 @@
             Binder.restoreCallingIdentity(identityToken);
         }
     }
+
+    /**
+     * Enforcing permission check to IPC caller or grant it if it's not through IPC.
+     *
+     * @param permission The permission to check
+     */
+    public void enforceCallingOrSelfPermission(@NonNull String permission) {
+        if (mContext.checkCallingOrSelfPermission(permission)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Caller does not hold permission "
+                    + permission);
+        }
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
new file mode 100644
index 0000000..351c9e0
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
@@ -0,0 +1,110 @@
+/*
+ * 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.accessibility.magnification;
+
+import static android.os.IBinder.DeathRecipient;
+
+import android.annotation.NonNull;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.accessibility.IWindowMagnificationConnection;
+import android.view.accessibility.IWindowMagnificationConnectionCallback;
+
+/**
+ * A wrapper of {@link IWindowMagnificationConnection}.
+ */
+class WindowMagnificationConnectionWrapper {
+
+    private static final boolean DBG = false;
+    private static final String TAG = "WindowMagnificationConnectionWrapper";
+
+    private final @NonNull IWindowMagnificationConnection mConnection;
+
+    WindowMagnificationConnectionWrapper(@NonNull IWindowMagnificationConnection connection) {
+        mConnection = connection;
+    }
+
+    //Should not use this instance anymore after calling it.
+    void unlinkToDeath(@NonNull DeathRecipient deathRecipient) {
+        mConnection.asBinder().unlinkToDeath(deathRecipient, 0);
+    }
+
+    void linkToDeath(@NonNull DeathRecipient deathRecipient) throws RemoteException {
+        mConnection.asBinder().linkToDeath(deathRecipient, 0);
+    }
+
+    boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY) {
+        try {
+            mConnection.enableWindowMagnification(displayId, scale, centerX, centerY);
+        } catch (RemoteException e) {
+            if (DBG) {
+                Slog.e(TAG, "Error calling enableWindowMagnification()");
+            }
+            return false;
+        }
+        return true;
+    }
+
+    boolean setScale(int displayId, float scale) {
+        try {
+            mConnection.setScale(displayId, scale);
+        } catch (RemoteException e) {
+            if (DBG) {
+                Slog.e(TAG, "Error calling setScale()");
+            }
+            return false;
+        }
+        return true;
+    }
+
+    boolean disableWindowMagnification(int displayId) {
+        try {
+            mConnection.disableWindowMagnification(displayId);
+        } catch (RemoteException e) {
+            if (DBG) {
+                Slog.e(TAG, "Error calling disableWindowMagnification()");
+            }
+            return false;
+        }
+        return true;
+    }
+
+    boolean moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
+        try {
+            mConnection.moveWindowMagnifier(displayId, offsetX, offsetY);
+        } catch (RemoteException e) {
+            if (DBG) {
+                Slog.e(TAG, "Error calling moveWindowMagnifier()");
+            }
+            return false;
+        }
+        return true;
+    }
+
+    boolean setConnectionCallback(IWindowMagnificationConnectionCallback connectionCallback) {
+        try {
+            mConnection.setConnectionCallback(connectionCallback);
+        } catch (RemoteException e) {
+            if (DBG) {
+                Slog.e(TAG, "Error calling setConnectionCallback()");
+            }
+            return false;
+        }
+        return true;
+    }
+
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
new file mode 100644
index 0000000..00db329
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -0,0 +1,98 @@
+/*
+ * 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.accessibility.magnification;
+
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.accessibility.IWindowMagnificationConnection;
+import android.view.accessibility.IWindowMagnificationConnectionCallback;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * A class to manipulate  window magnification through {@link WindowMagnificationConnectionWrapper}.
+ */
+public final class WindowMagnificationManager {
+
+    private static final String TAG = "WindowMagnificationMgr";
+    private final Object mLock = new Object();
+    @VisibleForTesting
+    @Nullable WindowMagnificationConnectionWrapper mConnectionWrapper;
+    private ConnectionCallback mConnectionCallback;
+
+    /**
+     * Sets {@link IWindowMagnificationConnection}.
+     * @param connection {@link IWindowMagnificationConnection}
+     */
+    public void setConnection(@Nullable IWindowMagnificationConnection connection) {
+        synchronized (mLock) {
+            //Reset connectionWrapper.
+            if (mConnectionWrapper != null) {
+                mConnectionWrapper.setConnectionCallback(null);
+                if (mConnectionCallback != null) {
+                    mConnectionCallback.mExpiredDeathRecipient = true;
+                }
+                mConnectionWrapper.unlinkToDeath(mConnectionCallback);
+                mConnectionWrapper = null;
+            }
+            if (connection != null) {
+                mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection);
+            }
+
+            if (mConnectionWrapper != null) {
+                try {
+                    mConnectionCallback = new ConnectionCallback();
+                    mConnectionWrapper.linkToDeath(mConnectionCallback);
+                    mConnectionWrapper.setConnectionCallback(mConnectionCallback);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "setConnection failed", e);
+                    mConnectionWrapper = null;
+                }
+            }
+        }
+    }
+
+    private class ConnectionCallback extends IWindowMagnificationConnectionCallback.Stub implements
+            IBinder.DeathRecipient {
+        private boolean mExpiredDeathRecipient = false;
+
+        @Override
+        public void onWindowMagnifierBoundsChanged(int display, Rect frame) throws RemoteException {
+        }
+
+        @Override
+        public void onChangeMagnificationMode(int display, int magnificationMode)
+                throws RemoteException {
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mLock) {
+                if (mExpiredDeathRecipient) {
+                    Slog.w(TAG, "binderDied DeathRecipient is expired");
+                    return;
+                }
+                mConnectionWrapper.unlinkToDeath(this);
+                mConnectionWrapper = null;
+                mConnectionCallback = null;
+            }
+        }
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 7d354d2..cdd7510 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -19,6 +19,7 @@
 import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
 import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
+import static android.view.autofill.AutofillManager.ACTION_RESPONSE_EXPIRED;
 import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
 import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
 import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
@@ -269,6 +270,9 @@
     @GuardedBy("mLock")
     private final LocalLog mWtfHistory;
 
+    @GuardedBy("mLock")
+    private boolean mExpiredResponse;
+
     /**
      * Map of {@link MetricsEvent#AUTOFILL_REQUEST} metrics, keyed by fill request id.
      */
@@ -690,6 +694,7 @@
     @GuardedBy("mLock")
     private void requestNewFillResponseLocked(@NonNull ViewState viewState, int newState,
             int flags) {
+        mExpiredResponse = false;
         if (mForAugmentedAutofillOnly || mRemoteFillService == null) {
             if (sVerbose) {
                 Slog.v(TAG, "requestNewFillResponse(): triggering augmented autofill instead "
@@ -1307,6 +1312,9 @@
             }
         }
 
+        // The client becomes invisible for the authentication, the response is effective.
+        mExpiredResponse = false;
+
         final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT);
         final Bundle newClientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE);
         if (sDebug) {
@@ -2322,16 +2330,18 @@
      * @param id The id of the view that is entered.
      * @param viewState The view that is entered.
      * @param flags The flag that was passed by the AutofillManager.
+     *
+     * @return {@code true} if a new fill response is requested.
      */
     @GuardedBy("mLock")
-    private void requestNewFillResponseOnViewEnteredIfNecessaryLocked(@NonNull AutofillId id,
+    private boolean requestNewFillResponseOnViewEnteredIfNecessaryLocked(@NonNull AutofillId id,
             @NonNull ViewState viewState, int flags) {
         if ((flags & FLAG_MANUAL_REQUEST) != 0) {
             mForAugmentedAutofillOnly = false;
             if (sDebug) Slog.d(TAG, "Re-starting session on view " + id + " and flags " + flags);
             maybeRequestInlineSuggestionsRequestThenFillLocked(viewState,
                     ViewState.STATE_RESTARTED_SESSION, flags);
-            return;
+            return true;
         }
 
         // If it's not, then check if it it should start a partition.
@@ -2342,12 +2352,14 @@
             }
             maybeRequestInlineSuggestionsRequestThenFillLocked(viewState,
                     ViewState.STATE_STARTED_PARTITION, flags);
+            return true;
         } else {
             if (sVerbose) {
                 Slog.v(TAG, "Not starting new partition for view " + id + ": "
                         + viewState.getStateAsString());
             }
         }
+        return false;
     }
 
     /**
@@ -2355,7 +2367,7 @@
      *
      * @param id The id of the view that is entered
      *
-     * @return {@code true} iff a new partition should be started
+     * @return {@code true} if a new partition should be started
      */
     @GuardedBy("mLock")
     private boolean shouldStartNewPartitionLocked(@NonNull AutofillId id) {
@@ -2363,6 +2375,13 @@
             return true;
         }
 
+        if (mExpiredResponse) {
+            if (sDebug) {
+                Slog.d(TAG, "Starting a new partition because the response has expired.");
+            }
+            return true;
+        }
+
         final int numResponses = mResponses.size();
         if (numResponses >= AutofillManagerService.getPartitionMaxCount()) {
             Slog.e(TAG, "Not starting a new partition on " + id + " because session " + this.id
@@ -2414,6 +2433,14 @@
                     + id + " destroyed");
             return;
         }
+        if (action == ACTION_RESPONSE_EXPIRED) {
+            mExpiredResponse = true;
+            if (sDebug) {
+                Slog.d(TAG, "Set the response has expired.");
+            }
+            return;
+        }
+
         id.setSessionId(this.id);
         if (sVerbose) {
             Slog.v(TAG, "updateLocked(" + this.id + "): id=" + id + ", action="
@@ -2577,7 +2604,9 @@
                     return;
                 }
 
-                requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags);
+                if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags)) {
+                    return;
+                }
 
                 if (isSameViewEntered) {
                     return;
@@ -3678,6 +3707,8 @@
                 return "VIEW_EXITED";
             case ACTION_VALUE_CHANGED:
                 return "VALUE_CHANGED";
+            case ACTION_RESPONSE_EXPIRED:
+                return "RESPONSE_EXPIRED";
             default:
                 return "UNKNOWN_" + action;
         }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c1e23e4..5e495b9 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -39,6 +39,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkPolicyManager.RULE_NONE;
 import static android.net.NetworkPolicyManager.uidRulesToString;
@@ -5840,11 +5841,6 @@
         } else {
             newNc.addCapability(NET_CAPABILITY_FOREGROUND);
         }
-        if (nai.isSuspended()) {
-            newNc.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
-        } else {
-            newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
-        }
         if (nai.partialConnectivity) {
             newNc.addCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY);
         } else {
@@ -5852,6 +5848,11 @@
         }
         newNc.setPrivateDnsBroken(nai.networkCapabilities.isPrivateDnsBroken());
 
+        // TODO : remove this once all factories are updated to send NOT_SUSPENDED
+        if (!newNc.hasTransport(TRANSPORT_CELLULAR)) {
+            newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+        }
+
         return newNc;
     }
 
@@ -5896,6 +5897,17 @@
             // on this network. We might have been called by rematchNetworkAndRequests when a
             // network changed foreground state.
             processListenRequests(nai);
+            final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+            final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+            if (prevSuspended != suspended) {
+                // TODO (b/73132094) : remove this call once the few users of onSuspended and
+                // onResumed have been removed.
+                notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED
+                        : ConnectivityManager.CALLBACK_RESUMED);
+                // updateNetworkInfo will mix in the suspended info from the capabilities and
+                // take appropriate action for the network having possibly changed state.
+                updateNetworkInfo(nai, nai.networkInfo);
+            }
         } else {
             // If the requestable capabilities have changed or the score changed, we can't have been
             // called by rematchNetworkAndRequests, so it's safe to start a rematch.
@@ -5903,6 +5915,9 @@
             notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
         }
 
+        // TODO : static analysis indicates that prevNc can't be null here (getAndSetNetworkCaps
+        // never returns null), so mark the relevant members and functions in nai as @NonNull and
+        // remove this test
         if (prevNc != null) {
             final boolean oldMetered = prevNc.isMetered();
             final boolean newMetered = newNc.isMetered();
@@ -6597,10 +6612,30 @@
         }
     }
 
-    private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) {
+    @NonNull
+    private NetworkInfo mixInInfo(@NonNull final NetworkAgentInfo nai, @NonNull NetworkInfo info) {
+        final NetworkInfo newInfo = new NetworkInfo(info);
+        // The suspended bit is managed in NetworkCapabilities.
+        final boolean suspended =
+                !nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+        if (suspended && info.getDetailedState() == NetworkInfo.DetailedState.CONNECTED) {
+            // Only override the state with SUSPENDED if the network is currently in CONNECTED
+            // state. This is because the network could have been suspended before connecting,
+            // or it could be disconnecting while being suspended, and in both these cases
+            // the state should not be overridden. Note that the only detailed state that
+            // maps to State.CONNECTED is DetailedState.CONNECTED, so there is also no need to
+            // worry about multiple different substates of CONNECTED.
+            newInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, info.getReason(),
+                    info.getExtraInfo());
+        }
+        return newInfo;
+    }
+
+    private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo info) {
+        final NetworkInfo newInfo = mixInInfo(networkAgent, info);
+
         final NetworkInfo.State state = newInfo.getState();
         NetworkInfo oldInfo = null;
-        final int oldScore = networkAgent.getCurrentScore();
         synchronized (networkAgent) {
             oldInfo = networkAgent.networkInfo;
             networkAgent.networkInfo = newInfo;
@@ -6682,17 +6717,6 @@
             }
         } else if (networkAgent.created && (oldInfo.getState() == NetworkInfo.State.SUSPENDED ||
                 state == NetworkInfo.State.SUSPENDED)) {
-            // going into or coming out of SUSPEND: re-score and notify
-            if (networkAgent.getCurrentScore() != oldScore) {
-                rematchAllNetworksAndRequests();
-            }
-            updateCapabilities(networkAgent.getCurrentScore(), networkAgent,
-                    networkAgent.networkCapabilities);
-            // TODO (b/73132094) : remove this call once the few users of onSuspended and
-            // onResumed have been removed.
-            notifyNetworkCallbacks(networkAgent, (state == NetworkInfo.State.SUSPENDED ?
-                    ConnectivityManager.CALLBACK_SUSPENDED :
-                    ConnectivityManager.CALLBACK_RESUMED));
             mLegacyTypeTracker.update(networkAgent);
         }
     }
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index dc393d1..e9db9c8 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.location.LocationManager.FUSED_PROVIDER;
 import static android.location.LocationManager.GPS_PROVIDER;
@@ -83,6 +85,7 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
@@ -459,8 +462,10 @@
             Log.d(TAG, "[u" + userId + "] location enabled = " + isLocationEnabledForUser(userId));
         }
 
-        Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION);
-        intent.putExtra(LocationManager.EXTRA_LOCATION_ENABLED, isLocationEnabledForUser(userId));
+        Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION)
+                .putExtra(LocationManager.EXTRA_LOCATION_ENABLED, isLocationEnabledForUser(userId))
+                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
 
         for (LocationProviderManager manager : mProviderManagers) {
@@ -929,9 +934,11 @@
                 // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
                 mSettingsStore.setLocationProviderAllowed(mName, useable, userId);
 
-                Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION);
-                intent.putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName);
-                intent.putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, useable);
+                Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION)
+                        .putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName)
+                        .putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, useable)
+                        .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                 mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
             }
 
@@ -1396,20 +1403,19 @@
     private String getResolutionPermission(int resolutionLevel) {
         switch (resolutionLevel) {
             case RESOLUTION_LEVEL_FINE:
-                return android.Manifest.permission.ACCESS_FINE_LOCATION;
+                return ACCESS_FINE_LOCATION;
             case RESOLUTION_LEVEL_COARSE:
-                return android.Manifest.permission.ACCESS_COARSE_LOCATION;
+                return ACCESS_COARSE_LOCATION;
             default:
                 return null;
         }
     }
 
     private int getAllowedResolutionLevel(int pid, int uid) {
-        if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
-                pid, uid) == PERMISSION_GRANTED) {
+        if (mContext.checkPermission(ACCESS_FINE_LOCATION, pid, uid) == PERMISSION_GRANTED) {
             return RESOLUTION_LEVEL_FINE;
-        } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
-                pid, uid) == PERMISSION_GRANTED) {
+        } else if (mContext.checkPermission(ACCESS_COARSE_LOCATION, pid, uid)
+                == PERMISSION_GRANTED) {
             return RESOLUTION_LEVEL_COARSE;
         } else {
             return RESOLUTION_LEVEL_NONE;
@@ -1420,59 +1426,28 @@
         return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid());
     }
 
-    private void checkResolutionLevelIsSufficientForGeofenceUse(int allowedResolutionLevel) {
-        if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
-            throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission");
-        }
+    private boolean checkCallingOrSelfLocationPermission() {
+        return mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED
+                || mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
+                == PERMISSION_GRANTED;
     }
 
-    @GuardedBy("mLock")
-    private int getMinimumResolutionLevelForProviderUseLocked(String provider) {
-        if (GPS_PROVIDER.equals(provider) || PASSIVE_PROVIDER.equals(provider)) {
-            // gps and passive providers require FINE permission
-            return RESOLUTION_LEVEL_FINE;
-        } else if (NETWORK_PROVIDER.equals(provider) || FUSED_PROVIDER.equals(provider)) {
-            // network and fused providers are ok with COARSE or FINE
-            return RESOLUTION_LEVEL_COARSE;
-        } else {
-            for (LocationProviderManager lp : mProviderManagers) {
-                if (!lp.getName().equals(provider)) {
-                    continue;
-                }
-
-                ProviderProperties properties = lp.getProperties();
-                if (properties != null) {
-                    if (properties.mRequiresSatellite) {
-                        // provider requiring satellites require FINE permission
-                        return RESOLUTION_LEVEL_FINE;
-                    } else if (properties.mRequiresNetwork || properties.mRequiresCell) {
-                        // provider requiring network and or cell require COARSE or FINE
-                        return RESOLUTION_LEVEL_COARSE;
-                    }
-                }
-            }
+    private void enforceCallingOrSelfLocationPermission() {
+        if (checkCallingOrSelfLocationPermission()) {
+            return;
         }
 
-        return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE
+        throw new SecurityException("uid " + Binder.getCallingUid() + " does not have "
+            + ACCESS_COARSE_LOCATION + " or " + ACCESS_FINE_LOCATION + ".");
     }
 
-    @GuardedBy("mLock")
-    private void checkResolutionLevelIsSufficientForProviderUseLocked(int allowedResolutionLevel,
-            String providerName) {
-        int requiredResolutionLevel = getMinimumResolutionLevelForProviderUseLocked(providerName);
-        if (allowedResolutionLevel < requiredResolutionLevel) {
-            switch (requiredResolutionLevel) {
-                case RESOLUTION_LEVEL_FINE:
-                    throw new SecurityException("\"" + providerName + "\" location provider " +
-                            "requires ACCESS_FINE_LOCATION permission.");
-                case RESOLUTION_LEVEL_COARSE:
-                    throw new SecurityException("\"" + providerName + "\" location provider " +
-                            "requires ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission.");
-                default:
-                    throw new SecurityException("Insufficient permission for \"" + providerName +
-                            "\" location provider.");
-            }
+    private void enforceCallingOrSelfPackageName(String packageName) {
+        int uid = Binder.getCallingUid();
+        if (ArrayUtils.contains(mPackageManager.getPackagesForUid(uid), packageName)) {
+            return;
         }
+
+        throw new SecurityException("invalid package \"" + packageName + "\" for uid " + uid);
     }
 
     public static int resolutionLevelToOp(int allowedResolutionLevel) {
@@ -1548,7 +1523,10 @@
      */
     @Override
     public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
-        int allowedResolutionLevel = getCallerAllowedResolutionLevel();
+        if (!checkCallingOrSelfLocationPermission()) {
+            return Collections.emptyList();
+        }
+
         synchronized (mLock) {
             ArrayList<String> providers = new ArrayList<>(mProviderManagers.size());
             for (LocationProviderManager manager : mProviderManagers) {
@@ -1556,9 +1534,6 @@
                 if (FUSED_PROVIDER.equals(name)) {
                     continue;
                 }
-                if (allowedResolutionLevel < getMinimumResolutionLevelForProviderUseLocked(name)) {
-                    continue;
-                }
                 if (enabledOnly && !manager.isUseable()) {
                     continue;
                 }
@@ -2002,33 +1977,18 @@
         return sanitizedRequest;
     }
 
-    private void checkPackageName(String packageName) {
-        if (packageName == null) {
-            throw new SecurityException("invalid package name: " + null);
-        }
-        int uid = Binder.getCallingUid();
-        String[] packages = mPackageManager.getPackagesForUid(uid);
-        if (packages == null) {
-            throw new SecurityException("invalid UID " + uid);
-        }
-        for (String pkg : packages) {
-            if (packageName.equals(pkg)) return;
-        }
-        throw new SecurityException("invalid package name: " + packageName);
-    }
-
     @Override
     public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
             PendingIntent intent, String packageName, String featureId,
             String listenerIdentifier) {
         Objects.requireNonNull(listenerIdentifier);
 
+        enforceCallingOrSelfLocationPermission();
+        enforceCallingOrSelfPackageName(packageName);
+
         synchronized (mLock) {
             if (request == null) request = DEFAULT_LOCATION_REQUEST;
-            checkPackageName(packageName);
             int allowedResolutionLevel = getCallerAllowedResolutionLevel();
-            checkResolutionLevelIsSufficientForProviderUseLocked(allowedResolutionLevel,
-                    request.getProvider());
             WorkSource workSource = request.getWorkSource();
             if (workSource != null && !workSource.isEmpty()) {
                 mContext.enforceCallingOrSelfPermission(
@@ -2135,7 +2095,7 @@
     @Override
     public void removeUpdates(ILocationListener listener, PendingIntent intent,
             String packageName) {
-        checkPackageName(packageName);
+        enforceCallingOrSelfPackageName(packageName);
 
         int pid = Binder.getCallingPid();
         int uid = Binder.getCallingUid();
@@ -2197,12 +2157,12 @@
 
     @Override
     public Location getLastLocation(LocationRequest r, String packageName, String featureId) {
+        enforceCallingOrSelfLocationPermission();
+        enforceCallingOrSelfPackageName(packageName);
+
         synchronized (mLock) {
             LocationRequest request = r != null ? r : DEFAULT_LOCATION_REQUEST;
             int allowedResolutionLevel = getCallerAllowedResolutionLevel();
-            checkPackageName(packageName);
-            checkResolutionLevelIsSufficientForProviderUseLocked(allowedResolutionLevel,
-                    request.getProvider());
             // no need to sanitize this request, as only the provider name is used
 
             final int pid = Binder.getCallingPid();
@@ -2348,7 +2308,7 @@
     public boolean injectLocation(Location location) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
                 "Location Hardware permission not granted to inject location");
-        mContext.enforceCallingPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
+        mContext.enforceCallingPermission(ACCESS_FINE_LOCATION,
                 "Access Fine Location permission not granted to inject Location");
 
         synchronized (mLock) {
@@ -2374,17 +2334,14 @@
             String packageName, String featureId, String listenerIdentifier) {
         Objects.requireNonNull(listenerIdentifier);
 
+        mContext.enforceCallingOrSelfPermission(ACCESS_FINE_LOCATION, null);
+        enforceCallingOrSelfPackageName(packageName);
+
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
-        checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel);
         if (intent == null) {
             throw new IllegalArgumentException("invalid pending intent: " + null);
         }
-        checkPackageName(packageName);
-        synchronized (mLock) {
-            checkResolutionLevelIsSufficientForProviderUseLocked(allowedResolutionLevel,
-                    request.getProvider());
-        }
         // Require that caller can manage given document
         boolean callerHasLocationHardwarePermission =
                 mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
@@ -2430,7 +2387,7 @@
         if (intent == null) {
             throw new IllegalArgumentException("invalid pending intent: " + null);
         }
-        checkPackageName(packageName);
+        enforceCallingOrSelfPackageName(packageName);
 
         if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
 
@@ -2517,36 +2474,30 @@
 
     @Override
     public boolean sendExtraCommand(String providerName, String command, Bundle extras) {
-        if (providerName == null) {
-            // throw NullPointerException to remain compatible with previous implementation
-            throw new NullPointerException();
-        }
+        Objects.requireNonNull(providerName);
+        Objects.requireNonNull(command);
 
         mContext.enforceCallingOrSelfPermission(
                 Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, null);
+        enforceCallingOrSelfLocationPermission();
 
-        synchronized (mLock) {
-            checkResolutionLevelIsSufficientForProviderUseLocked(getCallerAllowedResolutionLevel(),
-                    providerName);
+        mLocationUsageLogger.logLocationApiUsage(
+                LocationStatsEnums.USAGE_STARTED,
+                LocationStatsEnums.API_SEND_EXTRA_COMMAND,
+                providerName);
 
-            mLocationUsageLogger.logLocationApiUsage(
-                    LocationStatsEnums.USAGE_STARTED,
-                    LocationStatsEnums.API_SEND_EXTRA_COMMAND,
-                    providerName);
-
-            LocationProviderManager manager = getLocationProviderManager(providerName);
-            if (manager != null) {
-                manager.sendExtraCommand(Binder.getCallingUid(), Binder.getCallingPid(), command,
-                        extras);
-            }
-
-            mLocationUsageLogger.logLocationApiUsage(
-                    LocationStatsEnums.USAGE_ENDED,
-                    LocationStatsEnums.API_SEND_EXTRA_COMMAND,
-                    providerName);
-
-            return true;
+        LocationProviderManager manager = getLocationProviderManager(providerName);
+        if (manager != null) {
+            manager.sendExtraCommand(Binder.getCallingUid(), Binder.getCallingPid(), command,
+                    extras);
         }
+
+        mLocationUsageLogger.logLocationApiUsage(
+                LocationStatsEnums.USAGE_ENDED,
+                LocationStatsEnums.API_SEND_EXTRA_COMMAND,
+                providerName);
+
+        return true;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 7b4fd37..b464422 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -29,6 +29,7 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.Process;
 import android.os.SystemProperties;
 import android.provider.DeviceConfig;
 import android.text.TextUtils;
@@ -36,6 +37,7 @@
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.LongArrayQueue;
+import android.util.MathUtils;
 import android.util.Slog;
 import android.util.Xml;
 
@@ -117,6 +119,12 @@
     // Whether explicit health checks are enabled or not
     private static final boolean DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED = true;
 
+    @VisibleForTesting
+    static final int DEFAULT_BOOT_LOOP_TRIGGER_COUNT = 5;
+    static final long DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS = TimeUnit.MINUTES.toMillis(10);
+    private static final String PROP_RESCUE_BOOT_COUNT = "sys.rescue_boot_count";
+    private static final String PROP_RESCUE_BOOT_START = "sys.rescue_boot_start";
+
     private long mNumberOfNativeCrashPollsRemaining;
 
     private static final int DB_VERSION = 1;
@@ -152,6 +160,7 @@
     private final Runnable mSyncStateWithScheduledReason = this::syncStateWithScheduledReason;
     private final Runnable mSaveToFile = this::saveToFile;
     private final SystemClock mSystemClock;
+    private final BootThreshold mBootThreshold;
     @GuardedBy("mLock")
     private boolean mIsPackagesReady;
     // Flag to control whether explicit health checks are supported or not
@@ -169,6 +178,7 @@
     @FunctionalInterface
     @VisibleForTesting
     interface SystemClock {
+        // TODO: Add elapsedRealtime to this interface
         long uptimeMillis();
     }
 
@@ -198,6 +208,8 @@
         mConnectivityModuleConnector = connectivityModuleConnector;
         mSystemClock = clock;
         mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS;
+        mBootThreshold = new BootThreshold(DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
+                DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS);
         loadFromFile();
         sPackageWatchdog = this;
     }
@@ -411,6 +423,35 @@
         }
     }
 
+    /**
+     * Called when the system server boots. If the system server is detected to be in a boot loop,
+     * query each observer and perform the mitigation action with the lowest user impact.
+     */
+    public void noteBoot() {
+        synchronized (mLock) {
+            if (mBootThreshold.incrementAndTest()) {
+                mBootThreshold.reset();
+                PackageHealthObserver currentObserverToNotify = null;
+                int currentObserverImpact = Integer.MAX_VALUE;
+                for (int i = 0; i < mAllObservers.size(); i++) {
+                    final ObserverInternal observer = mAllObservers.valueAt(i);
+                    PackageHealthObserver registeredObserver = observer.registeredObserver;
+                    if (registeredObserver != null) {
+                        int impact = registeredObserver.onBootLoop();
+                        if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE
+                                && impact < currentObserverImpact) {
+                            currentObserverToNotify = registeredObserver;
+                            currentObserverImpact = impact;
+                        }
+                    }
+                }
+                if (currentObserverToNotify != null) {
+                    currentObserverToNotify.executeBootLoopMitigation();
+                }
+            }
+        }
+    }
+
     // TODO(b/120598832): Optimize write? Maybe only write a separate smaller file? Also
     // avoid holding lock?
     // This currently adds about 7ms extra to shutdown thread
@@ -519,6 +560,22 @@
         boolean execute(@Nullable VersionedPackage versionedPackage,
                 @FailureReasons int failureReason);
 
+
+        /**
+         * Called when the system server has booted several times within a window of time, defined
+         * by {@link #mBootThreshold}
+         */
+        default @PackageHealthObserverImpact int onBootLoop() {
+            return PackageHealthObserverImpact.USER_IMPACT_NONE;
+        }
+
+        /**
+         * Executes mitigation for {@link #onBootLoop}
+         */
+        default boolean executeBootLoopMitigation() {
+            return false;
+        }
+
         // TODO(b/120598832): Ensure uniqueness?
         /**
          * Identifier for the observer, should not change across device updates otherwise the
@@ -1367,4 +1424,62 @@
             return value > 0 ? value : Long.MAX_VALUE;
         }
     }
+
+    /**
+     * Handles the thresholding logic for system server boots.
+     */
+    static class BootThreshold {
+
+        private final int mBootTriggerCount;
+        private final long mTriggerWindow;
+
+        BootThreshold(int bootTriggerCount, long triggerWindow) {
+            this.mBootTriggerCount = bootTriggerCount;
+            this.mTriggerWindow = triggerWindow;
+        }
+
+        public void reset() {
+            setStart(0);
+            setCount(0);
+        }
+
+        private int getCount() {
+            return SystemProperties.getInt(PROP_RESCUE_BOOT_COUNT, 0);
+        }
+
+        private void setCount(int count) {
+            SystemProperties.set(PROP_RESCUE_BOOT_COUNT, Integer.toString(count));
+        }
+
+        public long getStart() {
+            return SystemProperties.getLong(PROP_RESCUE_BOOT_START, 0);
+        }
+
+        public void setStart(long start) {
+            final long now = android.os.SystemClock.elapsedRealtime();
+            final long newStart = MathUtils.constrain(start, 0, now);
+            SystemProperties.set(PROP_RESCUE_BOOT_START, Long.toString(newStart));
+        }
+
+        /** Increments the boot counter, and returns whether the device is bootlooping. */
+        public boolean incrementAndTest() {
+            final long now = android.os.SystemClock.elapsedRealtime();
+            if (now - getStart() < 0) {
+                Slog.e(TAG, "Window was less than zero. Resetting start to current time.");
+                setStart(now);
+            }
+            final long window = now - getStart();
+            if (window >= mTriggerWindow) {
+                setCount(1);
+                setStart(now);
+                return false;
+            } else {
+                int count = getCount() + 1;
+                setCount(count);
+                EventLogTags.writeRescueNote(Process.ROOT_UID, count, window);
+                return count >= mBootTriggerCount;
+            }
+        }
+
+    }
 }
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index 3dafc64..e8e3b39d 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -27,17 +27,16 @@
 import android.os.Build;
 import android.os.Environment;
 import android.os.FileUtils;
+import android.os.Process;
 import android.os.RecoverySystem;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.text.format.DateUtils;
 import android.util.ExceptionUtils;
 import android.util.Log;
 import android.util.MathUtils;
 import android.util.Slog;
-import android.util.SparseArray;
 import android.util.StatsLog;
 
 import com.android.internal.annotations.GuardedBy;
@@ -80,12 +79,6 @@
     static final int LEVEL_FACTORY_RESET = 4;
     @VisibleForTesting
     static final String PROP_RESCUE_BOOT_COUNT = "sys.rescue_boot_count";
-    /**
-     * The boot trigger window size must always be greater than Watchdog's deadlock timeout
-     * {@link Watchdog#DEFAULT_TIMEOUT}.
-     */
-    @VisibleForTesting
-    static final long BOOT_TRIGGER_WINDOW_MILLIS = 600 * DateUtils.SECOND_IN_MILLIS;
     @VisibleForTesting
     static final String TAG = "RescueParty";
 
@@ -93,18 +86,11 @@
 
 
     private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
-    private static final String PROP_RESCUE_BOOT_START = "sys.rescue_boot_start";
     private static final String PROP_VIRTUAL_DEVICE = "ro.hardware.virtual_device";
 
     private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT
             | ApplicationInfo.FLAG_SYSTEM;
 
-
-    /** Threshold for boot loops */
-    private static final Threshold sBoot = new BootThreshold();
-    /** Threshold for app crash loops */
-    private static SparseArray<Threshold> sApps = new SparseArray<>();
-
     /** Register the Rescue Party observer as a Package Watchdog health observer */
     public static void registerHealthObserver(Context context) {
         PackageWatchdog.getInstance(context).registerHealthObserver(
@@ -141,19 +127,6 @@
     }
 
     /**
-     * Take note of a boot event. If we notice too many of these events
-     * happening in rapid succession, we'll send out a rescue party.
-     */
-    public static void noteBoot(Context context) {
-        if (isDisabled()) return;
-        if (sBoot.incrementAndTest()) {
-            sBoot.reset();
-            incrementRescueLevel(sBoot.uid);
-            executeRescueLevel(context);
-        }
-    }
-
-    /**
      * Check if we're currently attempting to reboot for a factory reset.
      */
     public static boolean isAttemptingFactoryReset() {
@@ -170,11 +143,6 @@
     }
 
     @VisibleForTesting
-    static void resetAllThresholds() {
-        sBoot.reset();
-    }
-
-    @VisibleForTesting
     static long getElapsedRealtime() {
         return SystemClock.elapsedRealtime();
     }
@@ -187,6 +155,14 @@
     }
 
     /**
+     * Get the current rescue level.
+     */
+    private static int getRescueLevel() {
+        return MathUtils.constrain(SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE),
+                LEVEL_NONE, LEVEL_FACTORY_RESET);
+    }
+
+    /**
      * Escalate to the next rescue level. After incrementing the level you'll
      * probably want to call {@link #executeRescueLevel(Context)}.
      */
@@ -366,90 +342,29 @@
         }
 
         @Override
+        public int onBootLoop() {
+            if (isDisabled()) {
+                return PackageHealthObserverImpact.USER_IMPACT_NONE;
+            }
+            return mapRescueLevelToUserImpact(getRescueLevel());
+        }
+
+        @Override
+        public boolean executeBootLoopMitigation() {
+            if (isDisabled()) {
+                return false;
+            }
+            incrementRescueLevel(Process.ROOT_UID);
+            executeRescueLevel(mContext);
+            return true;
+        }
+
+        @Override
         public String getName() {
             return NAME;
         }
     }
 
-    /**
-     * Threshold that can be triggered if a number of events occur within a
-     * window of time.
-     */
-    private abstract static class Threshold {
-        public abstract int getCount();
-        public abstract void setCount(int count);
-        public abstract long getStart();
-        public abstract void setStart(long start);
-
-        private final int uid;
-        private final int triggerCount;
-        private final long triggerWindow;
-
-        public Threshold(int uid, int triggerCount, long triggerWindow) {
-            this.uid = uid;
-            this.triggerCount = triggerCount;
-            this.triggerWindow = triggerWindow;
-        }
-
-        public void reset() {
-            setCount(0);
-            setStart(0);
-        }
-
-        /**
-         * @return if this threshold has been triggered
-         */
-        public boolean incrementAndTest() {
-            final long now = getElapsedRealtime();
-            final long window = now - getStart();
-            if (window > triggerWindow) {
-                setCount(1);
-                setStart(now);
-                return false;
-            } else {
-                int count = getCount() + 1;
-                setCount(count);
-                EventLogTags.writeRescueNote(uid, count, window);
-                Slog.w(TAG, "Noticed " + count + " events for UID " + uid + " in last "
-                        + (window / 1000) + " sec");
-                return (count >= triggerCount);
-            }
-        }
-    }
-
-    /**
-     * Specialization of {@link Threshold} for monitoring boot events. It stores
-     * counters in system properties for robustness.
-     */
-    private static class BootThreshold extends Threshold {
-        public BootThreshold() {
-            // We're interested in TRIGGER_COUNT events in any
-            // BOOT_TRIGGER_WINDOW_MILLIS second period; this window is super relaxed because
-            // booting can take a long time if forced to dexopt things.
-            super(android.os.Process.ROOT_UID, TRIGGER_COUNT, BOOT_TRIGGER_WINDOW_MILLIS);
-        }
-
-        @Override
-        public int getCount() {
-            return SystemProperties.getInt(PROP_RESCUE_BOOT_COUNT, 0);
-        }
-
-        @Override
-        public void setCount(int count) {
-            SystemProperties.set(PROP_RESCUE_BOOT_COUNT, Integer.toString(count));
-        }
-
-        @Override
-        public long getStart() {
-            return SystemProperties.getLong(PROP_RESCUE_BOOT_START, 0);
-        }
-
-        @Override
-        public void setStart(long start) {
-            SystemProperties.set(PROP_RESCUE_BOOT_START, Long.toString(start));
-        }
-    }
-
     private static int[] getAllUserIds() {
         int[] userIds = { UserHandle.USER_SYSTEM };
         try {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 396b977..bcc3bdb 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -44,6 +44,7 @@
 import static com.android.internal.util.XmlUtils.writeIntAttribute;
 import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
+import static com.android.server.storage.StorageUserConnection.REMOTE_TIMEOUT_SECONDS;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -1985,16 +1986,28 @@
             Slog.i(TAG, "Mounting volume " + vol);
             mVold.mount(vol.id, vol.mountFlags, vol.mountUserId, new IVoldMountCallback.Stub() {
                     @Override
-                    public boolean onVolumeChecking(FileDescriptor deviceFd, String path,
+                    public boolean onVolumeChecking(FileDescriptor fd, String path,
                             String internalPath) {
                         vol.path = path;
                         vol.internalPath = internalPath;
+                        ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd);
                         try {
-                            mStorageSessionController.onVolumeMount(deviceFd, vol);
+                            mStorageSessionController.onVolumeMount(pfd, vol);
                             return true;
                         } catch (ExternalStorageServiceException e) {
-                            Slog.i(TAG, "Failed to mount volume " + vol, e);
+                            Slog.e(TAG, "Failed to mount volume " + vol, e);
+
+                            Slog.i(TAG, "Scheduling reset in one minute");
+                            mHandler.removeMessages(H_RESET);
+                            mHandler.sendMessageDelayed(mHandler.obtainMessage(H_RESET),
+                                    TimeUnit.SECONDS.toMillis(REMOTE_TIMEOUT_SECONDS * 2));
                             return false;
+                        } finally {
+                            try {
+                                pfd.close();
+                            } catch (Exception e) {
+                                Slog.e(TAG, "Failed to close FUSE device fd", e);
+                            }
                         }
                     }
                 });
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 37026fd..a98b83b 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -402,7 +402,7 @@
     }
 
     public ParcelFileDescriptor getStatisticsStream() {
-        mContext.enforceCallingPermission(
+        mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BATTERY_STATS, null);
         //Slog.i("foo", "SENDING BATTERY INFO:");
         //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index c1ab551..d66aec5 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -451,15 +451,6 @@
                 && !isLingering();
     }
 
-    /**
-     * Returns whether this network is currently suspended. A network is suspended if it is still
-     * connected but data temporarily fails to transfer. See {@link NetworkInfo.State#SUSPENDED}
-     * and {@link NetworkCapabilities#NET_CAPABILITY_NOT_SUSPENDED}.
-     */
-    public boolean isSuspended() {
-        return networkInfo.getState() == NetworkInfo.State.SUSPENDED;
-    }
-
     // Does this network satisfy request?
     public boolean satisfies(NetworkRequest request) {
         return created &&
diff --git a/services/core/java/com/android/server/integrity/IntegrityFileManager.java b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
index d763bae..fffe7d9 100644
--- a/services/core/java/com/android/server/integrity/IntegrityFileManager.java
+++ b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
@@ -185,6 +185,10 @@
                     && tmpDir.renameTo(mStagingDir))) {
                 throw new IOException("Error switching staging/rules directory");
             }
+
+            for (File file : mStagingDir.listFiles()) {
+                file.delete();
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java b/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java
index d21febb..0c4052a 100644
--- a/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java
+++ b/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java
@@ -18,9 +18,9 @@
 
 /**  A helper class containing special indexing file constants. */
 public final class IndexingFileConstants {
-    // We empirically experimented with different block sizes and identified that 250 is in the
+    // We empirically experimented with different block sizes and identified that 50 is in the
     // optimal range of efficient computation.
-    public static final int INDEXING_BLOCK_SIZE = 250;
+    public static final int INDEXING_BLOCK_SIZE = 50;
 
     public static final String START_INDEXING_KEY = "START_KEY";
     public static final String END_INDEXING_KEY = "END_KEY";
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
index 03392ab..87eee4e 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
@@ -28,9 +28,9 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.TreeSet;
 import java.util.stream.Collectors;
 
 /** Helper class to identify the necessary indexes that needs to be read. */
@@ -93,19 +93,28 @@
         return keyToIndexMap;
     }
 
-    private RuleIndexRange searchIndexingKeysRangeContainingKey(
+    private static RuleIndexRange searchIndexingKeysRangeContainingKey(
             LinkedHashMap<String, Integer> indexMap, String searchedKey) {
-        TreeSet<String> keyTreeSet =
-                indexMap.keySet().stream()
-                        .filter(key -> !key.matches(START_INDEXING_KEY) && !key.matches(
-                                END_INDEXING_KEY))
-                        .collect(Collectors.toCollection(TreeSet::new));
-
-        String minIndex = keyTreeSet.floor(searchedKey);
-        String maxIndex = keyTreeSet.higher(searchedKey);
-
+        List<String> keys = indexMap.keySet().stream().collect(Collectors.toList());
+        List<String> identifiedKeyRange =
+                searchKeysRangeContainingKey(keys, searchedKey, 0, keys.size() - 1);
         return new RuleIndexRange(
-                indexMap.get(minIndex == null ? START_INDEXING_KEY : minIndex),
-                indexMap.get(maxIndex == null ? END_INDEXING_KEY : maxIndex));
+                indexMap.get(identifiedKeyRange.get(0)), indexMap.get(identifiedKeyRange.get(1)));
+    }
+
+    private static List<String> searchKeysRangeContainingKey(
+            List<String> sortedKeyList, String key, int startIndex, int endIndex) {
+        if (endIndex - startIndex == 1) {
+            return Arrays.asList(sortedKeyList.get(startIndex), sortedKeyList.get(endIndex));
+        }
+
+        int midKeyIndex = startIndex + ((endIndex - startIndex) / 2);
+        String midKey = sortedKeyList.get(midKeyIndex);
+
+        if (key.compareTo(midKey) >= 0) {
+            return searchKeysRangeContainingKey(sortedKeyList, key, midKeyIndex, endIndex);
+        } else {
+            return searchKeysRangeContainingKey(sortedKeyList, key, startIndex, midKeyIndex);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index 0f8561e..4943c25 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -24,6 +24,7 @@
 import android.app.ActivityManager;
 import android.app.admin.PasswordMetrics;
 import android.os.ShellCommand;
+import android.text.TextUtils;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
@@ -195,6 +196,9 @@
     }
 
     private LockscreenCredential getOldCredential() {
+        if (TextUtils.isEmpty(mOld)) {
+            return LockscreenCredential.createNone();
+        }
         if (mLockPatternUtils.isLockPasswordEnabled(mCurrentUserId)) {
             final int quality = mLockPatternUtils.getKeyguardStoredPasswordQuality(mCurrentUserId);
             if (LockPatternUtils.isQualityAlphabeticPassword(quality)) {
@@ -202,12 +206,15 @@
             } else {
                 return LockscreenCredential.createPin(mOld);
             }
-        } else if (mLockPatternUtils.isLockPatternEnabled(mCurrentUserId)) {
+        }
+        if (mLockPatternUtils.isLockPatternEnabled(mCurrentUserId)) {
             return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
                     mOld.getBytes()));
-        } else {
-            return LockscreenCredential.createNone();
         }
+        // User supplied some old credential but the device has neither password nor pattern,
+        // so just return a password credential (and let it be rejected during LSS verification)
+        return LockscreenCredential.createPassword(mOld);
+
     }
 
     private boolean runSetPattern() {
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 408c1c9..b186771 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -49,8 +49,7 @@
         mCallback = callback;
     }
 
-    public abstract void requestCreateSession(String packageName, String routeId,
-            String routeType, long requestId);
+    public abstract void requestCreateSession(String packageName, String routeId, long requestId);
     public abstract void releaseSession(String sessionId);
 
     public abstract void selectRoute(String sessionId, String routeId);
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index 3840d02..4b992be 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -73,11 +73,9 @@
     }
 
     @Override
-    public void requestCreateSession(String packageName, String routeId, String routeType,
-            long requestId) {
+    public void requestCreateSession(String packageName, String routeId, long requestId) {
         if (mConnectionReady) {
-            mActiveConnection.requestCreateSession(packageName, routeId, routeType,
-                    requestId);
+            mActiveConnection.requestCreateSession(packageName, routeId, requestId);
             updateBinding();
         }
     }
@@ -429,11 +427,9 @@
             mClient.dispose();
         }
 
-        public void requestCreateSession(String packageName, String routeId, String routeType,
-                long requestId) {
+        public void requestCreateSession(String packageName, String routeId, long requestId) {
             try {
-                mProvider.requestCreateSession(packageName, routeId,
-                        routeType, requestId);
+                mProvider.requestCreateSession(packageName, routeId, requestId);
             } catch (RemoteException ex) {
                 Slog.e(TAG, "Failed to deliver request to create a session.", ex);
             }
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 161afb5..45d50b3 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -178,18 +178,14 @@
     }
 
     public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route,
-            String routeFeature, int requestId) {
+            int requestId) {
         Objects.requireNonNull(client, "client must not be null");
         Objects.requireNonNull(route, "route must not be null");
-        if (TextUtils.isEmpty(routeFeature)) {
-            throw new IllegalArgumentException("routeFeature must not be empty");
-        }
 
         final long token = Binder.clearCallingIdentity();
-
         try {
             synchronized (mLock) {
-                requestCreateSessionLocked(client, route, routeFeature, requestId);
+                requestCreateSessionLocked(client, route, requestId);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -450,7 +446,7 @@
     }
 
     private void requestCreateSessionLocked(@NonNull IMediaRouter2Client client,
-            @NonNull MediaRoute2Info route, @NonNull String routeFeature, long requestId) {
+            @NonNull MediaRoute2Info route, long requestId) {
         final IBinder binder = client.asBinder();
         final Client2Record clientRecord = mAllClientRecords.get(binder);
 
@@ -462,8 +458,7 @@
         if (clientRecord != null) {
             clientRecord.mUserRecord.mHandler.sendMessage(
                     obtainMessage(UserHandler::requestCreateSessionOnHandler,
-                            clientRecord.mUserRecord.mHandler,
-                            clientRecord, route, routeFeature, requestId));
+                            clientRecord.mUserRecord.mHandler, clientRecord, route, requestId));
         }
     }
 
@@ -624,7 +619,7 @@
             if (clientRecord != null && managerRecord.mTrusted) {
                 //TODO: select route feature properly
                 requestCreateSessionLocked(clientRecord.mClient, route,
-                        route.getFeatures().get(0), uniqueRequestId);
+                        uniqueRequestId);
             }
         }
     }
@@ -985,7 +980,7 @@
         }
 
         private void requestCreateSessionOnHandler(Client2Record clientRecord,
-                MediaRoute2Info route, String routeFeature, long requestId) {
+                MediaRoute2Info route, long requestId) {
 
             final MediaRoute2Provider provider = findProvider(route.getProviderId());
             if (provider == null) {
@@ -995,20 +990,13 @@
                 return;
             }
 
-            if (!route.getFeatures().contains(routeFeature)) {
-                Slog.w(TAG, "Ignoring session creation request since the given route=" + route
-                        + " doesn't support the given feature=" + routeFeature);
-                notifySessionCreationFailed(clientRecord, toClientRequestId(requestId));
-                return;
-            }
-
             // TODO: Apply timeout for each request (How many seconds should we wait?)
-            SessionCreationRequest request = new SessionCreationRequest(
-                    clientRecord, route, routeFeature, requestId);
+            SessionCreationRequest request =
+                    new SessionCreationRequest(clientRecord, route, requestId);
             mSessionCreationRequests.add(request);
 
             provider.requestCreateSession(clientRecord.mPackageName, route.getOriginalId(),
-                    routeFeature, requestId);
+                    requestId);
         }
 
         private void selectRouteOnHandler(@NonNull Client2Record clientRecord,
@@ -1173,15 +1161,11 @@
             }
 
             String originalRouteId = matchingRequest.mRoute.getId();
-            String originalRouteFeature = matchingRequest.mRouteFeature;
             Client2Record client2Record = matchingRequest.mClientRecord;
 
-            if (!sessionInfo.getSelectedRoutes().contains(originalRouteId)
-                    || !TextUtils.equals(originalRouteFeature,
-                        sessionInfo.getRouteFeature())) {
+            if (!sessionInfo.getSelectedRoutes().contains(originalRouteId)) {
                 Slog.w(TAG, "Created session doesn't match the original request."
                         + " originalRouteId=" + originalRouteId
-                        + ", originalRouteFeature=" + originalRouteFeature
                         + ", requestId=" + requestId + ", sessionInfo=" + sessionInfo);
                 notifySessionCreationFailed(matchingRequest.mClientRecord,
                         toClientRequestId(requestId));
@@ -1470,15 +1454,12 @@
         final class SessionCreationRequest {
             public final Client2Record mClientRecord;
             public final MediaRoute2Info mRoute;
-            public final String mRouteFeature;
             public final long mRequestId;
 
             SessionCreationRequest(@NonNull Client2Record clientRecord,
-                    @NonNull MediaRoute2Info route,
-                    @NonNull String routeFeature, long requestId) {
+                    @NonNull MediaRoute2Info route, long requestId) {
                 mClientRecord = clientRecord;
                 mRoute = route;
-                mRouteFeature = routeFeature;
                 mRequestId = requestId;
             }
         }
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index c80a898..aad9636 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -460,8 +460,8 @@
     // Binder call
     @Override
     public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route,
-            String routeType, int requestId) {
-        mService2.requestCreateSession(client, route, routeType, requestId);
+            int requestId) {
+        mService2.requestCreateSession(client, route, requestId);
     }
 
     // Binder call
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 6695227..3759ba9 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -96,8 +96,7 @@
     }
 
     @Override
-    public void requestCreateSession(String packageName, String routeId, String routeType,
-            long requestId) {
+    public void requestCreateSession(String packageName, String routeId, long requestId) {
         // Do nothing
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 6331dd4..b1c38d1 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -400,10 +400,10 @@
         } finally {
             IoUtils.closeQuietly(fis);
         }
-        // After all of the sessions were loaded, they are ready to be sealed and validated
+        // Re-sealing the sealed sessions.
         for (int i = 0; i < mSessions.size(); ++i) {
             PackageInstallerSession session = mSessions.valueAt(i);
-            session.sealAndValidateIfNecessary();
+            session.sealIfNecessary();
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 165bdeb..71555c9 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1374,15 +1374,13 @@
     }
 
     /**
-     * If session should be sealed, then it's sealed to prevent further modification
-     * and then it's validated.
-     *
-     * If the session was sealed but something went wrong then it's destroyed.
+     * If session should be sealed, then it's sealed to prevent further modification.
+     * If the session can't be sealed then it's destroyed.
      *
      * <p> This is meant to be called after all of the sessions are loaded and added to
      * PackageInstallerService
      */
-    void sealAndValidateIfNecessary() {
+    void sealIfNecessary() {
         synchronized (mLock) {
             if (!mShouldBeSealed || isStagedAndInTerminalState()) {
                 return;
@@ -1391,9 +1389,7 @@
         List<PackageInstallerSession> childSessions = getChildSessions();
         synchronized (mLock) {
             try {
-                sealAndValidateLocked(childSessions);
-            } catch (StreamingException e) {
-                Slog.e(TAG, "Streaming failed", e);
+                sealLocked(childSessions);
             } catch (PackageManagerException e) {
                 Slog.e(TAG, "Package not valid", e);
             }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 410017a..d0f91c2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1534,7 +1534,7 @@
     final @Nullable String mAppPredictionServicePackage;
     final @Nullable String mIncidentReportApproverPackage;
     final @Nullable String[] mTelephonyPackages;
-    final @NonNull String mServicesSystemSharedLibraryPackageName;
+    final @NonNull String mServicesExtensionPackageName;
     final @NonNull String mSharedSystemSharedLibraryPackageName;
 
     private final PackageUsage mPackageUsage = new PackageUsage();
@@ -3303,9 +3303,7 @@
                 } else {
                     mIntentFilterVerifier = null;
                 }
-                mServicesSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
-                        PackageManager.SYSTEM_SHARED_LIBRARY_SERVICES,
-                        SharedLibraryInfo.VERSION_UNDEFINED);
+                mServicesExtensionPackageName = getRequiredServicesExtensionPackageLPr();
                 mSharedSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
                         PackageManager.SYSTEM_SHARED_LIBRARY_SHARED,
                         SharedLibraryInfo.VERSION_UNDEFINED);
@@ -3315,7 +3313,7 @@
                 mRequiredUninstallerPackage = null;
                 mIntentFilterVerifierComponent = null;
                 mIntentFilterVerifier = null;
-                mServicesSystemSharedLibraryPackageName = null;
+                mServicesExtensionPackageName = null;
                 mSharedSystemSharedLibraryPackageName = null;
             }
             // PermissionController hosts default permission granting and role management, so it's a
@@ -3745,6 +3743,19 @@
         }
     }
 
+    @NonNull
+    private String getRequiredServicesExtensionPackageLPr() {
+        String servicesExtensionPackage =
+                ensureSystemPackageName(
+                        mContext.getString(R.string.config_servicesExtensionPackage));
+        if (TextUtils.isEmpty(servicesExtensionPackage)) {
+            throw new RuntimeException(
+                    "Required services extension package is missing, check "
+                            + "config_servicesExtensionPackage.");
+        }
+        return servicesExtensionPackage;
+    }
+
     private @NonNull String getRequiredInstallerLPr() {
         final Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
         intent.addCategory(Intent.CATEGORY_DEFAULT);
@@ -5467,7 +5478,7 @@
     public @NonNull String getServicesSystemSharedLibraryPackageName() {
         // allow instant applications
         synchronized (mLock) {
-            return mServicesSystemSharedLibraryPackageName;
+            return mServicesExtensionPackageName;
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 6ee8648..66a2b01 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3395,6 +3395,9 @@
     private void dispatchUserAddedIntent(@NonNull UserInfo userInfo) {
         Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
         addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
+        // Also, add the UserHandle for mainline modules which can't use the @hide
+        // EXTRA_USER_HANDLE.
+        addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userInfo.id));
         mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
                 android.Manifest.permission.MANAGE_USERS);
         MetricsLogger.count(mContext, userInfo.isGuest() ? TRON_GUEST_CREATED
@@ -3678,9 +3681,12 @@
         // wiping the user's system directory and removing from the user list
         long ident = Binder.clearCallingIdentity();
         try {
-            Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
-            addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
-            mContext.sendOrderedBroadcastAsUser(addedIntent, UserHandle.ALL,
+            Intent removedIntent = new Intent(Intent.ACTION_USER_REMOVED);
+            removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+            // Also, add the UserHandle for mainline modules which can't use the @hide
+            // EXTRA_USER_HANDLE.
+            removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
+            mContext.sendOrderedBroadcastAsUser(removedIntent, UserHandle.ALL,
                     android.Manifest.permission.MANAGE_USERS,
 
                     new BroadcastReceiver() {
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 7d0072a..5c0dd9a 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -360,7 +360,7 @@
      */
     boolean enableForPackageInApex(String packageName, long installedVersion,
             int rollbackDataPolicy) {
-        // TODO(b/142712057): Extract the new version number of apk-in-apex
+        // TODO(b/147666157): Extract the new version number of apk-in-apex
         // The new version for the apk-in-apex is set to 0 for now. If the package is then further
         // updated via non-staged install flow, then RollbackManagerServiceImpl#onPackageReplaced()
         // will be called and this rollback will be deleted. Other ways of package update have not
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index eefcde6..de48939 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -43,6 +43,7 @@
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.Process;
 import android.os.SystemClock;
@@ -78,6 +79,7 @@
 import java.util.Random;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
@@ -134,6 +136,7 @@
 
     private final Context mContext;
     private final HandlerThread mHandlerThread;
+    private final Executor mExecutor;
     private final Installer mInstaller;
     private final RollbackPackageHealthObserver mPackageHealthObserver;
     private final AppDataRollbackHelper mAppDataRollbackHelper;
@@ -173,6 +176,7 @@
         mHandlerThread = new HandlerThread("RollbackManagerServiceHandler");
         mHandlerThread.start();
         Watchdog.getInstance().addThread(getHandler(), HANDLER_THREAD_TIMEOUT_DURATION_MILLIS);
+        mExecutor = new HandlerExecutor(getHandler());
 
         for (UserInfo userInfo : UserManager.get(mContext).getUsers(true)) {
             registerUserCallbacks(userInfo.getUserHandle());
@@ -409,7 +413,6 @@
 
         CountDownLatch latch = new CountDownLatch(1);
         getHandler().post(() -> {
-            updateRollbackLifetimeDurationInMillis();
             synchronized (mLock) {
                 mRollbacks.clear();
                 mRollbacks.addAll(mRollbackStore.loadRollbacks());
@@ -520,11 +523,13 @@
 
     @AnyThread
     void onBootCompleted() {
-        getHandler().post(() -> updateRollbackLifetimeDurationInMillis());
-        // Also posts to handler thread
-        scheduleExpiration(0);
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
+                mExecutor, properties -> updateRollbackLifetimeDurationInMillis());
 
         getHandler().post(() -> {
+            updateRollbackLifetimeDurationInMillis();
+            runExpiration();
+
             // Check to see if any rollback-enabled staged sessions or staged
             // rollback sessions been applied.
             List<Rollback> enabling = new ArrayList<>();
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index b9ef7b3..6686de9 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -179,10 +179,10 @@
 
         // Use the version of the metadata package that was installed before
         // we rolled back for logging purposes.
-        VersionedPackage oldModuleMetadataPackage = null;
+        VersionedPackage oldLogPackage = null;
         for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
             if (packageRollback.getPackageName().equals(moduleMetadataPackageName)) {
-                oldModuleMetadataPackage = packageRollback.getVersionRolledBackFrom();
+                oldLogPackage = packageRollback.getVersionRolledBackFrom();
                 break;
             }
         }
@@ -194,13 +194,13 @@
             return;
         }
         if (sessionInfo.isStagedSessionApplied()) {
-            logEvent(oldModuleMetadataPackage,
+            logEvent(oldLogPackage,
                     StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
                     WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, "");
         } else if (sessionInfo.isStagedSessionReady()) {
             // TODO: What do for staged session ready but not applied
         } else {
-            logEvent(oldModuleMetadataPackage,
+            logEvent(oldLogPackage,
                     StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
                     WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, "");
         }
@@ -213,6 +213,23 @@
                 if (packageRollback.getVersionRolledBackFrom().equals(failedPackage)) {
                     return rollback;
                 }
+                // TODO(b/147666157): Extract version number of apk-in-apex so that we don't have
+                //  to rely on complicated reasoning as below
+
+                // Due to b/147666157, for apk in apex, we do not know the version we are rolling
+                // back from. But if a package X is embedded in apex A exclusively (not embedded in
+                // any other apex), which is not guaranteed, then it is sufficient to check only
+                // package names here, as the version of failedPackage and the PackageRollbackInfo
+                // can't be different. If failedPackage has a higher version, then it must have
+                // been updated somehow. There are two ways: it was updated by an update of apex A
+                // or updated directly as apk. In both cases, this rollback would have gotten
+                // expired when onPackageReplaced() was called. Since the rollback exists, it has
+                // same version as failedPackage.
+                if (packageRollback.isApkInApex()
+                        && packageRollback.getVersionRolledBackFrom().getPackageName()
+                        .equals(failedPackage.getPackageName())) {
+                    return rollback;
+                }
             }
         }
         return null;
@@ -245,12 +262,12 @@
     }
 
     private BroadcastReceiver listenForStagedSessionReady(RollbackManager rollbackManager,
-            int rollbackId, @Nullable VersionedPackage moduleMetadataPackage) {
+            int rollbackId, @Nullable VersionedPackage logPackage) {
         BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
                 handleStagedSessionChange(rollbackManager,
-                        rollbackId, this /* BroadcastReceiver */, moduleMetadataPackage);
+                        rollbackId, this /* BroadcastReceiver */, logPackage);
             }
         };
         IntentFilter sessionUpdatedFilter =
@@ -260,7 +277,7 @@
     }
 
     private void handleStagedSessionChange(RollbackManager rollbackManager, int rollbackId,
-            BroadcastReceiver listener, @Nullable VersionedPackage moduleMetadataPackage) {
+            BroadcastReceiver listener, @Nullable VersionedPackage logPackage) {
         PackageInstaller packageInstaller =
                 mContext.getPackageManager().getPackageInstaller();
         List<RollbackInfo> recentRollbacks =
@@ -274,15 +291,19 @@
                         packageInstaller.getSessionInfo(sessionId);
                 if (sessionInfo.isStagedSessionReady() && markStagedSessionHandled(rollbackId)) {
                     mContext.unregisterReceiver(listener);
-                    saveLastStagedRollbackId(rollbackId);
-                    logEvent(moduleMetadataPackage,
+                    if (logPackage != null) {
+                        // We save the rollback id so that after reboot, we can log if rollback was
+                        // successful or not. If logPackage is null, then there is nothing to log.
+                        saveLastStagedRollbackId(rollbackId);
+                    }
+                    logEvent(logPackage,
                             StatsLog
                             .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED,
                             WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN,
                             "");
                 } else if (sessionInfo.isStagedSessionFailed()
                         && markStagedSessionHandled(rollbackId)) {
-                    logEvent(moduleMetadataPackage,
+                    logEvent(logPackage,
                             StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
                             WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN,
                             "");
@@ -362,12 +383,12 @@
         }
     }
 
-    private static void logEvent(@Nullable VersionedPackage moduleMetadataPackage, int type,
+    private static void logEvent(@Nullable VersionedPackage logPackage, int type,
             int rollbackReason, @NonNull String failingPackageName) {
         Slog.i(TAG, "Watchdog event occurred of type: " + rollbackTypeToString(type));
-        if (moduleMetadataPackage != null) {
-            StatsLog.logWatchdogRollbackOccurred(type, moduleMetadataPackage.getPackageName(),
-                    moduleMetadataPackage.getVersionCode(), rollbackReason, failingPackageName);
+        if (logPackage != null) {
+            StatsLog.logWatchdogRollbackOccurred(type, logPackage.getPackageName(),
+                    logPackage.getVersionCode(), rollbackReason, failingPackageName);
         }
     }
 
diff --git a/services/core/java/com/android/server/stats/IonMemoryUtil.java b/services/core/java/com/android/server/stats/pull/IonMemoryUtil.java
similarity index 93%
rename from services/core/java/com/android/server/stats/IonMemoryUtil.java
rename to services/core/java/com/android/server/stats/pull/IonMemoryUtil.java
index c9be96f..fde0a59 100644
--- a/services/core/java/com/android/server/stats/IonMemoryUtil.java
+++ b/services/core/java/com/android/server/stats/pull/IonMemoryUtil.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.server.stats;
+package com.android.server.stats.pull;
 
 import android.os.FileUtils;
 import android.util.Slog;
@@ -30,8 +30,11 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-/** Utility methods for reading ion memory stats. */
-final class IonMemoryUtil {
+/**
+ * Utility methods for reading ion memory stats.
+ * TODO: Consider making package private after puller migration
+ */
+public final class IonMemoryUtil {
     private static final String TAG = "IonMemoryUtil";
 
     /** Path to debugfs file for the system ion heap. */
@@ -50,7 +53,7 @@
      * Returns value of the total size in bytes of the system ion heap from
      * /sys/kernel/debug/ion/heaps/system.
      */
-    static long readSystemIonHeapSizeFromDebugfs() {
+    public static long readSystemIonHeapSizeFromDebugfs() {
         return parseIonHeapSizeFromDebugfs(readFile(DEBUG_SYSTEM_ION_HEAP_FILE));
     }
 
@@ -78,7 +81,7 @@
      * Returns values of allocation sizes in bytes on the system ion heap from
      * /sys/kernel/debug/ion/heaps/system.
      */
-    static List<IonAllocations> readProcessSystemIonHeapSizesFromDebugfs() {
+    public static List<IonAllocations> readProcessSystemIonHeapSizesFromDebugfs() {
         return parseProcessIonHeapSizesFromDebugfs(readFile(DEBUG_SYSTEM_ION_HEAP_FILE));
     }
 
@@ -130,7 +133,7 @@
     }
 
     /** Summary information about process ion allocations. */
-    static final class IonAllocations {
+    public static final class IonAllocations {
         /** PID these allocations belong to. */
         public int pid;
         /** Size of all individual allocations added together. */
diff --git a/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
similarity index 89%
rename from services/core/java/com/android/server/stats/ProcfsMemoryUtil.java
rename to services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
index c1eacce..638dfd2 100644
--- a/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java
+++ b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.server.stats;
+package com.android.server.stats.pull;
 
 import static android.os.Process.PROC_OUT_STRING;
 
@@ -22,7 +22,7 @@
 
 import java.util.function.BiConsumer;
 
-final class ProcfsMemoryUtil {
+public final class ProcfsMemoryUtil {
     private static final int[] CMDLINE_OUT = new int[] { PROC_OUT_STRING };
     private static final String[] STATUS_KEYS = new String[] {
             "Uid:",
@@ -39,7 +39,7 @@
      * VmSwap fields in /proc/pid/status in kilobytes or null if not available.
      */
     @Nullable
-    static MemorySnapshot readMemorySnapshotFromProcfs(int pid) {
+    public static MemorySnapshot readMemorySnapshotFromProcfs(int pid) {
         long[] output = new long[STATUS_KEYS.length];
         output[0] = -1;
         Process.readProcLines("/proc/" + pid + "/status", STATUS_KEYS, output);
@@ -63,7 +63,7 @@
      * Returns content of /proc/pid/cmdline (e.g. /system/bin/statsd) or an empty string
      * if the file is not available.
      */
-    static String readCmdlineFromProcfs(int pid) {
+    public static String readCmdlineFromProcfs(int pid) {
         String[] cmdline = new String[1];
         if (!Process.readProcFile("/proc/" + pid + "/cmdline", CMDLINE_OUT, cmdline, null, null)) {
             return "";
@@ -71,7 +71,7 @@
         return cmdline[0];
     }
 
-    static void forEachPid(BiConsumer<Integer, String> func) {
+    public static void forEachPid(BiConsumer<Integer, String> func) {
         int[] pids = new int[1024];
         pids = Process.getPids("/proc", pids);
         for (int pid : pids) {
@@ -86,7 +86,7 @@
         }
     }
 
-    static final class MemorySnapshot {
+    public static final class MemorySnapshot {
         public int uid;
         public int rssHighWaterMarkInKilobytes;
         public int rssInKilobytes;
diff --git a/services/core/java/com/android/server/stats/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
similarity index 60%
rename from services/core/java/com/android/server/stats/StatsPullAtomService.java
rename to services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 1f7ed8a..3bc860a 100644
--- a/services/core/java/com/android/server/stats/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -14,22 +14,23 @@
  * limitations under the License.
  */
 
-package com.android.server.stats;
+package com.android.server.stats.pull;
 
 import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
 import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
 import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
+import static android.os.Debug.getIonHeapsSizeKb;
 import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
 import static android.os.Process.getUidForPid;
 import static android.os.storage.VolumeInfo.TYPE_PRIVATE;
 import static android.os.storage.VolumeInfo.TYPE_PUBLIC;
 
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
-import static com.android.server.stats.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
-import static com.android.server.stats.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
-import static com.android.server.stats.ProcfsMemoryUtil.forEachPid;
-import static com.android.server.stats.ProcfsMemoryUtil.readCmdlineFromProcfs;
-import static com.android.server.stats.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
+import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
+import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
+import static com.android.server.stats.pull.ProcfsMemoryUtil.forEachPid;
+import static com.android.server.stats.pull.ProcfsMemoryUtil.readCmdlineFromProcfs;
+import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -141,8 +142,8 @@
 import com.android.server.am.MemoryStatUtil.MemoryStat;
 import com.android.server.notification.NotificationManagerService;
 import com.android.server.role.RoleManagerInternal;
-import com.android.server.stats.IonMemoryUtil.IonAllocations;
-import com.android.server.stats.ProcfsMemoryUtil.MemorySnapshot;
+import com.android.server.stats.pull.IonMemoryUtil.IonAllocations;
+import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot;
 import com.android.server.storage.DiskStatsFileLogger;
 import com.android.server.storage.DiskStatsLoggingService;
 
@@ -210,6 +211,8 @@
     @Override
     public void onStart() {
         mStatsManager = (StatsManager) mContext.getSystemService(Context.STATS_MANAGER);
+        mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+        mTelephony = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
 
         // Used to initialize the CPU Frequency atom.
         PowerProfile powerProfile = new PowerProfile(mContext);
@@ -263,11 +266,12 @@
         registerProcessMemoryHighWaterMark();
         registerProcessMemorySnapshot();
         registerSystemIonHeapSize();
+        registerIonHeapSize();
         registerProcessSystemIonHeapSize();
         registerTemperature();
         registerCoolingDevice();
-        registerBinderCalls();
-        registerBinderCallsExceptions();
+        registerBinderCallsStats();
+        registerBinderCallsStatsExceptions();
         registerLooperStats();
         registerDiskStats();
         registerDirectoryUsage();
@@ -812,19 +816,96 @@
     }
 
     private void registerWifiActivityInfo() {
-        // No op.
+        int tagId = StatsLog.WIFI_ACTIVITY_INFO;
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                (atomTag, data) -> pullWifiActivityInfo(atomTag, data),
+                BackgroundThread.getExecutor()
+        );
     }
 
-    private void pullWifiActivityInfo() {
-        // No op.
+    private WifiManager mWifiManager;
+    private TelephonyManager mTelephony;
+
+    private int pullWifiActivityInfo(int atomTag, List<StatsEvent> pulledData) {
+        long token = Binder.clearCallingIdentity();
+        try {
+            SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
+            mWifiManager.getWifiActivityEnergyInfoAsync(
+                    new Executor() {
+                        @Override
+                        public void execute(Runnable runnable) {
+                            // run the listener on the binder thread, if it was run on the main
+                            // thread it would deadlock since we would be waiting on ourselves
+                            runnable.run();
+                        }
+                    },
+                    info -> {
+                        Bundle bundle = new Bundle();
+                        bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info);
+                        wifiReceiver.send(0, bundle);
+                    }
+            );
+            final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
+            if (wifiInfo == null) {
+                return StatsManager.PULL_SKIP;
+            }
+            StatsEvent e = StatsEvent.newBuilder()
+                    .setAtomId(atomTag)
+                    .writeLong(wifiInfo.getTimeSinceBootMillis())
+                    .writeInt(wifiInfo.getStackState())
+                    .writeLong(wifiInfo.getControllerTxDurationMillis())
+                    .writeLong(wifiInfo.getControllerRxDurationMillis())
+                    .writeLong(wifiInfo.getControllerIdleDurationMillis())
+                    .writeLong(wifiInfo.getControllerEnergyUsedMicroJoules())
+                    .build();
+            pulledData.add(e);
+        } catch (RuntimeException e) {
+            Slog.e(TAG, "failed to getWifiActivityEnergyInfoAsync", e);
+            return StatsManager.PULL_SKIP;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        return StatsManager.PULL_SUCCESS;
     }
 
     private void registerModemActivityInfo() {
-        // No op.
+        int tagId = StatsLog.MODEM_ACTIVITY_INFO;
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                (atomTag, data) -> pullModemActivityInfo(atomTag, data),
+                BackgroundThread.getExecutor()
+        );
     }
 
-    private void pullModemActivityInfo() {
-        // No op.
+    private int pullModemActivityInfo(int atomTag, List<StatsEvent> pulledData) {
+        long token = Binder.clearCallingIdentity();
+        try {
+            SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
+            mTelephony.requestModemActivityInfo(modemReceiver);
+            final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
+            if (modemInfo == null) {
+                return StatsManager.PULL_SKIP;
+            }
+            StatsEvent e = StatsEvent.newBuilder()
+                    .setAtomId(atomTag)
+                    .writeLong(modemInfo.getTimestamp())
+                    .writeLong(modemInfo.getSleepTimeMillis())
+                    .writeLong(modemInfo.getIdleTimeMillis())
+                    .writeLong(modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis())
+                    .writeLong(modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis())
+                    .writeLong(modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis())
+                    .writeLong(modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis())
+                    .writeLong(modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis())
+                    .writeLong(modemInfo.getReceiveTimeMillis())
+                    .build();
+            pulledData.add(e);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        return StatsManager.PULL_SUCCESS;
     }
 
     private void registerBluetoothActivityInfo() {
@@ -923,75 +1004,426 @@
     }
 
     private void registerProcessMemoryState() {
-        // No op.
+        int tagId = StatsLog.PROCESS_MEMORY_STATE;
+        PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+                .setAdditiveFields(new int[] {4, 5, 6, 7, 8})
+                .build();
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                metadata,
+                (atomTag, data) -> pullProcessMemoryState(atomTag, data),
+                BackgroundThread.getExecutor()
+        );
     }
 
-    private void pullProcessMemoryState() {
-        // No op.
+    private int pullProcessMemoryState(int atomTag, List<StatsEvent> pulledData) {
+        List<ProcessMemoryState> processMemoryStates =
+                LocalServices.getService(ActivityManagerInternal.class)
+                        .getMemoryStateForProcesses();
+        for (ProcessMemoryState processMemoryState : processMemoryStates) {
+            final MemoryStat memoryStat = readMemoryStatFromFilesystem(processMemoryState.uid,
+                    processMemoryState.pid);
+            if (memoryStat == null) {
+                continue;
+            }
+            StatsEvent e = StatsEvent.newBuilder()
+                    .setAtomId(atomTag)
+                    .writeInt(processMemoryState.uid)
+                    .writeString(processMemoryState.processName)
+                    .writeInt(processMemoryState.oomScore)
+                    .writeLong(memoryStat.pgfault)
+                    .writeLong(memoryStat.pgmajfault)
+                    .writeLong(memoryStat.rssInBytes)
+                    .writeLong(memoryStat.cacheInBytes)
+                    .writeLong(memoryStat.swapInBytes)
+                    .writeLong(-1)  // unused
+                    .writeLong(-1)  // unused
+                    .writeInt(-1)  // unused
+                    .build();
+            pulledData.add(e);
+        }
+        return StatsManager.PULL_SUCCESS;
+    }
+
+    /**
+     * Which native processes to snapshot memory for.
+     *
+     * <p>Processes are matched by their cmdline in procfs. Example: cat /proc/pid/cmdline returns
+     * /system/bin/statsd for the stats daemon.
+     */
+    private static final Set<String> MEMORY_INTERESTING_NATIVE_PROCESSES = Sets.newHashSet(
+            "/system/bin/statsd",  // Stats daemon.
+            "/system/bin/surfaceflinger",
+            "/system/bin/apexd",  // APEX daemon.
+            "/system/bin/audioserver",
+            "/system/bin/cameraserver",
+            "/system/bin/drmserver",
+            "/system/bin/healthd",
+            "/system/bin/incidentd",
+            "/system/bin/installd",
+            "/system/bin/lmkd",  // Low memory killer daemon.
+            "/system/bin/logd",
+            "media.codec",
+            "media.extractor",
+            "media.metrics",
+            "/system/bin/mediadrmserver",
+            "/system/bin/mediaserver",
+            "/system/bin/performanced",
+            "/system/bin/tombstoned",
+            "/system/bin/traced",  // Perfetto.
+            "/system/bin/traced_probes",  // Perfetto.
+            "webview_zygote",
+            "zygote",
+            "zygote64");
+
+    /**
+     * Lowest available uid for apps.
+     *
+     * <p>Used to quickly discard memory snapshots of the zygote forks from native process
+     * measurements.
+     */
+    private static final int MIN_APP_UID = 10_000;
+
+    private static boolean isAppUid(int uid) {
+        return uid >= MIN_APP_UID;
     }
 
     private void registerProcessMemoryHighWaterMark() {
-        // No op.
+        int tagId = StatsLog.PROCESS_MEMORY_HIGH_WATER_MARK;
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                (atomTag, data) -> pullProcessMemoryHighWaterMark(atomTag, data),
+                BackgroundThread.getExecutor()
+        );
     }
 
-    private void pullProcessMemoryHighWaterMark() {
-        // No op.
+    private int pullProcessMemoryHighWaterMark(int atomTag, List<StatsEvent> pulledData) {
+        List<ProcessMemoryState> managedProcessList =
+                LocalServices.getService(ActivityManagerInternal.class)
+                        .getMemoryStateForProcesses();
+        for (ProcessMemoryState managedProcess : managedProcessList) {
+            final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid);
+            if (snapshot == null) {
+                continue;
+            }
+            StatsEvent e = StatsEvent.newBuilder()
+                    .setAtomId(atomTag)
+                    .writeInt(managedProcess.uid)
+                    .writeString(managedProcess.processName)
+                    // RSS high-water mark in bytes.
+                    .writeLong(snapshot.rssHighWaterMarkInKilobytes * 1024L)
+                    .writeInt(snapshot.rssHighWaterMarkInKilobytes)
+                    .build();
+            pulledData.add(e);
+        }
+        forEachPid((pid, cmdLine) -> {
+            if (!MEMORY_INTERESTING_NATIVE_PROCESSES.contains(cmdLine)) {
+                return;
+            }
+            final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid);
+            if (snapshot == null) {
+                return;
+            }
+            // Sometimes we get here a process that is not included in the whitelist. It comes
+            // from forking the zygote for an app. We can ignore that sample because this process
+            // is collected by ProcessMemoryState.
+            if (isAppUid(snapshot.uid)) {
+                return;
+            }
+            StatsEvent e = StatsEvent.newBuilder()
+                    .setAtomId(atomTag)
+                    .writeInt(snapshot.uid)
+                    .writeString(cmdLine)
+                    // RSS high-water mark in bytes.
+                    .writeLong(snapshot.rssHighWaterMarkInKilobytes * 1024L)
+                    .writeInt(snapshot.rssHighWaterMarkInKilobytes)
+                    .build();
+            pulledData.add(e);
+        });
+        // Invoke rss_hwm_reset binary to reset RSS HWM counters for all processes.
+        SystemProperties.set("sys.rss_hwm_reset.on", "1");
+        return StatsManager.PULL_SUCCESS;
     }
 
     private void registerProcessMemorySnapshot() {
-        // No op.
+        int tagId = StatsLog.PROCESS_MEMORY_SNAPSHOT;
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                (atomTag, data) -> pullProcessMemorySnapshot(atomTag, data),
+                BackgroundThread.getExecutor()
+        );
     }
 
-    private void pullProcessMemorySnapshot() {
-        // No op.
+    private int pullProcessMemorySnapshot(int atomTag, List<StatsEvent> pulledData) {
+        List<ProcessMemoryState> managedProcessList =
+                LocalServices.getService(ActivityManagerInternal.class)
+                        .getMemoryStateForProcesses();
+        for (ProcessMemoryState managedProcess : managedProcessList) {
+            final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid);
+            if (snapshot == null) {
+                continue;
+            }
+            StatsEvent e = StatsEvent.newBuilder()
+                    .writeInt(managedProcess.uid)
+                    .writeString(managedProcess.processName)
+                    .writeInt(managedProcess.pid)
+                    .writeInt(managedProcess.oomScore)
+                    .writeInt(snapshot.rssInKilobytes)
+                    .writeInt(snapshot.anonRssInKilobytes)
+                    .writeInt(snapshot.swapInKilobytes)
+                    .writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes)
+                    .build();
+            pulledData.add(e);
+        }
+        forEachPid((pid, cmdLine) -> {
+            if (!MEMORY_INTERESTING_NATIVE_PROCESSES.contains(cmdLine)) {
+                return;
+            }
+            final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid);
+            if (snapshot == null) {
+                return;
+            }
+            // Sometimes we get here a process that is not included in the whitelist. It comes
+            // from forking the zygote for an app. We can ignore that sample because this process
+            // is collected by ProcessMemoryState.
+            if (isAppUid(snapshot.uid)) {
+                return;
+            }
+            StatsEvent e = StatsEvent.newBuilder()
+                    .setAtomId(atomTag)
+                    .writeInt(snapshot.uid)
+                    .writeString(cmdLine)
+                    .writeInt(pid)
+                    .writeInt(-1001)  // Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.
+                    .writeInt(snapshot.rssInKilobytes)
+                    .writeInt(snapshot.anonRssInKilobytes)
+                    .writeInt(snapshot.swapInKilobytes)
+                    .writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes)
+                    .build();
+            pulledData.add(e);
+        });
+        return StatsManager.PULL_SUCCESS;
     }
 
     private void registerSystemIonHeapSize() {
-        // No op.
+        int tagId = StatsLog.SYSTEM_ION_HEAP_SIZE;
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                (atomTag, data) -> pullSystemIonHeapSize(atomTag, data),
+                BackgroundThread.getExecutor()
+        );
     }
 
-    private void pullSystemIonHeapSize() {
-        // No op.
+    private int pullSystemIonHeapSize(int atomTag, List<StatsEvent> pulledData) {
+        final long systemIonHeapSizeInBytes = readSystemIonHeapSizeFromDebugfs();
+        StatsEvent e = StatsEvent.newBuilder()
+                .setAtomId(atomTag)
+                .writeLong(systemIonHeapSizeInBytes)
+                .build();
+        pulledData.add(e);
+        return StatsManager.PULL_SUCCESS;
+    }
+
+    private void registerIonHeapSize() {
+        int tagId = StatsLog.ION_HEAP_SIZE;
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                /* PullAtomMetadata */ null,
+                (atomTag, data) -> pullIonHeapSize(atomTag, data),
+                Executors.newSingleThreadExecutor()
+        );
+    }
+
+    private int pullIonHeapSize(int atomTag, List<StatsEvent> pulledData) {
+        int ionHeapSizeInKilobytes = (int) getIonHeapsSizeKb();
+        StatsEvent e = StatsEvent.newBuilder()
+              .setAtomId(atomTag)
+              .writeInt(ionHeapSizeInKilobytes)
+              .build();
+        pulledData.add(e);
+        return StatsManager.PULL_SUCCESS;
     }
 
     private void registerProcessSystemIonHeapSize() {
-        // No op.
+        int tagId = StatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE;
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                (atomTag, data) -> pullProcessSystemIonHeapSize(atomTag, data),
+                BackgroundThread.getExecutor()
+        );
     }
 
-    private void pullProcessSystemIonHeapSize() {
-        // No op.
+    private int pullProcessSystemIonHeapSize(int atomTag, List<StatsEvent> pulledData) {
+        List<IonAllocations> result = readProcessSystemIonHeapSizesFromDebugfs();
+        for (IonAllocations allocations : result) {
+            StatsEvent e = StatsEvent.newBuilder()
+                    .setAtomId(atomTag)
+                    .writeInt(getUidForPid(allocations.pid))
+                    .writeString(readCmdlineFromProcfs(allocations.pid))
+                    .writeInt((int) (allocations.totalSizeInBytes / 1024))
+                    .writeInt(allocations.count)
+                    .writeInt((int) (allocations.maxSizeInBytes / 1024))
+                    .build();
+            pulledData.add(e);
+        }
+        return StatsManager.PULL_SUCCESS;
     }
 
     private void registerTemperature() {
-        // No op.
+        int tagId = StatsLog.TEMPERATURE;
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                (atomTag, data) -> pullTemperature(atomTag, data),
+                BackgroundThread.getExecutor()
+        );
     }
 
-    private void pullTemperature() {
-        // No op.
+    private int pullTemperature(int atomTag, List<StatsEvent> pulledData) {
+        IThermalService thermalService = getIThermalService();
+        if (thermalService == null) {
+            return StatsManager.PULL_SKIP;
+        }
+        final long callingToken = Binder.clearCallingIdentity();
+        try {
+            List<Temperature> temperatures = thermalService.getCurrentTemperatures();
+            for (Temperature temp : temperatures) {
+                StatsEvent e = StatsEvent.newBuilder()
+                        .setAtomId(atomTag)
+                        .writeInt(temp.getType())
+                        .writeString(temp.getName())
+                        .writeInt((int) (temp.getValue() * 10))
+                        .writeInt(temp.getStatus())
+                        .build();
+                pulledData.add(e);
+            }
+        } catch (RemoteException e) {
+            // Should not happen.
+            Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures.");
+            return StatsManager.PULL_SKIP;
+        } finally {
+            Binder.restoreCallingIdentity(callingToken);
+        }
+        return StatsManager.PULL_SUCCESS;
     }
 
     private void registerCoolingDevice() {
-        // No op.
+        int tagId = StatsLog.COOLING_DEVICE;
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                (atomTag, data) -> pullCooldownDevice(atomTag, data),
+                BackgroundThread.getExecutor()
+        );
     }
 
-    private void pullCooldownDevice() {
-        // No op.
+    private int pullCooldownDevice(int atomTag, List<StatsEvent> pulledData) {
+        IThermalService thermalService = getIThermalService();
+        if (thermalService == null) {
+            return StatsManager.PULL_SKIP;
+        }
+        final long callingToken = Binder.clearCallingIdentity();
+        try {
+            List<CoolingDevice> devices = thermalService.getCurrentCoolingDevices();
+            for (CoolingDevice device : devices) {
+                StatsEvent e = StatsEvent.newBuilder()
+                        .setAtomId(atomTag)
+                        .writeInt(device.getType())
+                        .writeString(device.getName())
+                        .writeInt((int) (device.getValue()))
+                        .build();
+                pulledData.add(e);
+            }
+        } catch (RemoteException e) {
+            // Should not happen.
+            Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures.");
+            return StatsManager.PULL_SKIP;
+        } finally {
+            Binder.restoreCallingIdentity(callingToken);
+        }
+        return StatsManager.PULL_SUCCESS;
     }
 
-    private void registerBinderCalls() {
-        // No op.
+    private void registerBinderCallsStats() {
+        int tagId = StatsLog.BINDER_CALLS;
+        PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+                .setAdditiveFields(new int[] {4, 5, 6, 8, 12})
+                .build();
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                metadata,
+                (atomTag, data) -> pullBinderCallsStats(atomTag, data),
+                BackgroundThread.getExecutor()
+        );
     }
 
-    private void pullBinderCalls() {
-        // No op.
+    private int pullBinderCallsStats(int atomTag, List<StatsEvent> pulledData) {
+        BinderCallsStatsService.Internal binderStats =
+                LocalServices.getService(BinderCallsStatsService.Internal.class);
+        if (binderStats == null) {
+            Slog.e(TAG, "failed to get binderStats");
+            return StatsManager.PULL_SKIP;
+        }
+
+        List<ExportedCallStat> callStats = binderStats.getExportedCallStats();
+        binderStats.reset();
+        for (ExportedCallStat callStat : callStats) {
+            StatsEvent e = StatsEvent.newBuilder()
+                    .setAtomId(atomTag)
+                    .writeInt(callStat.workSourceUid)
+                    .writeString(callStat.className)
+                    .writeString(callStat.methodName)
+                    .writeLong(callStat.callCount)
+                    .writeLong(callStat.exceptionCount)
+                    .writeLong(callStat.latencyMicros)
+                    .writeLong(callStat.maxLatencyMicros)
+                    .writeLong(callStat.cpuTimeMicros)
+                    .writeLong(callStat.maxCpuTimeMicros)
+                    .writeLong(callStat.maxReplySizeBytes)
+                    .writeLong(callStat.maxRequestSizeBytes)
+                    .writeLong(callStat.recordedCallCount)
+                    .writeInt(callStat.screenInteractive ? 1 : 0)
+                    .writeInt(callStat.callingUid)
+                    .build();
+            pulledData.add(e);
+        }
+        return StatsManager.PULL_SUCCESS;
     }
 
-    private void registerBinderCallsExceptions() {
-        // No op.
+    private void registerBinderCallsStatsExceptions() {
+        int tagId = StatsLog.BINDER_CALLS_EXCEPTIONS;
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                (atomTag, data) -> pullBinderCallsStatsExceptions(atomTag, data),
+                BackgroundThread.getExecutor()
+        );
     }
 
-    private void pullBinderCallsExceptions() {
-        // No op.
+    private int pullBinderCallsStatsExceptions(int atomTag, List<StatsEvent> pulledData) {
+        BinderCallsStatsService.Internal binderStats =
+                LocalServices.getService(BinderCallsStatsService.Internal.class);
+        if (binderStats == null) {
+            Slog.e(TAG, "failed to get binderStats");
+            return StatsManager.PULL_SKIP;
+        }
+
+        ArrayMap<String, Integer> exceptionStats = binderStats.getExportedExceptionStats();
+        // TODO: decouple binder calls exceptions with the rest of the binder calls data so that we
+        // can reset the exception stats.
+        for (Map.Entry<String, Integer> entry : exceptionStats.entrySet()) {
+            StatsEvent e = StatsEvent.newBuilder()
+                    .setAtomId(atomTag)
+                    .writeString(entry.getKey())
+                    .writeInt(entry.getValue())
+                    .build();
+            pulledData.add(e);
+        }
+        return StatsManager.PULL_SUCCESS;
     }
 
     private void registerLooperStats() {
@@ -1113,28 +1545,119 @@
         // No op.
     }
 
-    private void registerDeviceCalculatedPowerUse() {
-        // No op.
+    // TODO: move to top of file when all migrations are complete
+    private BatteryStatsHelper mBatteryStatsHelper = null;
+    private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000;
+    private long mBatteryStatsHelperTimestampMs = -MAX_BATTERY_STATS_HELPER_FREQUENCY_MS;
+    private static final long MILLI_AMP_HR_TO_NANO_AMP_SECS = 1_000_000L * 3600L;
+
+    private BatteryStatsHelper getBatteryStatsHelper() {
+        if (mBatteryStatsHelper == null) {
+            final long callingToken = Binder.clearCallingIdentity();
+            try {
+                // clearCallingIdentity required for BatteryStatsHelper.checkWifiOnly().
+                mBatteryStatsHelper = new BatteryStatsHelper(mContext, false);
+            } finally {
+                Binder.restoreCallingIdentity(callingToken);
+            }
+            mBatteryStatsHelper.create((Bundle) null);
+        }
+        long currentTime = SystemClock.elapsedRealtime();
+        if (currentTime - mBatteryStatsHelperTimestampMs >= MAX_BATTERY_STATS_HELPER_FREQUENCY_MS) {
+            // Load BatteryStats and do all the calculations.
+            mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.USER_ALL);
+            // Calculations are done so we don't need to save the raw BatteryStats data in RAM.
+            mBatteryStatsHelper.clearStats();
+            mBatteryStatsHelperTimestampMs = currentTime;
+        }
+        return mBatteryStatsHelper;
     }
 
-    private void pullDeviceCalculatedPowerUse() {
-        // No op.
+    private long milliAmpHrsToNanoAmpSecs(double mAh) {
+        return (long) (mAh * MILLI_AMP_HR_TO_NANO_AMP_SECS + 0.5);
+    }
+
+    private void registerDeviceCalculatedPowerUse() {
+        int tagId = StatsLog.DEVICE_CALCULATED_POWER_USE;
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                (atomTag, data) -> pullDeviceCalculatedPowerUse(atomTag, data),
+                BackgroundThread.getExecutor()
+        );
+    }
+
+    private int pullDeviceCalculatedPowerUse(int atomTag, List<StatsEvent> pulledData) {
+        BatteryStatsHelper bsHelper = getBatteryStatsHelper();
+        StatsEvent e = StatsEvent.newBuilder()
+                .setAtomId(atomTag)
+                .writeLong(milliAmpHrsToNanoAmpSecs(bsHelper.getComputedPower()))
+                .build();
+        pulledData.add(e);
+        return StatsManager.PULL_SUCCESS;
     }
 
     private void registerDeviceCalculatedPowerBlameUid() {
-        // No op.
+        int tagId = StatsLog.DEVICE_CALCULATED_POWER_BLAME_UID;
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                (atomTag, data) -> pullDeviceCalculatedPowerBlameUid(atomTag, data),
+                BackgroundThread.getExecutor()
+        );
     }
 
-    private void pullDeviceCalculatedPowerBlameUid() {
-        // No op.
+    private int pullDeviceCalculatedPowerBlameUid(int atomTag, List<StatsEvent> pulledData) {
+        final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
+        if (sippers == null) {
+            return StatsManager.PULL_SKIP;
+        }
+
+        for (BatterySipper bs : sippers) {
+            if (bs.drainType != bs.drainType.APP) {
+                continue;
+            }
+            StatsEvent e = StatsEvent.newBuilder()
+                    .setAtomId(atomTag)
+                    .writeInt(bs.uidObj.getUid())
+                    .writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah))
+                    .build();
+            pulledData.add(e);
+        }
+        return StatsManager.PULL_SUCCESS;
     }
 
     private void registerDeviceCalculatedPowerBlameOther() {
-        // No op.
+        int tagId = StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER;
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                (atomTag, data) -> pullDeviceCalculatedPowerBlameOther(atomTag, data),
+                BackgroundThread.getExecutor()
+        );
     }
 
-    private void pullDeviceCalculatedPowerBlameOther() {
-        // No op.
+    private int pullDeviceCalculatedPowerBlameOther(int atomTag, List<StatsEvent> pulledData) {
+        final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
+        if (sippers == null) {
+            return StatsManager.PULL_SKIP;
+        }
+
+        for (BatterySipper bs : sippers) {
+            if (bs.drainType == bs.drainType.APP) {
+                continue; // This is a separate atom; see pullDeviceCalculatedPowerBlameUid().
+            }
+            if (bs.drainType == bs.drainType.USER) {
+                continue; // This is not supported. We purposefully calculate over USER_ALL.
+            }
+            StatsEvent e = StatsEvent.newBuilder()
+                    .setAtomId(atomTag)
+                    .writeInt(bs.drainType.ordinal())
+                    .writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah))
+                    .build();
+            pulledData.add(e);
+        }
+        return StatsManager.PULL_SUCCESS;
     }
 
     private void registerDebugElapsedClock() {
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index f4fb93a..baef5d6 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -40,9 +40,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
 
-import java.io.FileDescriptor;
 import java.util.Objects;
 
 /**
@@ -82,7 +80,7 @@
      * @throws ExternalStorageServiceException if the session fails to start
      * @throws IllegalStateException if a session has already been created for {@code vol}
      */
-    public void onVolumeMount(FileDescriptor deviceFd, VolumeInfo vol)
+    public void onVolumeMount(ParcelFileDescriptor deviceFd, VolumeInfo vol)
             throws ExternalStorageServiceException {
         if (!shouldHandle(vol)) {
             return;
@@ -102,8 +100,8 @@
                 mConnections.put(userId, connection);
             }
             Slog.i(TAG, "Creating and starting session with id: " + sessionId);
-            connection.startSession(sessionId, new ParcelFileDescriptor(deviceFd),
-                    vol.getPath().getPath(), vol.getInternalPath().getPath());
+            connection.startSession(sessionId, deviceFd, vol.getPath().getPath(),
+                    vol.getInternalPath().getPath());
         }
     }
 
@@ -185,7 +183,7 @@
      * This call removes all sessions for the user that is being stopped;
      * this will make sure that we don't rebind to the service needlessly.
      */
-    public void onUserStopping(int userId) throws ExternalStorageServiceException {
+    public void onUserStopping(int userId) {
         if (!shouldHandle(null)) {
             return;
         }
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index c02ded8..dd18f4e 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -60,7 +60,8 @@
  */
 public final class StorageUserConnection {
     private static final String TAG = "StorageUserConnection";
-    private static final int REMOTE_TIMEOUT_SECONDS = 15;
+
+    public static final int REMOTE_TIMEOUT_SECONDS = 5;
 
     private final Object mLock = new Object();
     private final Context mContext;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 19bc560..b596d2a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1680,6 +1680,7 @@
         if (root == this) {
             task.setRootProcess(proc);
         }
+        proc.addActivityIfNeeded(this);
     }
 
     boolean hasProcess() {
@@ -6363,6 +6364,14 @@
      *         aspect ratio.
      */
     boolean shouldUseSizeCompatMode() {
+        if (inMultiWindowMode() || getWindowConfiguration().hasWindowDecorCaption()) {
+            final ActivityRecord root = task != null ? task.getRootActivity() : null;
+            if (root != null && root != this && !root.shouldUseSizeCompatMode()) {
+                // If the root activity doesn't use size compatibility mode, the activities above
+                // are forced to be the same for consistent visual appearance.
+                return false;
+            }
+        }
         return !isResizeable() && (info.isFixedOrientation() || info.hasFixedAspectRatio())
                 // The configuration of non-standard type should be enforced by system.
                 && isActivityTypeStandard()
@@ -6910,7 +6919,7 @@
         // Update last reported values.
         final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
 
-        setLastReportedConfiguration(mAtmService.getGlobalConfiguration(), newMergedOverrideConfig);
+        setLastReportedConfiguration(getProcessGlobalConfiguration(), newMergedOverrideConfig);
 
         if (mState == INITIALIZING) {
             // No need to relaunch or schedule new config for activity that hasn't been launched
@@ -7009,6 +7018,11 @@
         return true;
     }
 
+    /** Get process configuration, or global config if the process is not set. */
+    private Configuration getProcessGlobalConfiguration() {
+        return app != null ? app.getConfiguration() : mAtmService.getGlobalConfiguration();
+    }
+
     /**
      * When assessing a configuration change, decide if the changes flags and the new configurations
      * should cause the Activity to relaunch.
@@ -7121,7 +7135,7 @@
             startRelaunching();
             final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
                     pendingNewIntents, configChangeFlags,
-                    new MergedConfiguration(mAtmService.getGlobalConfiguration(),
+                    new MergedConfiguration(getProcessGlobalConfiguration(),
                             getMergedOverrideConfiguration()),
                     preserveWindow);
             final ActivityLifecycleItem lifecycleItem;
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index aa90248..0a68408 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -848,8 +848,6 @@
 
             if (DEBUG_ALL) Slog.v(TAG, "Launching: " + r);
 
-            proc.addActivityIfNeeded(r);
-
             final LockTaskController lockTaskController = mService.getLockTaskController();
             if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
                     || task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index e308f6b..47e8b87 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6743,7 +6743,7 @@
                     return;
                 }
                 process.mIsImeProcess = true;
-                process.registerDisplayConfigurationListenerLocked(displayContent);
+                process.registerDisplayConfigurationListener(displayContent);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 1e60ce8..7b23e2d 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -134,8 +134,8 @@
         resolveOverrideConfiguration(newParentConfig);
         mFullConfiguration.setTo(newParentConfig);
         mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
+        onMergedOverrideConfigurationChanged();
         if (!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration)) {
-            onMergedOverrideConfigurationChanged();
             // This depends on the assumption that change-listeners don't do
             // their own override resolution. This way, dependent hierarchies
             // can stay properly synced-up with a primary hierarchy's constraints.
@@ -147,6 +147,10 @@
                         mResolvedOverrideConfiguration);
             }
         }
+        for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
+            mChangeListeners.get(i).onMergedOverrideConfigurationChanged(
+                    mMergedOverrideConfiguration);
+        }
         if (forwardToChildren) {
             for (int i = getChildCount() - 1; i >= 0; --i) {
                 final ConfigurationContainer child = getChildAt(i);
@@ -545,6 +549,7 @@
         }
         mChangeListeners.add(listener);
         listener.onRequestedOverrideConfigurationChanged(mResolvedOverrideConfiguration);
+        listener.onMergedOverrideConfigurationChanged(mMergedOverrideConfiguration);
     }
 
     void unregisterConfigurationChangeListener(ConfigurationContainerListener listener) {
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainerListener.java b/services/core/java/com/android/server/wm/ConfigurationContainerListener.java
index dc4939d..3d84e17 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainerListener.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainerListener.java
@@ -24,5 +24,8 @@
 public interface ConfigurationContainerListener {
 
     /** {@see ConfigurationContainer#onRequestedOverrideConfigurationChanged} */
-    void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration);
+    default void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {}
+
+    /** Called when new merged override configuration is reported. */
+    default void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfiguration) {}
 }
diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
index e0a7b18..2cb7d5a 100644
--- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java
+++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
@@ -33,6 +33,27 @@
     private final HighRefreshRateBlacklist mHighRefreshRateBlacklist;
     private final WindowManagerService mWmService;
 
+    /**
+     * The following constants represent priority of the window. SF uses this information when
+     * deciding which window has a priority when deciding about the refresh rate of the screen.
+     * Priority 0 is considered the highest priority. -1 means that the priority is unset.
+     */
+    static final int LAYER_PRIORITY_UNSET = -1;
+    /** Windows that are in focus and voted for the preferred mode ID have the highest priority. */
+    static final int LAYER_PRIORITY_FOCUSED_WITH_MODE = 0;
+    /**
+     * This is a default priority for all windows that are in focus, but have not requested a
+     * specific mode ID.
+     */
+    static final int LAYER_PRIORITY_FOCUSED_WITHOUT_MODE = 1;
+    /**
+     * Windows that are not in focus, but voted for a specific mode ID should be
+     * acknowledged by SF. For example, there are two applications in a split screen.
+     * One voted for a given mode ID, and the second one doesn't care. Even though the
+     * second one might be in focus, we can honor the mode ID of the first one.
+     */
+    static final int LAYER_PRIORITY_NOT_FOCUSED_WITH_MODE = 2;
+
     RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo,
             HighRefreshRateBlacklist blacklist) {
         mLowRefreshRateId = findLowRefreshRateModeId(displayInfo);
@@ -92,4 +113,28 @@
         }
         return 0;
     }
+
+    /**
+     * Calculate the priority based on whether the window is in focus and whether the application
+     * voted for a specific refresh rate.
+     *
+     * TODO(b/144307188): This is a very basic algorithm version. Explore other signals that might
+     * be useful in edge cases when we are deciding which layer should get priority when deciding
+     * about the refresh rate.
+     */
+    int calculatePriority(WindowState w) {
+        boolean isFocused = w.isFocused();
+        int preferredModeId = getPreferredModeId(w);
+
+        if (!isFocused && preferredModeId > 0) {
+            return LAYER_PRIORITY_NOT_FOCUSED_WITH_MODE;
+        }
+        if (isFocused && preferredModeId == 0) {
+            return LAYER_PRIORITY_FOCUSED_WITHOUT_MODE;
+        }
+        if (isFocused && preferredModeId > 0) {
+            return LAYER_PRIORITY_FOCUSED_WITH_MODE;
+        }
+        return LAYER_PRIORITY_UNSET;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 9a40b1b1..ceb38f7 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.os.Build.VERSION_CODES.Q;
 import static android.view.Display.INVALID_DISPLAY;
 
@@ -178,8 +179,10 @@
 
     // Last configuration that was reported to the process.
     private final Configuration mLastReportedConfiguration;
+    private final Configuration mNewOverrideConfig = new Configuration();
     // Registered display id as a listener to override config change
     private int mDisplayId;
+    private ActivityRecord mConfigActivityRecord;
 
     /** Whether our process is currently running a {@link RecentsAnimation} */
     private boolean mRunningRecentsAnimation;
@@ -327,6 +330,12 @@
         return mDisplayId != INVALID_DISPLAY;
     }
 
+    /** @return {@code true} if the process registered to an activity as a config listener. */
+    @VisibleForTesting
+    boolean registeredForActivityConfigChanges() {
+        return mConfigActivityRecord != null;
+    }
+
     void postPendingUiCleanMsg(boolean pendingUiClean) {
         if (mListener == null) return;
         // Posting on handler so WM lock isn't held when we call into AM.
@@ -483,7 +492,10 @@
 
     @Override
     protected ConfigurationContainer getParent() {
-        return null;
+        // Returning RootWindowContainer as the parent, so that this process controller always
+        // has full configuration and overrides (e.g. from display) are always added on top of
+        // global config.
+        return mAtm.mRootWindowContainer;
     }
 
     @HotPath(caller = HotPath.PROCESS_CHANGE)
@@ -507,10 +519,12 @@
             return;
         }
         mActivities.add(r);
+        updateActivityConfigurationListener();
     }
 
     void removeActivity(ActivityRecord r) {
         mActivities.remove(r);
+        updateActivityConfigurationListener();
     }
 
     void makeFinishingForProcessRemoved() {
@@ -521,6 +535,7 @@
 
     void clearActivities() {
         mActivities.clear();
+        updateActivityConfigurationListener();
     }
 
     @HotPath(caller = HotPath.OOM_ADJUSTMENT)
@@ -964,19 +979,20 @@
         mAtm.mH.sendMessage(m);
     }
 
-    void registerDisplayConfigurationListenerLocked(DisplayContent displayContent) {
+    void registerDisplayConfigurationListener(DisplayContent displayContent) {
         if (displayContent == null) {
             return;
         }
-        // A process can only register to one display to listener to the override configuration
+        // A process can only register to one display to listen to the override configuration
         // change. Unregister existing listener if it has one before register the new one.
-        unregisterDisplayConfigurationListenerLocked();
+        unregisterDisplayConfigurationListener();
+        unregisterActivityConfigurationListener();
         mDisplayId = displayContent.mDisplayId;
         displayContent.registerConfigurationChangeListener(this);
     }
 
     @VisibleForTesting
-    void unregisterDisplayConfigurationListenerLocked() {
+    void unregisterDisplayConfigurationListener() {
         if (mDisplayId == INVALID_DISPLAY) {
             return;
         }
@@ -986,6 +1002,48 @@
             displayContent.unregisterConfigurationChangeListener(this);
         }
         mDisplayId = INVALID_DISPLAY;
+        onMergedOverrideConfigurationChanged(Configuration.EMPTY);
+    }
+
+    private void registerActivityConfigurationListener(ActivityRecord activityRecord) {
+        if (activityRecord == null) {
+            return;
+        }
+        // A process can only register to one activityRecord to listen to the override configuration
+        // change. Unregister existing listener if it has one before register the new one.
+        unregisterDisplayConfigurationListener();
+        unregisterActivityConfigurationListener();
+        mConfigActivityRecord = activityRecord;
+        activityRecord.registerConfigurationChangeListener(this);
+    }
+
+    private void unregisterActivityConfigurationListener() {
+        if (mConfigActivityRecord == null) {
+            return;
+        }
+        mConfigActivityRecord.unregisterConfigurationChangeListener(this);
+        mConfigActivityRecord = null;
+        onMergedOverrideConfigurationChanged(Configuration.EMPTY);
+    }
+
+    /**
+     * Check if activity configuration override for the activity process needs an update and perform
+     * if needed. By default we try to override the process configuration to match the top activity
+     * config to increase app compatibility with multi-window and multi-display. The process will
+     * always track the configuration of the non-finishing activity last added to the process.
+     */
+    private void updateActivityConfigurationListener() {
+        for (int i = mActivities.size() - 1; i >= 0; i--) {
+            final ActivityRecord activityRecord = mActivities.get(i);
+            if (!activityRecord.finishing && !activityRecord.containsListener(this)) {
+                // Eligible activity is found, update listener.
+                registerActivityConfigurationListener(activityRecord);
+                return;
+            }
+        }
+
+        // No eligible activities found, let's remove the configuration listener.
+        unregisterActivityConfigurationListener();
     }
 
     @Override
@@ -995,8 +1053,11 @@
     }
 
     @Override
-    public void onRequestedOverrideConfigurationChanged(Configuration newOverrideConfig) {
-        super.onRequestedOverrideConfigurationChanged(newOverrideConfig);
+    public void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfig) {
+        // Make sure that we don't accidentally override the activity type.
+        mNewOverrideConfig.setTo(mergedOverrideConfig);
+        mNewOverrideConfig.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+        super.onRequestedOverrideConfigurationChanged(mNewOverrideConfig);
         updateConfiguration();
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c2eb0e4..a4297416 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -657,6 +657,13 @@
     private KeyInterceptionInfo mKeyInterceptionInfo;
 
     /**
+     * This information is passed to SurfaceFlinger to decide which window should have a priority
+     * when deciding about the refresh rate of the display. All windows have the lowest priority by
+     * default. The variable is cached, so we do not send too many updates to SF.
+     */
+    int mFrameRateSelectionPriority = RefreshRatePolicy.LAYER_PRIORITY_UNSET;
+
+    /**
      * @return The insets state as requested by the client, i.e. the dispatched insets state
      *         for which the visibilities are overridden with what the client requested.
      */
@@ -3395,7 +3402,7 @@
                     "Reporting new frame to %s: %s", this,
                             mWindowFrames.mCompatFrame);
             final MergedConfiguration mergedConfiguration =
-                    new MergedConfiguration(mWmService.mRoot.getConfiguration(),
+                    new MergedConfiguration(getProcessGlobalConfiguration(),
                     getMergedOverrideConfiguration());
 
             setLastReportedMergedConfiguration(mergedConfiguration);
@@ -5165,6 +5172,24 @@
         }
     }
 
+
+    /**
+     * Notifies SF about the priority of the window, if it changed. SF then uses this information
+     * to decide which window's desired rendering rate should have a priority when deciding about
+     * the refresh rate of the screen. Priority
+     * {@link RefreshRatePolicy#LAYER_PRIORITY_FOCUSED_WITH_MODE} is considered the highest.
+     */
+    @VisibleForTesting
+    void updateFrameRateSelectionPriorityIfNeeded() {
+        final int priority = getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
+                .calculatePriority(this);
+        if (mFrameRateSelectionPriority != priority) {
+            mFrameRateSelectionPriority = priority;
+            getPendingTransaction().setFrameRateSelectionPriority(mSurfaceControl,
+                    mFrameRateSelectionPriority);
+        }
+    }
+
     @Override
     void prepareSurfaces() {
         final Dimmer dimmer = getDimmer();
@@ -5173,6 +5198,8 @@
             applyDims(dimmer);
         }
         updateSurfacePosition();
+        // Send information to SufaceFlinger about the priority of the current window.
+        updateFrameRateSelectionPriorityIfNeeded();
 
         mWinAnimator.prepareSurfaceLocked(true);
         super.prepareSurfaces();
diff --git a/services/core/jni/com_android_server_GraphicsStatsService.cpp b/services/core/jni/com_android_server_GraphicsStatsService.cpp
index 9353fbd..7644ade 100644
--- a/services/core/jni/com_android_server_GraphicsStatsService.cpp
+++ b/services/core/jni/com_android_server_GraphicsStatsService.cpp
@@ -178,15 +178,16 @@
 }
 
 // graphicsStatsPullCallback is invoked by statsd service to pull GRAPHICS_STATS atom.
-static bool graphicsStatsPullCallback(int32_t atom_tag, pulled_stats_event_list* data,
-                                      const void* cookie) {
+static status_pull_atom_return_t graphicsStatsPullCallback(int32_t atom_tag,
+                                                           pulled_stats_event_list* data,
+                                                           void* cookie) {
     JNIEnv* env = getJNIEnv();
     if (!env) {
         return false;
     }
     if (gGraphicsStatsServiceObject == nullptr) {
         ALOGE("Failed to get graphicsstats service");
-        return false;
+        return STATS_PULL_SKIP;
     }
 
     for (bool lastFullDay : {true, false}) {
@@ -198,7 +199,7 @@
             env->ExceptionDescribe();
             env->ExceptionClear();
             ALOGE("Failed to invoke graphicsstats service");
-            return false;
+            return STATS_PULL_SKIP;
         }
         if (!jdata) {
             // null means data is not available for that day.
@@ -217,7 +218,7 @@
         if (!success) {
             ALOGW("Parse failed on GraphicsStatsPuller error='%s' dataSize='%d'",
                   serviceDump.InitializationErrorString().c_str(), dataSize);
-            return false;
+            return STATS_PULL_SKIP;
         }
 
         for (int stat_index = 0; stat_index < serviceDump.stats_size(); stat_index++) {
@@ -244,7 +245,7 @@
             stats_event_build(event);
         }
     }
-    return true;
+    return STATS_PULL_SUCCESS;
 }
 
 // Register a puller for GRAPHICS_STATS atom with the statsd service.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7fdb4c4..b8b0dbf 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1095,7 +1095,7 @@
         String globalProxySpec = null;
         String globalProxyExclusionList = null;
 
-        ArrayMap<String, TrustAgentInfo> trustAgentInfos = new ArrayMap<>();
+        @NonNull ArrayMap<String, TrustAgentInfo> trustAgentInfos = new ArrayMap<>();
 
         List<String> crossProfileWidgetProviders;
 
@@ -1650,6 +1650,7 @@
             }
         }
 
+        @NonNull
         private ArrayMap<String, TrustAgentInfo> getAllTrustAgentInfos(
                 XmlPullParser parser, String tag) throws XmlPullParserException, IOException {
             int outerDepthDAM = parser.getDepth();
@@ -2434,11 +2435,133 @@
             migrateUserRestrictionsIfNecessaryLocked();
 
             // TODO PO may not have a class name either due to b/17652534.  Address that too.
-
             updateDeviceOwnerLocked();
         }
     }
 
+    /**
+     * Checks if the device is in COMP mode, and if so migrates it to managed profile on a
+     * corporate owned device.
+     */
+    @GuardedBy("getLockObject()")
+    private void maybeMigrateToProfileOnOrganizationOwnedDeviceLocked() {
+        logIfVerbose("Checking whether we need to migrate COMP ");
+        final int doUserId = mOwners.getDeviceOwnerUserId();
+        if (doUserId == UserHandle.USER_NULL) {
+            logIfVerbose("No DO found, skipping migration.");
+            return;
+        }
+
+        final List<UserInfo> profiles = mUserManager.getProfiles(doUserId);
+        if (profiles.size() != 2) {
+            if (profiles.size() == 1) {
+                logIfVerbose("Profile not found, skipping migration.");
+            } else {
+                Slog.wtf(LOG_TAG, "Found " + profiles.size() + " profiles, skipping migration");
+            }
+            return;
+        }
+
+        final int poUserId = getManagedUserId(doUserId);
+        if (poUserId < 0) {
+            Slog.wtf(LOG_TAG, "Found DO and a profile, but it is not managed, skipping migration");
+            return;
+        }
+
+        final ActiveAdmin doAdmin = getDeviceOwnerAdminLocked();
+        final ActiveAdmin poAdmin = getProfileOwnerAdminLocked(poUserId);
+        if (doAdmin == null || poAdmin == null) {
+            Slog.wtf(LOG_TAG, "Failed to get either PO or DO admin, aborting migration.");
+            return;
+        }
+
+        final ComponentName doAdminComponent = mOwners.getDeviceOwnerComponent();
+        final ComponentName poAdminComponent = mOwners.getProfileOwnerComponent(poUserId);
+        if (doAdminComponent == null || poAdminComponent == null) {
+            Slog.wtf(LOG_TAG, "Cannot find PO or DO component name, aborting migration.");
+            return;
+        }
+        if (!doAdminComponent.getPackageName().equals(poAdminComponent.getPackageName())) {
+            Slog.e(LOG_TAG, "DO and PO are different packages, aborting migration.");
+            return;
+        }
+
+        Slog.i(LOG_TAG, String.format(
+                "Migrating COMP to PO on a corp owned device; primary user: %d; profile: %d",
+                doUserId, poUserId));
+
+        Slog.i(LOG_TAG, "Giving the PO additional power...");
+        markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(poAdminComponent, poUserId);
+        Slog.i(LOG_TAG, "Migrating DO policies to PO...");
+        moveDoPoliciesToProfileParentAdmin(doAdmin, poAdmin.getParentActiveAdmin());
+        saveSettingsLocked(poUserId);
+        Slog.i(LOG_TAG, "Clearing the DO...");
+        final ComponentName doAdminReceiver = doAdmin.info.getComponent();
+        clearDeviceOwnerLocked(doAdmin, doUserId);
+        // TODO(b/143516163): If we have a power cut here, we might leave active admin. Consider if
+        // it is worth the complexity to make it more robust.
+        Slog.i(LOG_TAG, "Removing admin artifacts...");
+        // TODO(b/143516163): Clean up application restrictions in UserManager.
+        removeAdminArtifacts(doAdminReceiver, doUserId);
+        Slog.i(LOG_TAG, "Migration complete.");
+
+        // Note: KeyChain keys are not removed and will remain accessible for the apps that have
+        // been given grants to use them.
+    }
+
+    private void moveDoPoliciesToProfileParentAdmin(ActiveAdmin doAdmin, ActiveAdmin parentAdmin) {
+        // The following policies can be already controlled via parent instance, skip if so.
+        if (parentAdmin.mPasswordPolicy.quality == PASSWORD_QUALITY_UNSPECIFIED) {
+            parentAdmin.mPasswordPolicy = doAdmin.mPasswordPolicy;
+        }
+        if (parentAdmin.passwordHistoryLength == ActiveAdmin.DEF_PASSWORD_HISTORY_LENGTH) {
+            parentAdmin.passwordHistoryLength = doAdmin.passwordHistoryLength;
+        }
+        if (parentAdmin.passwordExpirationTimeout == ActiveAdmin.DEF_PASSWORD_HISTORY_LENGTH) {
+            parentAdmin.passwordExpirationTimeout = doAdmin.passwordExpirationTimeout;
+        }
+        if (parentAdmin.maximumFailedPasswordsForWipe
+                == ActiveAdmin.DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE) {
+            parentAdmin.maximumFailedPasswordsForWipe = doAdmin.maximumFailedPasswordsForWipe;
+        }
+        if (parentAdmin.maximumTimeToUnlock == ActiveAdmin.DEF_MAXIMUM_TIME_TO_UNLOCK) {
+            parentAdmin.maximumTimeToUnlock = doAdmin.maximumTimeToUnlock;
+        }
+        if (parentAdmin.strongAuthUnlockTimeout
+                == DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS) {
+            parentAdmin.strongAuthUnlockTimeout = doAdmin.strongAuthUnlockTimeout;
+        }
+        parentAdmin.disabledKeyguardFeatures |=
+                doAdmin.disabledKeyguardFeatures & PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
+
+        parentAdmin.trustAgentInfos.putAll(doAdmin.trustAgentInfos);
+
+        // The following policies weren't available to PO, but will be available after migration.
+        parentAdmin.disableCamera = doAdmin.disableCamera;
+
+        // TODO(b/143516163): Uncomment once corresponding APIs are available via parent instance.
+        // parentAdmin.disableScreenCapture = doAdmin.disableScreenCapture;
+        // parentAdmin.accountTypesWithManagementDisabled.addAll(
+        //         doAdmin.accountTypesWithManagementDisabled);
+
+        moveDoUserRestrictionsToCopeParent(doAdmin, parentAdmin);
+
+        // TODO(b/143516163): migrate network and security logging state, currently they are
+        // turned off when DO is removed.
+    }
+
+    private void moveDoUserRestrictionsToCopeParent(ActiveAdmin doAdmin, ActiveAdmin parentAdmin) {
+        if (doAdmin.userRestrictions == null) {
+            return;
+        }
+        for (final String restriction : doAdmin.userRestrictions.keySet()) {
+            if (UserRestrictionsUtils.canProfileOwnerOfOrganizationOwnedDeviceChange(restriction)) {
+                parentAdmin.userRestrictions.putBoolean(
+                        restriction, doAdmin.userRestrictions.getBoolean(restriction));
+            }
+        }
+    }
+
     /** Apply default restrictions that haven't been applied to profile owners yet. */
     private void maybeSetDefaultProfileOwnerUserRestrictions() {
         synchronized (getLockObject()) {
@@ -3625,6 +3748,9 @@
                 break;
             case SystemService.PHASE_ACTIVITY_MANAGER_READY:
                 maybeStartSecurityLogMonitorOnActivityManagerReady();
+                synchronized (getLockObject()) {
+                    maybeMigrateToProfileOnOrganizationOwnedDeviceLocked();
+                }
                 break;
             case SystemService.PHASE_BOOT_COMPLETED:
                 ensureDeviceOwnerUserStarted(); // TODO Consider better place to do this.
@@ -7456,8 +7582,7 @@
             return;
         }
         Objects.requireNonNull(who, "ComponentName is null");
-        // TODO (b/145286957) Refactor security checks
-        enforceDeviceOwnerOrProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
+        enforceProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
 
         mInjector.binderWithCleanCallingIdentity(() ->
                 mInjector.settingsGlobalPutInt(Settings.Global.AUTO_TIME, enabled ? 1 : 0));
@@ -7478,7 +7603,7 @@
             return false;
         }
         Objects.requireNonNull(who, "ComponentName is null");
-        enforceDeviceOwnerOrProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
+        enforceProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
 
         return mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) > 0;
     }
@@ -7492,8 +7617,7 @@
             return;
         }
         Objects.requireNonNull(who, "ComponentName is null");
-        // TODO (b/145286957) Refactor security checks
-        enforceDeviceOwnerOrProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
+        enforceProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
 
         mInjector.binderWithCleanCallingIdentity(() ->
                 mInjector.settingsGlobalPutInt(Global.AUTO_TIME_ZONE, enabled ? 1 : 0));
@@ -7514,7 +7638,7 @@
             return false;
         }
         Objects.requireNonNull(who, "ComponentName is null");
-        enforceDeviceOwnerOrProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
+        enforceProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
 
         return mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) > 0;
     }
@@ -9060,23 +9184,22 @@
                 "Only profile owner, device owner and system may call this method.");
     }
 
-    private ActiveAdmin enforceDeviceOwnerOrProfileOwnerOnUser0OrProfileOwnerOrganizationOwned() {
+    private void enforceProfileOwnerOnUser0OrProfileOwnerOrganizationOwned() {
         synchronized (getLockObject()) {
-            // Check if there is a device owner
-            ActiveAdmin deviceOwner = getActiveAdminWithPolicyForUidLocked(null,
-                    DeviceAdminInfo.USES_POLICY_DEVICE_OWNER, mInjector.binderGetCallingUid());
-            if (deviceOwner != null) return deviceOwner;
+            // Check if there is a device owner or profile owner of an organization-owned device
+            ActiveAdmin owner = getActiveAdminWithPolicyForUidLocked(null,
+                    DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER,
+                    mInjector.binderGetCallingUid());
+            if (owner != null) {
+                return;
+            }
 
-            ActiveAdmin profileOwner = getActiveAdminWithPolicyForUidLocked(null,
+            // Checks whether the caller is a profile owner on user 0 rather than
+            // checking whether the active admin is on user 0
+            owner = getActiveAdminWithPolicyForUidLocked(null,
                     DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, mInjector.binderGetCallingUid());
-
-            // Check if there is a profile owner of an organization owned device
-            if (isProfileOwnerOfOrganizationOwnedDevice(profileOwner)) return profileOwner;
-
-            // Check if there is a profile owner called on user 0
-            if (profileOwner != null) {
-                enforceCallerSystemUserHandle();
-                return profileOwner;
+            if (owner != null && owner.getUserHandle().isSystem()) {
+                return;
             }
         }
         throw new SecurityException("No active admin found");
@@ -12922,37 +13045,43 @@
 
         // Grant access under lock.
         synchronized (getLockObject()) {
-            // Sanity check: Make sure that the user has a profile owner and that the specified
-            // component is the profile owner of that user.
-            if (!isProfileOwner(who, userId)) {
-                throw new IllegalArgumentException(String.format(
-                        "Component %s is not a Profile Owner of user %d",
-                        who.flattenToString(), userId));
-            }
+            markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(who, userId);
+        }
+    }
 
-            Slog.i(LOG_TAG, String.format(
-                    "Marking %s as profile owner on organization-owned device for user %d",
+    @GuardedBy("getLockObject()")
+    private void markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(
+            ComponentName who, int userId) {
+        // Sanity check: Make sure that the user has a profile owner and that the specified
+        // component is the profile owner of that user.
+        if (!isProfileOwner(who, userId)) {
+            throw new IllegalArgumentException(String.format(
+                    "Component %s is not a Profile Owner of user %d",
                     who.flattenToString(), userId));
+        }
 
-            // First, set restriction on removing the profile.
-            mInjector.binderWithCleanCallingIdentity(() -> {
-                // Clear restriction as user.
-                UserHandle parentUser = mUserManager.getProfileParent(UserHandle.of(userId));
-                if (!parentUser.isSystem()) {
-                    throw new IllegalStateException(
-                            String.format("Only the profile owner of a managed profile on the"
+        Slog.i(LOG_TAG, String.format(
+                "Marking %s as profile owner on organization-owned device for user %d",
+                who.flattenToString(), userId));
+
+        // First, set restriction on removing the profile.
+        mInjector.binderWithCleanCallingIdentity(() -> {
+            // Clear restriction as user.
+            final UserHandle parentUser = mUserManager.getProfileParent(UserHandle.of(userId));
+            if (!parentUser.isSystem()) {
+                throw new IllegalStateException(
+                        String.format("Only the profile owner of a managed profile on the"
                                 + " primary user can be granted access to device identifiers, not"
                                 + " on user %d", parentUser.getIdentifier()));
-                }
+            }
 
-                mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, true,
-                        parentUser);
-            });
+            mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, true,
+                    parentUser);
+        });
 
-            // markProfileOwnerOfOrganizationOwnedDevice will trigger writing of the profile owner
-            // data, no need to do it manually.
-            mOwners.markProfileOwnerOfOrganizationOwnedDevice(userId);
-        }
+        // markProfileOwnerOfOrganizationOwnedDevice will trigger writing of the profile owner
+        // data, no need to do it manually.
+        mOwners.markProfileOwnerOfOrganizationOwnedDevice(userId);
     }
 
     private void pushMeteredDisabledPackagesLocked(int userId) {
@@ -14916,4 +15045,10 @@
             return packages == null ? Collections.EMPTY_LIST : packages;
         }
     }
+
+    private void logIfVerbose(String message) {
+        if (VERBOSE_LOG) {
+            Slog.d(LOG_TAG, message);
+        }
+    }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0bd7392..109a6fd 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -218,7 +218,7 @@
     private static final String STATS_COMPANION_LIFECYCLE_CLASS =
             "com.android.server.stats.StatsCompanion$Lifecycle";
     private static final String STATS_PULL_ATOM_SERVICE_CLASS =
-            "com.android.server.stats.StatsPullAtomService";
+            "com.android.server.stats.pull.StatsPullAtomService";
     private static final String USB_SERVICE_CLASS =
             "com.android.server.usb.UsbService$Lifecycle";
     private static final String MIDI_SERVICE_CLASS =
@@ -758,7 +758,7 @@
         // note that we just booted, which might send out a rescue party if
         // we're stuck in a runtime restart loop.
         RescueParty.registerHealthObserver(mSystemContext);
-        RescueParty.noteBoot(mSystemContext);
+        PackageWatchdog.getInstance(mSystemContext).noteBoot();
 
         // Manages LEDs and display backlight so we need it to bring up the display.
         t.traceBegin("StartLightsService");
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 96fedf9..3d9f11f 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -21,6 +21,7 @@
         "services.core",
         "services.net",
         "service-jobscheduler",
+        "service-permission",
         "androidx.test.runner",
         "mockito-target-extended-minus-junit4",
         "platform-test-annotations",
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index 30d89d3..c3602f8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -24,6 +24,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.RescueParty.LEVEL_FACTORY_RESET;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -144,7 +145,6 @@
 
 
         doReturn(CURRENT_NETWORK_TIME_MILLIS).when(() -> RescueParty.getElapsedRealtime());
-        RescueParty.resetAllThresholds();
         FlagNamespaceUtils.resetKnownResetNamespacesFlagCounterForTest();
 
         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL,
@@ -160,28 +160,28 @@
 
     @Test
     public void testBootLoopDetectionWithExecutionForAllRescueLevels() {
-        noteBoot(RescueParty.TRIGGER_COUNT);
+        noteBoot();
 
         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
 
-        noteBoot(RescueParty.TRIGGER_COUNT);
+        noteBoot();
 
         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES);
         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES,
                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
 
-        noteBoot(RescueParty.TRIGGER_COUNT);
+        noteBoot();
 
         verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS);
         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS,
                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
 
-        noteBoot(RescueParty.TRIGGER_COUNT);
+        noteBoot();
 
         verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
-        assertEquals(RescueParty.LEVEL_FACTORY_RESET,
+        assertEquals(LEVEL_FACTORY_RESET,
                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
     }
 
@@ -208,48 +208,15 @@
         notePersistentAppCrash();
 
         verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
-        assertEquals(RescueParty.LEVEL_FACTORY_RESET,
-                SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
-    }
-
-    @Test
-    public void testBootLoopDetectionWithWrongInterval() {
-        noteBoot(RescueParty.TRIGGER_COUNT - 1);
-
-        // last boot is just outside of the boot loop detection window
-        doReturn(CURRENT_NETWORK_TIME_MILLIS + RescueParty.BOOT_TRIGGER_WINDOW_MILLIS + 1).when(
-                () -> RescueParty.getElapsedRealtime());
-        noteBoot(/*numTimes=*/1);
-
-        assertEquals(RescueParty.LEVEL_NONE,
-                SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
-    }
-
-    @Test
-    public void testBootLoopDetectionWithProperInterval() {
-        noteBoot(RescueParty.TRIGGER_COUNT - 1);
-
-        // last boot is just inside of the boot loop detection window
-        doReturn(CURRENT_NETWORK_TIME_MILLIS + RescueParty.BOOT_TRIGGER_WINDOW_MILLIS).when(
-                () -> RescueParty.getElapsedRealtime());
-        noteBoot(/*numTimes=*/1);
-
-        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
-        assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
-                SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
-    }
-
-    @Test
-    public void testBootLoopDetectionWithWrongTriggerCount() {
-        noteBoot(RescueParty.TRIGGER_COUNT - 1);
-        assertEquals(RescueParty.LEVEL_NONE,
+        assertEquals(LEVEL_FACTORY_RESET,
                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
     }
 
     @Test
     public void testIsAttemptingFactoryReset() {
-        noteBoot(RescueParty.TRIGGER_COUNT * 4);
-
+        for (int i = 0; i < LEVEL_FACTORY_RESET; i++) {
+            noteBoot();
+        }
         verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
         assertTrue(RescueParty.isAttemptingFactoryReset());
     }
@@ -306,7 +273,7 @@
 
         // Ensure that no action is taken for cases where the failure reason is unknown
         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
-                RescueParty.LEVEL_FACTORY_RESET));
+                LEVEL_FACTORY_RESET));
         assertEquals(observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_UNKNOWN),
                 PackageHealthObserverImpact.USER_IMPACT_NONE);
 
@@ -342,7 +309,7 @@
 
 
         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
-                RescueParty.LEVEL_FACTORY_RESET));
+                LEVEL_FACTORY_RESET));
         assertEquals(observer.onHealthCheckFailed(null,
                 PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING),
                 PackageHealthObserverImpact.USER_IMPACT_HIGH);
@@ -366,10 +333,8 @@
                 eq(resetMode), anyInt()));
     }
 
-    private void noteBoot(int numTimes) {
-        for (int i = 0; i < numTimes; i++) {
-            RescueParty.noteBoot(mMockContext);
-        }
+    private void noteBoot() {
+        RescuePartyObserver.getInstance(mMockContext).executeBootLoopMitigation();
     }
 
     private void notePersistentAppCrash() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index f2e118d..e0e374b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -50,6 +50,7 @@
 import android.os.BatteryManagerInternal;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 
 import com.android.server.AppStateTracker;
@@ -95,6 +96,7 @@
                 .initMocks(this)
                 .strictness(Strictness.LENIENT)
                 .mockStatic(LocalServices.class)
+                .mockStatic(ServiceManager.class)
                 .startMocking();
 
         // Called in JobSchedulerService constructor.
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index ace15eb..556f636 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -44,6 +44,7 @@
         "servicestests-utils",
         "service-appsearch",
         "service-jobscheduler",
+        "service-permission",
         // TODO: remove once Android migrates to JUnit 4.12,
         // which provides assertThrows
         "testng",
diff --git a/services/tests/servicestests/res/raw/comp_device_owner.xml b/services/tests/servicestests/res/raw/comp_device_owner.xml
new file mode 100644
index 0000000..0a10242
--- /dev/null
+++ b/services/tests/servicestests/res/raw/comp_device_owner.xml
@@ -0,0 +1,8 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<root>
+    <device-owner package="com.android.frameworks.servicestests"
+        name=""
+        component="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1"
+        userRestrictionsMigrated="true" />
+    <device-owner-context userId="0" />
+</root>
diff --git a/services/tests/servicestests/res/raw/comp_policies_primary.xml b/services/tests/servicestests/res/raw/comp_policies_primary.xml
new file mode 100644
index 0000000..1e1a0ef
--- /dev/null
+++ b/services/tests/servicestests/res/raw/comp_policies_primary.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<policies setup-complete="true" provisioning-state="3">
+    <admin name="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1">
+        <policies flags="991"/>
+        <password-history-length value="33" />
+    </admin>
+</policies>
diff --git a/services/tests/servicestests/res/raw/comp_policies_profile_another_package.xml b/services/tests/servicestests/res/raw/comp_policies_profile_another_package.xml
new file mode 100644
index 0000000..141315e
--- /dev/null
+++ b/services/tests/servicestests/res/raw/comp_policies_profile_another_package.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<policies setup-complete="true" provisioning-state="3">
+    <admin name="com.another.package.name/whatever.random.class">
+        <policies flags="991"/>
+    </admin>
+</policies>
diff --git a/services/tests/servicestests/res/raw/comp_policies_profile_same_package.xml b/services/tests/servicestests/res/raw/comp_policies_profile_same_package.xml
new file mode 100644
index 0000000..c874dcc
--- /dev/null
+++ b/services/tests/servicestests/res/raw/comp_policies_profile_same_package.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<policies setup-complete="true" provisioning-state="3">
+    <admin name="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1">
+        <policies flags="991"/>
+    </admin>
+</policies>
diff --git a/services/tests/servicestests/res/raw/comp_profile_owner_another_package.xml b/services/tests/servicestests/res/raw/comp_profile_owner_another_package.xml
new file mode 100644
index 0000000..d65ba78
--- /dev/null
+++ b/services/tests/servicestests/res/raw/comp_profile_owner_another_package.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<root>
+    <profile-owner package="com.another.package.name"
+        name="com.another.package.name"
+        component="com.another.package.name/whatever.random.class"
+        userRestrictionsMigrated="true"/>
+</root>
diff --git a/services/tests/servicestests/res/raw/comp_profile_owner_same_package.xml b/services/tests/servicestests/res/raw/comp_profile_owner_same_package.xml
new file mode 100644
index 0000000..7f98c91c
--- /dev/null
+++ b/services/tests/servicestests/res/raw/comp_profile_owner_same_package.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<root>
+    <profile-owner package="com.android.frameworks.servicestests"
+        name="com.android.frameworks.servicestests"
+        component="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1"
+        userRestrictionsMigrated="true"/>
+</root>
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
new file mode 100644
index 0000000..09466e7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.accessibility.magnification;
+
+
+import static org.mockito.Mockito.verify;
+
+import android.os.RemoteException;
+import android.view.Display;
+import android.view.accessibility.IWindowMagnificationConnection;
+import android.view.accessibility.IWindowMagnificationConnectionCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for WindowMagnificationConnectionWrapper. We don't test {@code
+ * WindowMagnificationConnectionWrapper#linkToDeath(IBinder.DeathRecipient)} since it's tested in
+ * {@link WindowMagnificationManagerTest}.
+ */
+public class WindowMagnificationConnectionWrapperTest {
+
+    private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
+
+    @Mock
+    private IWindowMagnificationConnection mConnection;
+    @Mock
+    private IWindowMagnificationConnectionCallback mCallback;
+    private WindowMagnificationConnectionWrapper mConnectionWrapper;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection);
+    }
+
+    @Test
+    public void enableWindowMagnification() throws RemoteException {
+        mConnectionWrapper.enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f);
+        verify(mConnection).enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f);
+    }
+
+    @Test
+    public void setScale() throws RemoteException {
+        mConnectionWrapper.setScale(TEST_DISPLAY, 3.0f);
+        verify(mConnection).setScale(TEST_DISPLAY, 3.0f);
+    }
+
+    @Test
+    public void disableWindowMagnification() throws RemoteException {
+        mConnectionWrapper.disableWindowMagnification(TEST_DISPLAY);
+        verify(mConnection).disableWindowMagnification(TEST_DISPLAY);
+    }
+
+    @Test
+    public void moveWindowMagnifier() throws RemoteException {
+        mConnectionWrapper.moveWindowMagnifier(0, 100, 150);
+        verify(mConnection).moveWindowMagnifier(0, 100, 150);
+    }
+
+    @Test
+    public void setMirrorWindowCallback() throws RemoteException {
+        mConnectionWrapper.setConnectionCallback(mCallback);
+        verify(mConnection).setConnectionCallback(mCallback);
+    }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
new file mode 100644
index 0000000..780a6c0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.accessibility.magnification;
+
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.view.accessibility.IWindowMagnificationConnection;
+import android.view.accessibility.IWindowMagnificationConnectionCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for WindowMagnificationManager.
+ */
+public class WindowMagnificationManagerTest {
+
+    private MockWindowMagnificationConnection mMockConnection;
+    private WindowMagnificationManager mWindowMagnificationManager;
+
+    @Before
+    public void setUp() {
+        mMockConnection = new MockWindowMagnificationConnection();
+        mWindowMagnificationManager = new WindowMagnificationManager();
+    }
+
+    @Test
+    public void setConnection_connectionIsNull_wrapperIsNullAndLinkToDeath() {
+        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+        assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
+        verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
+    }
+
+    @Test
+    public void setConnection_connectionIsNull_setMirrorWindowCallbackAndHasWrapper()
+            throws RemoteException {
+        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+
+        assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
+        verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
+        verify(mMockConnection.getConnection()).setConnectionCallback(
+                any(IWindowMagnificationConnectionCallback.class));
+    }
+
+    @Test
+    public void binderDied_hasConnection_wrapperIsNullAndUnlinkToDeath() {
+        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+
+        mMockConnection.getDeathRecipient().binderDied();
+
+        assertNull(mWindowMagnificationManager.mConnectionWrapper);
+        verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(),
+                0);
+    }
+
+    /**
+     * This test simulates {@link WindowMagnificationManager#setConnection} is called by thread A
+     * and then the former connection is called by thread B. In this situation we should keep the
+     * new connection.
+     */
+    @Test
+    public void
+            setSecondConnectionAndFormerConnectionBinderDead_hasWrapperAndNotCallUnlinkToDeath() {
+        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+        MockWindowMagnificationConnection secondConnection =
+                new MockWindowMagnificationConnection();
+
+        mWindowMagnificationManager.setConnection(secondConnection.getConnection());
+        mMockConnection.getDeathRecipient().binderDied();
+
+        assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
+        verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(), 0);
+        verify(secondConnection.asBinder(), never()).unlinkToDeath(
+                secondConnection.getDeathRecipient(), 0);
+    }
+
+    @Test
+    public void setNullConnection_hasConnection_wrapperIsNull() throws RemoteException {
+        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+
+        mWindowMagnificationManager.setConnection(null);
+
+        assertNull(mWindowMagnificationManager.mConnectionWrapper);
+        verify(mMockConnection.getConnection()).setConnectionCallback(null);
+    }
+
+    private static class MockWindowMagnificationConnection  {
+
+        private final IWindowMagnificationConnection mConnection;
+        private final Binder mBinder;
+        private IBinder.DeathRecipient mDeathRecipient;
+
+        MockWindowMagnificationConnection() {
+            mConnection = mock(IWindowMagnificationConnection.class);
+            mBinder = mock(Binder.class);
+            when(mConnection.asBinder()).thenReturn(mBinder);
+            doAnswer((invocation) -> {
+                mDeathRecipient = invocation.getArgument(0);
+                return null;
+            }).when(mBinder).linkToDeath(
+                    any(IBinder.DeathRecipient.class), eq(0));
+        }
+
+        IWindowMagnificationConnection getConnection() {
+            return mConnection;
+        }
+
+        public IBinder.DeathRecipient getDeathRecipient() {
+            return mDeathRecipient;
+        }
+
+        Binder asBinder() {
+            return mBinder;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/impl/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/impl/AppSearchImplTest.java
new file mode 100644
index 0000000..4195679
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appsearch/impl/AppSearchImplTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.appsearch.impl;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.expectThrows;
+
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.os.UserHandle;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.google.android.icing.proto.IndexingConfig;
+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;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class AppSearchImplTest {
+    private final Context mContext = InstrumentationRegistry.getContext();
+    private final @UserIdInt int mUserId = UserHandle.getCallingUserId();
+
+    @Test
+    public void testRewriteSchemaTypes() {
+        SchemaProto inSchema = SchemaProto.newBuilder()
+                .addTypes(SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("TestType")
+                        .addProperties(PropertyConfigProto.newBuilder()
+                                .setPropertyName("subject")
+                                .setDataType(PropertyConfigProto.DataType.Code.STRING)
+                                .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
+                                .setIndexingConfig(
+                                        IndexingConfig.newBuilder()
+                                                .setTokenizerType(
+                                                        IndexingConfig.TokenizerType.Code.PLAIN)
+                                                .setTermMatchType(TermMatchType.Code.PREFIX)
+                                                .build()
+                                ).build()
+                        ).addProperties(PropertyConfigProto.newBuilder()
+                                .setPropertyName("link")
+                                .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT)
+                                .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
+                                .setSchemaType("RefType")
+                                .build()
+                        ).build()
+                ).build();
+
+        SchemaProto expectedSchema = SchemaProto.newBuilder()
+                .addTypes(SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("com.android.server.appsearch.impl@42:TestType")
+                        .addProperties(PropertyConfigProto.newBuilder()
+                                .setPropertyName("subject")
+                                .setDataType(PropertyConfigProto.DataType.Code.STRING)
+                                .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
+                                .setIndexingConfig(
+                                        IndexingConfig.newBuilder()
+                                                .setTokenizerType(
+                                                        IndexingConfig.TokenizerType.Code.PLAIN)
+                                                .setTermMatchType(TermMatchType.Code.PREFIX)
+                                                .build()
+                                ).build()
+                        ).addProperties(PropertyConfigProto.newBuilder()
+                                .setPropertyName("link")
+                                .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT)
+                                .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
+                                .setSchemaType("com.android.server.appsearch.impl@42:RefType")
+                                .build()
+                        ).build()
+                ).build();
+
+        AppSearchImpl impl = new AppSearchImpl(mContext, mUserId);
+        SchemaProto.Builder actualSchema = inSchema.toBuilder();
+        impl.rewriteSchemaTypes("com.android.server.appsearch.impl@42:", actualSchema);
+
+        assertThat(actualSchema.build()).isEqualTo(expectedSchema);
+    }
+
+    @Test
+    public void testPackageNotFound() {
+        AppSearchImpl impl = new AppSearchImpl(mContext, mUserId);
+        IllegalStateException e = expectThrows(
+                IllegalStateException.class,
+                () -> impl.setSchema(
+                        /*callingUid=*/Integer.MAX_VALUE, SchemaProto.getDefaultInstance()));
+        assertThat(e).hasMessageThat().contains("Failed to look up package name");
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index 5f1f308..46b8371 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -15,6 +15,10 @@
  */
 package com.android.server.devicepolicy;
 
+import static android.os.UserHandle.USER_SYSTEM;
+
+import static com.android.server.devicepolicy.DpmTestUtils.writeInputStreamToFile;
+
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
@@ -22,12 +26,14 @@
 import static org.mockito.Mockito.when;
 
 import android.app.admin.DevicePolicyManagerInternal;
+import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 
+import com.android.frameworks.servicestests.R;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.devicepolicy.DevicePolicyManagerServiceTestable.OwnersTestable;
@@ -37,9 +43,13 @@
 import java.util.Map;
 import java.util.Set;
 
+// TODO (b/143516163): Fix old test cases and put into presubmit.
 public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
 
     private static final String USER_TYPE_EMPTY = "";
+    private static final int COPE_ADMIN1_APP_ID = 123;
+    private static final int COPE_ANOTHER_ADMIN_APP_ID = 125;
+    private static final int COPE_PROFILE_USER_ID = 11;
 
     private DpmMockContext mContext;
 
@@ -85,7 +95,7 @@
 
         // Set up UserManager
         when(getServices().userManagerInternal.getBaseUserRestrictions(
-                eq(UserHandle.USER_SYSTEM))).thenReturn(DpmTestUtils.newRestrictions(
+                eq(USER_SYSTEM))).thenReturn(DpmTestUtils.newRestrictions(
                 UserManager.DISALLOW_ADD_USER,
                 UserManager.DISALLOW_RECORD_AUDIO));
 
@@ -137,7 +147,7 @@
         }
 
         assertTrue(dpms.mOwners.hasDeviceOwner());
-        assertFalse(dpms.mOwners.hasProfileOwner(UserHandle.USER_SYSTEM));
+        assertFalse(dpms.mOwners.hasProfileOwner(USER_SYSTEM));
         assertTrue(dpms.mOwners.hasProfileOwner(10));
         assertTrue(dpms.mOwners.hasProfileOwner(11));
         assertFalse(dpms.mOwners.hasProfileOwner(12));
@@ -145,7 +155,7 @@
         // Now all information should be migrated.
         assertFalse(dpms.mOwners.getDeviceOwnerUserRestrictionsNeedsMigration());
         assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(
-                UserHandle.USER_SYSTEM));
+                USER_SYSTEM));
         assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(10));
         assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(11));
         assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(12));
@@ -155,7 +165,7 @@
                 DpmTestUtils.newRestrictions(
                         UserManager.DISALLOW_RECORD_AUDIO
                 ),
-                newBaseRestrictions.get(UserHandle.USER_SYSTEM));
+                newBaseRestrictions.get(USER_SYSTEM));
 
         DpmTestUtils.assertRestrictions(
                 DpmTestUtils.newRestrictions(
@@ -214,7 +224,7 @@
 
         // Set up UserManager
         when(getServices().userManagerInternal.getBaseUserRestrictions(
-                eq(UserHandle.USER_SYSTEM))).thenReturn(DpmTestUtils.newRestrictions(
+                eq(USER_SYSTEM))).thenReturn(DpmTestUtils.newRestrictions(
                 UserManager.DISALLOW_ADD_USER,
                 UserManager.DISALLOW_RECORD_AUDIO,
                 UserManager.DISALLOW_SMS,
@@ -249,19 +259,19 @@
             mContext.binder.restoreCallingIdentity(ident);
         }
         assertFalse(dpms.mOwners.hasDeviceOwner());
-        assertTrue(dpms.mOwners.hasProfileOwner(UserHandle.USER_SYSTEM));
+        assertTrue(dpms.mOwners.hasProfileOwner(USER_SYSTEM));
 
         // Now all information should be migrated.
         assertFalse(dpms.mOwners.getDeviceOwnerUserRestrictionsNeedsMigration());
         assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(
-                UserHandle.USER_SYSTEM));
+                USER_SYSTEM));
 
         // Check the new base restrictions.
         DpmTestUtils.assertRestrictions(
                 DpmTestUtils.newRestrictions(
                         UserManager.DISALLOW_RECORD_AUDIO
                 ),
-                newBaseRestrictions.get(UserHandle.USER_SYSTEM));
+                newBaseRestrictions.get(USER_SYSTEM));
 
         // Check the new owner restrictions.
         DpmTestUtils.assertRestrictions(
@@ -270,7 +280,7 @@
                         UserManager.DISALLOW_SMS,
                         UserManager.DISALLOW_OUTGOING_CALLS
                 ),
-                dpms.getProfileOwnerAdminLocked(UserHandle.USER_SYSTEM).ensureUserRestrictions());
+                dpms.getProfileOwnerAdminLocked(USER_SYSTEM).ensureUserRestrictions());
     }
 
     // Test setting default restrictions for managed profile.
@@ -332,4 +342,92 @@
         assertEquals(alreadySet.size(), 1);
         assertTrue(alreadySet.contains(UserManager.DISALLOW_BLUETOOTH_SHARING));
     }
+
+    public void testCompMigrationUnAffiliated_skipped() throws Exception {
+        prepareAdmin1AsDo();
+        prepareAdminAnotherPackageAsPo(COPE_PROFILE_USER_ID);
+
+        final DevicePolicyManagerServiceTestable dpms;
+        dpms = bootDpmsUp();
+
+        // DO should still be DO since no migration should happen.
+        assertTrue(dpms.mOwners.hasDeviceOwner());
+    }
+
+    public void testCompMigrationAffiliated() throws Exception {
+        prepareAdmin1AsDo();
+        prepareAdmin1AsPo(COPE_PROFILE_USER_ID);
+
+        // Secure lock screen is needed for password policy APIs to work.
+        when(getServices().lockPatternUtils.hasSecureLockScreen()).thenReturn(true);
+
+        final DevicePolicyManagerServiceTestable dpms;
+        dpms = bootDpmsUp();
+
+        // DO should cease to be DO.
+        assertFalse(dpms.mOwners.hasDeviceOwner());
+
+        final DpmMockContext poContext = new DpmMockContext(getServices(), mRealTestContext);
+        poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID);
+
+        runAsCaller(poContext, dpms, dpm -> {
+            // Check that DO policy is now set on parent instance.
+            assertEquals(33, dpm.getParentProfileInstance(admin1).getPasswordHistoryLength(admin1));
+            // And NOT set on profile instance.
+            assertEquals(0, dpm.getPasswordHistoryLength(admin1));
+
+            // TODO(b/143516163): verify more policies.
+        });
+    }
+
+    private DevicePolicyManagerServiceTestable bootDpmsUp() {
+        DevicePolicyManagerServiceTestable dpms;
+        final long ident = mContext.binder.clearCallingIdentity();
+        try {
+            LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+
+            dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
+
+            dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY);
+            dpms.systemReady(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+            dpms.systemReady(SystemService.PHASE_BOOT_COMPLETED);
+        } finally {
+            mContext.binder.restoreCallingIdentity(ident);
+        }
+        return dpms;
+    }
+
+    private void prepareAdmin1AsDo() throws Exception {
+        setUpPackageManagerForAdmin(admin1, UserHandle.getUid(USER_SYSTEM, COPE_ADMIN1_APP_ID));
+        final int xmlResource = R.raw.comp_policies_primary;
+        writeInputStreamToFile(getRawStream(xmlResource),
+                (new File(getServices().systemUserDataDir, "device_policies.xml"))
+                        .getAbsoluteFile());
+        writeInputStreamToFile(getRawStream(R.raw.comp_device_owner),
+                (new File(getServices().dataDir, "device_owner_2.xml"))
+                        .getAbsoluteFile());
+    }
+
+    private void prepareAdmin1AsPo(int profileUserId) throws Exception {
+        preparePo(profileUserId, admin1, R.raw.comp_profile_owner_same_package,
+                R.raw.comp_policies_profile_same_package, COPE_ADMIN1_APP_ID);
+    }
+
+    private void prepareAdminAnotherPackageAsPo(int profileUserId) throws Exception {
+        preparePo(profileUserId, adminAnotherPackage, R.raw.comp_profile_owner_another_package,
+                R.raw.comp_policies_profile_another_package, COPE_ANOTHER_ADMIN_APP_ID);
+    }
+
+    private void preparePo(int profileUserId, ComponentName admin, int profileOwnerXmlResId,
+            int policyXmlResId, int adminAppId) throws Exception {
+        final File profileDir = getServices().addUser(profileUserId, 0,
+                UserManager.USER_TYPE_PROFILE_MANAGED, USER_SYSTEM /* profile group */);
+        setUpPackageManagerForFakeAdmin(
+                admin, UserHandle.getUid(profileUserId, adminAppId), admin1);
+        writeInputStreamToFile(getRawStream(policyXmlResId),
+                (new File(profileDir, "device_policies.xml")).getAbsoluteFile());
+        writeInputStreamToFile(getRawStream(profileOwnerXmlResId),
+                (new File(profileDir, "profile_owner.xml")).getAbsoluteFile());
+    }
+
 }
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 2cb7103..bfadeea 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -58,7 +58,6 @@
 import static org.testng.Assert.assertThrows;
 
 import android.Manifest.permission;
-import android.annotation.RawRes;
 import android.app.Activity;
 import android.app.AppOpsManager;
 import android.app.Notification;
@@ -112,7 +111,6 @@
 import org.mockito.stubbing.Answer;
 
 import java.io.File;
-import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -5810,10 +5808,6 @@
         return new File(parentDir, "device_policies.xml");
     }
 
-    private InputStream getRawStream(@RawRes int id) {
-        return mRealTestContext.getResources().openRawResource(id);
-    }
-
     private void setUserSetupCompleteForUser(boolean isUserSetupComplete, int userhandle) {
         when(getServices().settings.settingsSecureGetIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0,
                 userhandle)).thenReturn(isUserSetupComplete ? 1 : 0);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index a34c2ff..9a1a5fb 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -24,6 +24,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.when;
 
+import android.annotation.RawRes;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -36,6 +37,7 @@
 import android.os.UserHandle;
 import android.test.AndroidTestCase;
 
+import java.io.InputStream;
 import java.util.List;
 
 public abstract class DpmTestBase extends AndroidTestCase {
@@ -256,4 +258,8 @@
                 invocation -> invocation.getArguments()[1]
         );
     }
+
+    protected InputStream getRawStream(@RawRes int id) {
+        return mRealTestContext.getResources().openRawResource(id);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 6c2c144..068daf5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.devicepolicy;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -236,6 +237,13 @@
                     return ui == null ? null : getUserInfo(ui.profileGroupId);
                 }
         );
+        when(userManager.getProfileParent(any(UserHandle.class))).thenAnswer(
+                invocation -> {
+                    final UserHandle userHandle = (UserHandle) invocation.getArguments()[0];
+                    final UserInfo ui = getUserInfo(userHandle.getIdentifier());
+                    return ui == null ? UserHandle.USER_NULL : UserHandle.of(ui.profileGroupId);
+                }
+        );
         when(userManager.getProfiles(anyInt())).thenAnswer(
                 invocation -> {
                     final int userId12 = (int) invocation.getArguments()[0];
diff --git a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
index 5aed194..a1810b9 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.integrity;
 
+import static com.android.server.integrity.model.IndexingFileConstants.INDEXING_BLOCK_SIZE;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.integrity.AppInstallMetadata;
@@ -135,14 +137,15 @@
                 Arrays.asList(packageNameRule, packageCertRule, versionCodeRule, randomRule);
         mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules);
 
-        AppInstallMetadata appInstallMetadata = new AppInstallMetadata.Builder()
-                .setPackageName(packageName)
-                .setAppCertificate(packageCert)
-                .setVersionCode(version)
-                .setInstallerName("abc")
-                .setInstallerCertificate("abc")
-                .setIsPreInstalled(true)
-                .build();
+        AppInstallMetadata appInstallMetadata =
+                new AppInstallMetadata.Builder()
+                        .setPackageName(packageName)
+                        .setAppCertificate(packageCert)
+                        .setVersionCode(version)
+                        .setInstallerName("abc")
+                        .setInstallerCertificate("abc")
+                        .setIsPreInstalled(true)
+                        .build();
         List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata);
 
         assertThat(rulesFetched)
@@ -158,13 +161,14 @@
         // Create a rule set with 2500 package name indexed, 2500 app certificate indexed and
         // 500 unindexed rules.
         List<Rule> rules = new ArrayList<>();
+        int unindexedRuleCount = 70;
 
         for (int i = 0; i < 2500; i++) {
             rules.add(getPackageNameIndexedRule(String.format("%s%04d", packageName, i)));
             rules.add(getAppCertificateIndexedRule(String.format("%s%04d", appCertificate, i)));
         }
 
-        for (int i = 0; i < 70; i++) {
+        for (int i = 0; i < unindexedRuleCount; i++) {
             rules.add(getInstallerCertificateRule(String.format("%s%04d", installerName, i)));
         }
 
@@ -174,18 +178,20 @@
         // Read the rules for a specific rule.
         String installedPackageName = String.format("%s%04d", packageName, 264);
         String installedAppCertificate = String.format("%s%04d", appCertificate, 1264);
-        AppInstallMetadata appInstallMetadata = new AppInstallMetadata.Builder()
-                .setPackageName(installedPackageName)
-                .setAppCertificate(installedAppCertificate)
-                .setVersionCode(250)
-                .setInstallerName("abc")
-                .setInstallerCertificate("abc")
-                .setIsPreInstalled(true)
-                .build();
+        AppInstallMetadata appInstallMetadata =
+                new AppInstallMetadata.Builder()
+                        .setPackageName(installedPackageName)
+                        .setAppCertificate(installedAppCertificate)
+                        .setVersionCode(250)
+                        .setInstallerName("abc")
+                        .setInstallerCertificate("abc")
+                        .setIsPreInstalled(true)
+                        .build();
         List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata);
 
         // Verify that we do not load all the rules and we have the necessary rules to evaluate.
-        assertThat(rulesFetched.size()).isEqualTo(270);
+        assertThat(rulesFetched.size())
+                .isEqualTo(INDEXING_BLOCK_SIZE * 2 + unindexedRuleCount);
         assertThat(rulesFetched)
                 .containsAllOf(
                         getPackageNameIndexedRule(installedPackageName),
@@ -195,27 +201,38 @@
     private Rule getPackageNameIndexedRule(String packageName) {
         return new Rule(
                 new StringAtomicFormula(
-                        AtomicFormula.PACKAGE_NAME,
-                        packageName,
-                        /* isHashedValue= */ false),
+                        AtomicFormula.PACKAGE_NAME, packageName, /* isHashedValue= */ false),
                 Rule.DENY);
     }
 
     private Rule getAppCertificateIndexedRule(String appCertificate) {
         return new Rule(
                 new StringAtomicFormula(
-                        AtomicFormula.APP_CERTIFICATE,
-                        appCertificate,
-                        /* isHashedValue= */ false),
+                        AtomicFormula.APP_CERTIFICATE, appCertificate, /* isHashedValue= */ false),
                 Rule.DENY);
     }
 
     private Rule getInstallerCertificateRule(String installerCert) {
         return new Rule(
                 new StringAtomicFormula(
-                        AtomicFormula.INSTALLER_NAME,
-                        installerCert,
-                        /* isHashedValue= */ false),
+                        AtomicFormula.INSTALLER_NAME, installerCert, /* isHashedValue= */ false),
                 Rule.DENY);
     }
+
+    @Test
+    public void testStagingDirectoryCleared() throws Exception {
+        // We must push rules two times to ensure that staging directory is empty because we cleared
+        // it, rather than because original rules directory is empty.
+        mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST);
+        mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST);
+
+        assertStagingDirectoryCleared();
+    }
+
+    private void assertStagingDirectoryCleared() {
+        File stagingDir = new File(mTmpDir, "integrity_staging");
+        assertThat(stagingDir.exists()).isTrue();
+        assertThat(stagingDir.isDirectory()).isTrue();
+        assertThat(stagingDir.listFiles()).isEmpty();
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/stats/IonMemoryUtilTest.java b/services/tests/servicestests/src/com/android/server/stats/pull/IonMemoryUtilTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/stats/IonMemoryUtilTest.java
rename to services/tests/servicestests/src/com/android/server/stats/pull/IonMemoryUtilTest.java
index 8cbf8e5..d4d4b4d 100644
--- a/services/tests/servicestests/src/com/android/server/stats/IonMemoryUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/stats/pull/IonMemoryUtilTest.java
@@ -13,16 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.server.stats;
+package com.android.server.stats.pull;
 
-import static com.android.server.stats.IonMemoryUtil.parseIonHeapSizeFromDebugfs;
-import static com.android.server.stats.IonMemoryUtil.parseProcessIonHeapSizesFromDebugfs;
+import static com.android.server.stats.pull.IonMemoryUtil.parseIonHeapSizeFromDebugfs;
+import static com.android.server.stats.pull.IonMemoryUtil.parseProcessIonHeapSizesFromDebugfs;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.server.stats.IonMemoryUtil.IonAllocations;
+import com.android.server.stats.pull.IonMemoryUtil.IonAllocations;
 
 import org.junit.Test;
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 7ac45f0..6f16574 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -227,6 +227,7 @@
         assertEquals(expected.getLightColor(), actual.getLightColor());
         assertEquals(expected.getParentChannelId(), actual.getParentChannelId());
         assertEquals(expected.getConversationId(), actual.getConversationId());
+        assertEquals(expected.isDemoted(), actual.isDemoted());
     }
 
     private void compareChannelsParentChild(NotificationChannel parent,
@@ -355,6 +356,7 @@
         channel2.setVibrationPattern(new long[]{100, 67, 145, 156});
         channel2.setLightColor(Color.BLUE);
         channel2.setConversationId("id1", "conversation");
+        channel2.setDemoted(true);
 
         mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
         mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index a3e94599..ad63d07 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
@@ -56,6 +58,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -1175,4 +1178,160 @@
 
         verify(mActivity).removeFromHistory(anyString());
     }
+
+    @Test
+    public void testActivityOverridesProcessConfig() {
+        final WindowProcessController wpc = mActivity.app;
+        assertTrue(wpc.registeredForActivityConfigChanges());
+        assertFalse(wpc.registeredForDisplayConfigChanges());
+
+        final ActivityRecord secondaryDisplayActivity =
+                createActivityOnDisplay(false /* defaultDisplay */, null /* process */);
+
+        assertTrue(wpc.registeredForActivityConfigChanges());
+        assertEquals(0, mActivity.getMergedOverrideConfiguration()
+                .diff(wpc.getRequestedOverrideConfiguration()));
+        assertNotEquals(mActivity.getConfiguration(),
+                secondaryDisplayActivity.getConfiguration());
+    }
+
+    @Test
+    public void testActivityOverridesProcessConfig_TwoActivities() {
+        final WindowProcessController wpc = mActivity.app;
+        assertTrue(wpc.registeredForActivityConfigChanges());
+
+        final Task firstTaskRecord = mActivity.getTask();
+        final ActivityRecord secondActivityRecord =
+                new ActivityBuilder(mService).setTask(firstTaskRecord).setUseProcess(wpc).build();
+
+        assertTrue(wpc.registeredForActivityConfigChanges());
+        assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration()
+                .diff(wpc.getRequestedOverrideConfiguration()));
+    }
+
+    @Test
+    public void testActivityOverridesProcessConfig_TwoActivities_SecondaryDisplay() {
+        final WindowProcessController wpc = mActivity.app;
+        assertTrue(wpc.registeredForActivityConfigChanges());
+
+        final ActivityRecord secondActivityRecord =
+                new ActivityBuilder(mService).setTask(mTask).setUseProcess(wpc).build();
+
+        assertTrue(wpc.registeredForActivityConfigChanges());
+        assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration()
+                .diff(wpc.getRequestedOverrideConfiguration()));
+    }
+
+    @Test
+    public void testActivityOverridesProcessConfig_TwoActivities_DifferentTasks() {
+        final WindowProcessController wpc = mActivity.app;
+        assertTrue(wpc.registeredForActivityConfigChanges());
+
+        final ActivityRecord secondActivityRecord =
+                createActivityOnDisplay(true /* defaultDisplay */, wpc);
+
+        assertTrue(wpc.registeredForActivityConfigChanges());
+        assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration()
+                .diff(wpc.getRequestedOverrideConfiguration()));
+    }
+
+    @Test
+    public void testActivityOnDifferentDisplayUpdatesProcessOverride() {
+        final ActivityRecord secondaryDisplayActivity =
+                createActivityOnDisplay(false /* defaultDisplay */, null /* process */);
+        final WindowProcessController wpc = secondaryDisplayActivity.app;
+        assertTrue(wpc.registeredForActivityConfigChanges());
+
+        final ActivityRecord secondActivityRecord =
+                createActivityOnDisplay(true /* defaultDisplay */, wpc);
+
+        assertTrue(wpc.registeredForActivityConfigChanges());
+        assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration()
+                .diff(wpc.getRequestedOverrideConfiguration()));
+        assertFalse(wpc.registeredForDisplayConfigChanges());
+    }
+
+    @Test
+    public void testActivityReparentChangesProcessOverride() {
+        final WindowProcessController wpc = mActivity.app;
+        final Task initialTask = mActivity.getTask();
+        final Configuration initialConf =
+                new Configuration(mActivity.getMergedOverrideConfiguration());
+        assertEquals(0, mActivity.getMergedOverrideConfiguration()
+                .diff(wpc.getRequestedOverrideConfiguration()));
+        assertTrue(wpc.registeredForActivityConfigChanges());
+
+        // Create a new task with custom config to reparent the activity to.
+        final Task newTask =
+                new TaskBuilder(mSupervisor).setStack(initialTask.getStack()).build();
+        final Configuration newConfig = newTask.getConfiguration();
+        newConfig.densityDpi += 100;
+        newTask.onRequestedOverrideConfigurationChanged(newConfig);
+        assertEquals(newTask.getConfiguration().densityDpi, newConfig.densityDpi);
+
+        // Reparent the activity and verify that config override changed.
+        mActivity.reparent(newTask, 0 /* top */, "test");
+        assertEquals(mActivity.getConfiguration().densityDpi, newConfig.densityDpi);
+        assertEquals(mActivity.getMergedOverrideConfiguration().densityDpi, newConfig.densityDpi);
+
+        assertTrue(wpc.registeredForActivityConfigChanges());
+        assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration());
+        assertEquals(0, mActivity.getMergedOverrideConfiguration()
+                .diff(wpc.getRequestedOverrideConfiguration()));
+    }
+
+    @Test
+    public void testActivityReparentDoesntClearProcessOverride_TwoActivities() {
+        final WindowProcessController wpc = mActivity.app;
+        final Configuration initialConf =
+                new Configuration(mActivity.getMergedOverrideConfiguration());
+        final Task initialTask = mActivity.getTask();
+        final ActivityRecord secondActivity = new ActivityBuilder(mService).setTask(initialTask)
+                .setUseProcess(wpc).build();
+
+        assertTrue(wpc.registeredForActivityConfigChanges());
+        assertEquals(0, secondActivity.getMergedOverrideConfiguration()
+                .diff(wpc.getRequestedOverrideConfiguration()));
+
+        // Create a new task with custom config to reparent the second activity to.
+        final Task newTask =
+                new TaskBuilder(mSupervisor).setStack(initialTask.getStack()).build();
+        final Configuration newConfig = newTask.getConfiguration();
+        newConfig.densityDpi += 100;
+        newTask.onRequestedOverrideConfigurationChanged(newConfig);
+
+        // Reparent the activity and verify that config override changed.
+        secondActivity.reparent(newTask, 0 /* top */, "test");
+
+        assertTrue(wpc.registeredForActivityConfigChanges());
+        assertEquals(0, secondActivity.getMergedOverrideConfiguration()
+                .diff(wpc.getRequestedOverrideConfiguration()));
+        assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration());
+
+        // Reparent the first activity and verify that config override didn't change.
+        mActivity.reparent(newTask, 1 /* top */, "test");
+        assertTrue(wpc.registeredForActivityConfigChanges());
+        assertEquals(0, secondActivity.getMergedOverrideConfiguration()
+                .diff(wpc.getRequestedOverrideConfiguration()));
+        assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration());
+    }
+
+    /**
+     * Creates an activity on display. For non-default display request it will also create a new
+     * display with custom DisplayInfo.
+     */
+    private ActivityRecord createActivityOnDisplay(boolean defaultDisplay,
+            WindowProcessController process) {
+        final DisplayContent display;
+        if (defaultDisplay) {
+            display = mRootWindowContainer.getDefaultDisplay();
+        } else {
+            display = new TestDisplayContent.Builder(mService, 2000, 1000).setDensityDpi(300)
+                    .setPosition(DisplayContent.POSITION_TOP).build();
+        }
+        final ActivityStack stack = display.createStack(WINDOWING_MODE_UNDEFINED,
+                ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final Task task = new TaskBuilder(mSupervisor).setStack(stack).build();
+        return new ActivityBuilder(mService).setTask(task).setUseProcess(process).build();
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 0f22724..4beede9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -111,6 +111,7 @@
         private int mConfigChanges;
         private int mLaunchedFromPid;
         private int mLaunchedFromUid;
+        private WindowProcessController mWpc;
 
         ActivityBuilder(ActivityTaskManagerService service) {
             mService = service;
@@ -201,6 +202,11 @@
             return this;
         }
 
+        ActivityBuilder setUseProcess(WindowProcessController wpc) {
+            mWpc = wpc;
+            return this;
+        }
+
         ActivityRecord build() {
             try {
                 mService.deferWindowLayout();
@@ -263,10 +269,16 @@
                 activity.setVisible(true);
             }
 
-            final WindowProcessController wpc = new WindowProcessController(mService,
-                    mService.mContext.getApplicationInfo(), mProcessName, mUid,
-                    UserHandle.getUserId(12345), mock(Object.class),
-                    mock(WindowProcessListener.class));
+            final WindowProcessController wpc;
+            if (mWpc != null) {
+                wpc = mWpc;
+            } else {
+                wpc = new WindowProcessController(mService,
+                        mService.mContext.getApplicationInfo(), mProcessName, mUid,
+                        UserHandle.getUserId(12345), mock(Object.class),
+                        mock(WindowProcessListener.class));
+                wpc.setThread(mock(IApplicationThread.class));
+            }
             wpc.setThread(mock(IApplicationThread.class));
             activity.setProcess(wpc);
             doReturn(wpc).when(mService).getProcessController(
diff --git a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
new file mode 100644
index 0000000..032edde
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 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.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * This file tests WM setting the priority on windows that is used in SF to determine at what
+ * frame rate the Display should run. Any changes to the algorithm should be reflected in these
+ * tests.
+ *
+ * Build/Install/Run: atest FrameRateSelectionPriority
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class FrameRateSelectionPriorityTests extends WindowTestsBase {
+
+    @Test
+    public void basicTest() {
+        final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
+        assertNotNull("Window state is created", appWindow);
+        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+
+        appWindow.updateFrameRateSelectionPriorityIfNeeded();
+        // Priority doesn't change.
+        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+
+        // Call the function a few times.
+        appWindow.updateFrameRateSelectionPriorityIfNeeded();
+        appWindow.updateFrameRateSelectionPriorityIfNeeded();
+
+        // Since nothing changed in the priority state, the transaction should not be updating.
+        verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
+                appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+    }
+
+    @Test
+    public void testApplicationInFocusWithoutModeId() {
+        final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
+        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+        assertEquals(appWindow.getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
+                .getPreferredModeId(appWindow), 0);
+
+        appWindow.updateFrameRateSelectionPriorityIfNeeded();
+        // Priority stays MAX_VALUE.
+        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+        verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
+                appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+
+        // Application is in focus.
+        appWindow.mToken.mDisplayContent.mCurrentFocus = appWindow;
+        appWindow.updateFrameRateSelectionPriorityIfNeeded();
+        // Priority changes to 1.
+        assertEquals(appWindow.mFrameRateSelectionPriority, 1);
+        verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
+                appWindow.getSurfaceControl(), 1);
+    }
+
+    @Test
+    public void testApplicationInFocusWithModeId() {
+        final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
+        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+
+        // Application is in focus.
+        appWindow.mToken.mDisplayContent.mCurrentFocus = appWindow;
+        appWindow.updateFrameRateSelectionPriorityIfNeeded();
+        // Priority changes.
+        assertEquals(appWindow.mFrameRateSelectionPriority, 1);
+        // Update the mode ID to a requested number.
+        appWindow.mAttrs.preferredDisplayModeId = 1;
+        appWindow.updateFrameRateSelectionPriorityIfNeeded();
+        // Priority changes.
+        assertEquals(appWindow.mFrameRateSelectionPriority, 0);
+
+        // Remove the mode ID request.
+        appWindow.mAttrs.preferredDisplayModeId = 0;
+        appWindow.updateFrameRateSelectionPriorityIfNeeded();
+        // Priority changes.
+        assertEquals(appWindow.mFrameRateSelectionPriority, 1);
+
+        // Verify we called actions on Transactions correctly.
+        verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
+                appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+        verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
+                appWindow.getSurfaceControl(), 0);
+        verify(appWindow.getPendingTransaction(), times(2)).setFrameRateSelectionPriority(
+                appWindow.getSurfaceControl(), 1);
+    }
+
+    @Test
+    public void testApplicationNotInFocusWithModeId() {
+        final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
+        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+
+        final WindowState inFocusWindow = createWindow(null, TYPE_APPLICATION, "inFocus");
+        appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow;
+
+        appWindow.updateFrameRateSelectionPriorityIfNeeded();
+        // The window is not in focus.
+        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+
+        // Update the mode ID to a requested number.
+        appWindow.mAttrs.preferredDisplayModeId = 1;
+        appWindow.updateFrameRateSelectionPriorityIfNeeded();
+        // Priority changes.
+        assertEquals(appWindow.mFrameRateSelectionPriority, 2);
+
+        verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
+                appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+        verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
+                appWindow.getSurfaceControl(), 2);
+    }
+
+    @Test
+    public void testApplicationNotInFocusWithoutModeId() {
+        final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
+        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+
+        final WindowState inFocusWindow = createWindow(null, TYPE_APPLICATION, "inFocus");
+        appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow;
+
+        appWindow.updateFrameRateSelectionPriorityIfNeeded();
+        // The window is not in focus.
+        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+
+        // Make sure that the mode ID is not set.
+        appWindow.mAttrs.preferredDisplayModeId = 0;
+        appWindow.updateFrameRateSelectionPriorityIfNeeded();
+        // Priority doesn't change.
+        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+
+        verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
+                appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 64db897..890e4ab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -391,6 +391,33 @@
         assertEquals(null, compatTokens.get(0));
     }
 
+    @Test
+    public void testShouldUseSizeCompatModeOnResizableTask() {
+        setUpApp(new TestDisplayContent.Builder(mService, 1000, 2500).build());
+
+        // Make the task root resizable.
+        mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
+
+        // Create a size compat activity on the same task.
+        final ActivityRecord activity = new ActivityBuilder(mService)
+                .setTask(mTask)
+                .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE)
+                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+                .build();
+        assertTrue(activity.shouldUseSizeCompatMode());
+
+        // The non-resizable activity should not be size compat because it is on a resizable task
+        // in multi-window mode.
+        mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+        assertFalse(activity.shouldUseSizeCompatMode());
+
+        // The non-resizable activity should not be size compat because the display support
+        // changing windowing mode from fullscreen to freeform.
+        mStack.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+        mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
+        assertFalse(activity.shouldUseSizeCompatMode());
+    }
+
     /**
      * Setup {@link #mActivity} as a size-compat-mode-able activity with fixed aspect and/or
      * orientation.
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index f5d08dc..eda1fb8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -250,4 +250,9 @@
         return this;
     }
 
+    @Override
+    public SurfaceControl.Transaction setFrameRateSelectionPriority(SurfaceControl sc,
+            int priority) {
+        return this;
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index 421a4582..db4fdc77 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.times;
 
 import android.content.pm.ApplicationInfo;
+import android.content.res.Configuration;
 import android.platform.test.annotations.Presubmit;
 
 import org.junit.Before;
@@ -62,33 +63,33 @@
 
         // Register to display 1 as a listener.
         TestDisplayContent testDisplayContent1 = createTestDisplayContentInContainer();
-        mWpc.registerDisplayConfigurationListenerLocked(testDisplayContent1);
+        mWpc.registerDisplayConfigurationListener(testDisplayContent1);
         assertTrue(testDisplayContent1.containsListener(mWpc));
         assertEquals(testDisplayContent1.mDisplayId, mWpc.getDisplayId());
 
         // Move to display 2.
         TestDisplayContent testDisplayContent2 = createTestDisplayContentInContainer();
-        mWpc.registerDisplayConfigurationListenerLocked(testDisplayContent2);
+        mWpc.registerDisplayConfigurationListener(testDisplayContent2);
         assertFalse(testDisplayContent1.containsListener(mWpc));
         assertTrue(testDisplayContent2.containsListener(mWpc));
         assertEquals(testDisplayContent2.mDisplayId, mWpc.getDisplayId());
 
         // Null DisplayContent will not change anything.
-        mWpc.registerDisplayConfigurationListenerLocked(null);
+        mWpc.registerDisplayConfigurationListener(null);
         assertTrue(testDisplayContent2.containsListener(mWpc));
         assertEquals(testDisplayContent2.mDisplayId, mWpc.getDisplayId());
 
         // Unregister listener will remove the wpc from registered displays.
-        mWpc.unregisterDisplayConfigurationListenerLocked();
+        mWpc.unregisterDisplayConfigurationListener();
         assertFalse(testDisplayContent1.containsListener(mWpc));
         assertFalse(testDisplayContent2.containsListener(mWpc));
         assertEquals(INVALID_DISPLAY, mWpc.getDisplayId());
 
         // Unregistration still work even if the display was removed.
-        mWpc.registerDisplayConfigurationListenerLocked(testDisplayContent1);
+        mWpc.registerDisplayConfigurationListener(testDisplayContent1);
         assertEquals(testDisplayContent1.mDisplayId, mWpc.getDisplayId());
         mRootWindowContainer.removeChild(testDisplayContent1);
-        mWpc.unregisterDisplayConfigurationListenerLocked();
+        mWpc.unregisterDisplayConfigurationListener();
         assertEquals(INVALID_DISPLAY, mWpc.getDisplayId());
     }
 
@@ -129,6 +130,24 @@
         orderVerifier.verifyNoMoreInteractions();
     }
 
+    @Test
+    public void testConfigurationForSecondaryScreen() {
+        final WindowProcessController wpc = new WindowProcessController(
+                mService, mock(ApplicationInfo.class), null, 0, -1, null, null);
+        // By default, the process should not listen to any display.
+        assertEquals(INVALID_DISPLAY, wpc.getDisplayId());
+
+        // Register to a new display as a listener.
+        final DisplayContent display = new TestDisplayContent.Builder(mService, 2000, 1000)
+                .setDensityDpi(300).setPosition(DisplayContent.POSITION_TOP).build();
+        wpc.registerDisplayConfigurationListener(display);
+
+        assertEquals(display.mDisplayId, wpc.getDisplayId());
+        final Configuration expectedConfig = mService.mRootWindowContainer.getConfiguration();
+        expectedConfig.updateFrom(display.getConfiguration());
+        assertEquals(expectedConfig, wpc.getConfiguration());
+    }
+
     private TestDisplayContent createTestDisplayContentInContainer() {
         return new TestDisplayContent.Builder(mService, 1000, 1500).build();
     }
diff --git a/services/usage/java/com/android/server/usage/StorageStatsManagerInternal.java b/services/usage/java/com/android/server/usage/StorageStatsManagerInternal.java
new file mode 100644
index 0000000..a532548
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/StorageStatsManagerInternal.java
@@ -0,0 +1,52 @@
+/*
+ * 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.usage;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.pm.PackageStats;
+
+/**
+ * StorageStatsManager local system service interface.
+ *
+ * Only for use within the system server.
+ */
+public abstract class StorageStatsManagerInternal {
+    /**
+     * Class used to augment {@link PackageStats} with the data stored by the system on
+     * behalf of apps in system specific directories
+     * ({@link android.os.Environment#getDataSystemDirectory},
+     * {@link android.os.Environment#getDataSystemCeDirectory}, etc).
+     */
+    public interface StorageStatsAugmenter {
+        void augmentStatsForPackage(@NonNull PackageStats stats,
+                @NonNull String packageName, @UserIdInt int userId,
+                @NonNull String callingPackage);
+        void augmentStatsForUid(@NonNull PackageStats stats, int uid,
+                @NonNull String callingPackage);
+        void augmentStatsForUser(@NonNull PackageStats stats, @UserIdInt int userId,
+                @NonNull String callingPackage);
+    }
+
+    /**
+     * Register a {@link StorageStatsAugmenter}.
+     *
+     * @param augmenter the {@link StorageStatsAugmenter} object to be registered.
+     * @param tag the identifier to be used for debugging in logs/trace.
+     */
+    public abstract void registerStorageStatsAugmenter(@NonNull StorageStatsAugmenter augmenter,
+            @NonNull String tag);
+}
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 531a931..18b640f 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.util.ArrayUtils.defeatNullable;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.usage.StorageStatsManagerInternal.StorageStatsAugmenter;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -46,6 +47,7 @@
 import android.os.ParcelableException;
 import android.os.StatFs;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.CrateInfo;
@@ -58,6 +60,7 @@
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.DataUnit;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseLongArray;
 
@@ -77,6 +80,8 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Consumer;
 
 public class StorageStatsService extends IStorageStatsManager.Stub {
     private static final String TAG = "StorageStatsService";
@@ -111,6 +116,9 @@
     private final Installer mInstaller;
     private final H mHandler;
 
+    private final CopyOnWriteArrayList<Pair<String, StorageStatsAugmenter>>
+            mStorageStatsAugmenters = new CopyOnWriteArrayList<>();
+
     public StorageStatsService(Context context) {
         mContext = Preconditions.checkNotNull(context);
         mAppOps = Preconditions.checkNotNull(context.getSystemService(AppOpsManager.class));
@@ -139,6 +147,8 @@
                 }
             }
         });
+
+        LocalServices.addService(StorageStatsManagerInternal.class, new LocalService());
     }
 
     private void invalidateMounts() {
@@ -300,6 +310,12 @@
             } catch (InstallerException e) {
                 throw new ParcelableException(new IOException(e.getMessage()));
             }
+            if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
+                forEachStorageStatsAugmenter((storageStatsAugmenter) -> {
+                    storageStatsAugmenter.augmentStatsForPackage(stats,
+                            packageName, userId, callingPackage);
+                }, "queryStatsForPackage");
+            }
             return translate(stats);
         }
     }
@@ -353,6 +369,12 @@
         } catch (InstallerException e) {
             throw new ParcelableException(new IOException(e.getMessage()));
         }
+
+        if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
+            forEachStorageStatsAugmenter((storageStatsAugmenter) -> {
+                storageStatsAugmenter.augmentStatsForUid(stats, uid, callingPackage);
+            }, "queryStatsForUid");
+        }
         return translate(stats);
     }
 
@@ -379,6 +401,12 @@
         } catch (InstallerException e) {
             throw new ParcelableException(new IOException(e.getMessage()));
         }
+
+        if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
+            forEachStorageStatsAugmenter((storageStatsAugmenter) -> {
+                storageStatsAugmenter.augmentStatsForUser(stats, userId, callingPackage);
+            }, "queryStatsForUser");
+        }
         return translate(stats);
     }
 
@@ -705,4 +733,29 @@
             throw new ParcelableException(new IOException(e.getMessage()));
         }
     }
+
+    void forEachStorageStatsAugmenter(@NonNull Consumer<StorageStatsAugmenter> consumer,
+                @NonNull String queryTag) {
+        for (int i = 0, count = mStorageStatsAugmenters.size(); i < count; ++i) {
+            final Pair<String, StorageStatsAugmenter> pair = mStorageStatsAugmenters.get(i);
+            final String augmenterTag = pair.first;
+            final StorageStatsAugmenter storageStatsAugmenter = pair.second;
+
+            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, queryTag + ":" + augmenterTag);
+            try {
+                consumer.accept(storageStatsAugmenter);
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+            }
+        }
+    }
+
+    private class LocalService extends StorageStatsManagerInternal {
+        @Override
+        public void registerStorageStatsAugmenter(
+                @NonNull StorageStatsAugmenter storageStatsAugmenter,
+                @NonNull String tag) {
+            mStorageStatsAugmenters.add(Pair.create(tag, storageStatsAugmenter));
+        }
+    }
 }
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 05d4bf3..b8b203d 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -558,7 +558,7 @@
         }
 
         if (DBG) {
-            Log.d(LOG_TAG, "No READ_PRIVILEGED_PHONE_STATE permission, "
+            Log.d(LOG_TAG, "No READ_PRIVILEDED_PHONE_STATE permission, "
                     + "check carrier privilege next.");
         }
 
@@ -566,33 +566,6 @@
     }
 
     /**
-     * Ensure the caller (or self, if not processing an IPC) has
-     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or
-     * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or carrier privileges.
-     *
-     * @throws SecurityException if the caller does not have the required permission/privileges
-     */
-    public static void enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
-            Context context, int subId, String message) {
-        if (context.checkCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-                == PERMISSION_GRANTED) {
-            return;
-        }
-
-        if (context.checkCallingOrSelfPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
-                == PERMISSION_GRANTED) {
-            return;
-        }
-
-        if (DBG) {
-            Log.d(LOG_TAG, "No READ_PRIVILEGED_PHONE_STATE nor READ_PRECISE_PHONE_STATE permission"
-                    + ", check carrier privilege next.");
-        }
-
-        enforceCallingOrSelfCarrierPrivilege(context, subId, message);
-    }
-
-    /**
      * Make sure the caller (or self, if not processing an IPC) has carrier privileges.
      *
      * @throws SecurityException if the caller does not have the required privileges
@@ -619,7 +592,7 @@
     private static boolean checkCarrierPrivilegeForAnySubId(Context context, int uid) {
         SubscriptionManager sm = (SubscriptionManager) context.getSystemService(
                 Context.TELEPHONY_SUBSCRIPTION_SERVICE);
-        int[] activeSubIds = sm.getActiveSubscriptionIdList(/* visibleOnly */ false);
+        int[] activeSubIds = sm.getActiveAndHiddenSubscriptionIdList();
         for (int activeSubId : activeSubIds) {
             if (getCarrierPrivilegeStatus(context, activeSubId, uid)
                     == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index 2abcc76..a7ad884 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -28,6 +28,8 @@
 import android.os.SystemProperties;
 
 import java.io.PrintWriter;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Supplier;
 
 /**
@@ -137,4 +139,12 @@
         }
         return ret;
     }
+
+    /** Wait for latch to trigger */
+    public static void waitUntilReady(CountDownLatch latch, long timeoutMs) {
+        try {
+            latch.await(timeoutMs, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException ignored) {
+        }
+    }
 }
diff --git a/telephony/framework-telephony-jarjar-rules.txt b/telephony/framework-telephony-jarjar-rules.txt
new file mode 100644
index 0000000..7cab806
--- /dev/null
+++ b/telephony/framework-telephony-jarjar-rules.txt
@@ -0,0 +1,4 @@
+rule android.telephony.Annotation* android.telephony.framework.Annotation@1
+rule com.android.i18n.phonenumbers.** com.android.telephony.framework.phonenumbers.@1
+#TODO: add jarjar rules for statically linked util classes
+
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 8ed4ee5..fee6d3f 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -25,11 +25,12 @@
 import android.annotation.SuppressAutoDoc;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.app.ActivityThread;
 import android.app.PendingIntent;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.database.CursorWindow;
 import android.net.Uri;
 import android.os.Build;
@@ -423,7 +424,7 @@
             String destinationAddress, String scAddress, String text,
             PendingIntent sentIntent, PendingIntent deliveryIntent) {
         sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
-                true /* persistMessage*/, ActivityThread.currentPackageName());
+                true /* persistMessage*/, null);
     }
 
     /**
@@ -633,7 +634,7 @@
             String destinationAddress, String scAddress, String text,
             PendingIntent sentIntent, PendingIntent deliveryIntent) {
         sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
-                false /* persistMessage */, ActivityThread.currentPackageName());
+                false /* persistMessage */, null);
     }
 
     private void sendTextMessageInternal(
@@ -676,7 +677,7 @@
                         ISms iSms = getISmsServiceOrThrow();
                         if (iSms != null) {
                             iSms.sendTextForSubscriberWithOptions(subId,
-                                    ActivityThread.currentPackageName(), destinationAddress,
+                                    null, destinationAddress,
                                     scAddress,
                                     text, sentIntent, deliveryIntent, persistMessage, finalPriority,
                                     expectMore, finalValidity);
@@ -698,7 +699,7 @@
                 ISms iSms = getISmsServiceOrThrow();
                 if (iSms != null) {
                     iSms.sendTextForSubscriberWithOptions(getSubscriptionId(),
-                            ActivityThread.currentPackageName(), destinationAddress,
+                            null, destinationAddress,
                             scAddress,
                             text, sentIntent, deliveryIntent, persistMessage, finalPriority,
                             expectMore, finalValidity);
@@ -920,7 +921,7 @@
             String destinationAddress, String scAddress, ArrayList<String> parts,
             ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
         sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
-                deliveryIntents, true /* persistMessage*/, ActivityThread.currentPackageName());
+                deliveryIntents, true /* persistMessage*/, null);
     }
 
     /**
@@ -937,8 +938,9 @@
      * subscription.
      * </p>
      *
-     * @param packageName serves as the default package name if
-     * {@link ActivityThread#currentPackageName()} is null.
+     * @param packageName serves as the default package name if the package name that is
+     *        associated with the user id is null.
+     *
      * @hide
      */
     @SystemApi
@@ -948,9 +950,7 @@
             @NonNull List<String> parts, @Nullable List<PendingIntent> sentIntents,
             @Nullable List<PendingIntent> deliveryIntents, @NonNull String packageName) {
         sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
-                deliveryIntents, true /* persistMessage*/,
-                ActivityThread.currentPackageName() == null
-                        ? packageName : ActivityThread.currentPackageName());
+                deliveryIntents, true /* persistMessage*/, packageName);
     }
 
     private void sendMultipartTextMessageInternal(
@@ -1051,7 +1051,7 @@
             String destinationAddress, String scAddress, List<String> parts,
             List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
         sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
-                deliveryIntents, false /* persistMessage*/, ActivityThread.currentPackageName());
+                deliveryIntents, false /* persistMessage*/, null);
     }
 
     /**
@@ -1213,7 +1213,7 @@
                             ISms iSms = getISmsServiceOrThrow();
                             if (iSms != null) {
                                 iSms.sendMultipartTextForSubscriberWithOptions(subId,
-                                        ActivityThread.currentPackageName(), destinationAddress,
+                                        null, destinationAddress,
                                         scAddress, parts, sentIntents, deliveryIntents,
                                         persistMessage, finalPriority, expectMore, finalValidity);
                             }
@@ -1235,7 +1235,7 @@
                     ISms iSms = getISmsServiceOrThrow();
                     if (iSms != null) {
                         iSms.sendMultipartTextForSubscriberWithOptions(getSubscriptionId(),
-                                ActivityThread.currentPackageName(), destinationAddress,
+                                null, destinationAddress,
                                 scAddress, parts, sentIntents, deliveryIntents,
                                 persistMessage, finalPriority, expectMore, finalValidity);
                     }
@@ -1366,7 +1366,7 @@
             public void onSuccess(int subId) {
                 try {
                     ISms iSms = getISmsServiceOrThrow();
-                    iSms.sendDataForSubscriber(subId, ActivityThread.currentPackageName(),
+                    iSms.sendDataForSubscriber(subId, null,
                             destinationAddress, scAddress, destinationPort & 0xFFFF, data,
                             sentIntent, deliveryIntent);
                 } catch (RemoteException e) {
@@ -1492,7 +1492,6 @@
     private void resolveSubscriptionForOperation(SubscriptionResolverResult resolverResult) {
         int subId = getSubscriptionId();
         boolean isSmsSimPickActivityNeeded = false;
-        final Context context = ActivityThread.currentApplication().getApplicationContext();
         try {
             ISms iSms = getISmsService();
             if (iSms != null) {
@@ -1514,14 +1513,14 @@
             return;
         }
         // We need to ask the user pick an appropriate subid for the operation.
-        Log.d(TAG, "resolveSubscriptionForOperation isSmsSimPickActivityNeeded is true for package "
-                + context.getPackageName());
+        Log.d(TAG, "resolveSubscriptionForOperation isSmsSimPickActivityNeeded is true for calling"
+                + " package. ");
         try {
             // Create the SMS pick activity and call back once the activity is complete. Can't do
             // it here because we do not have access to the activity context that is performing this
             // operation.
             // Requires that the calling process has the SEND_SMS permission.
-            getITelephony().enqueueSmsPickResult(context.getOpPackageName(),
+            getITelephony().enqueueSmsPickResult(null,
                     new IIntegerConsumer.Stub() {
                         @Override
                         public void accept(int subId) {
@@ -1539,6 +1538,13 @@
         }
     }
 
+    /**
+     * To check the SDK version for SmsManager.sendResolverResult method.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P)
+    private static final long GET_TARGET_SDK_VERSION_CODE_CHANGE = 145147528L;
+
     private void sendResolverResult(SubscriptionResolverResult resolverResult, int subId,
             boolean pickActivityShown) {
         if (SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -1546,7 +1552,8 @@
             return;
         }
 
-        if (getTargetSdkVersion() <= Build.VERSION_CODES.P && !pickActivityShown) {
+        if (!Compatibility.isChangeEnabled(GET_TARGET_SDK_VERSION_CODE_CHANGE)
+                && !pickActivityShown) {
             // Do not fail, return a success with an INVALID subid for apps targeting P or below
             // that tried to perform an operation and the SMS disambiguation dialog was never shown,
             // as these applications may not have been written to handle the failure case properly.
@@ -1559,19 +1566,6 @@
         }
     }
 
-    private static int getTargetSdkVersion() {
-        final Context context = ActivityThread.currentApplication().getApplicationContext();
-        int targetSdk;
-        try {
-            targetSdk = context.getPackageManager().getApplicationInfo(
-                    context.getOpPackageName(), 0).targetSdkVersion;
-        } catch (PackageManager.NameNotFoundException e) {
-            // Default to old behavior if we can not find this.
-            targetSdk = -1;
-        }
-        return targetSdk;
-    }
-
     private static ITelephony getITelephony() {
         ITelephony binder = ITelephony.Stub.asInterface(
                 TelephonyFrameworkInitializer
@@ -1657,7 +1651,7 @@
             ISms iSms = getISmsService();
             if (iSms != null) {
                 success = iSms.copyMessageToIccEfForSubscriber(getSubscriptionId(),
-                        ActivityThread.currentPackageName(),
+                        null,
                         status, pdu, smsc);
             }
         } catch (RemoteException ex) {
@@ -1698,7 +1692,7 @@
             ISms iSms = getISmsService();
             if (iSms != null) {
                 success = iSms.updateMessageOnIccEfForSubscriber(getSubscriptionId(),
-                        ActivityThread.currentPackageName(),
+                        null,
                         messageIndex, STATUS_ON_ICC_FREE, null /* pdu */);
             }
         } catch (RemoteException ex) {
@@ -1741,7 +1735,7 @@
             ISms iSms = getISmsService();
             if (iSms != null) {
                 success = iSms.updateMessageOnIccEfForSubscriber(getSubscriptionId(),
-                        ActivityThread.currentPackageName(),
+                        null,
                         messageIndex, newStatus, pdu);
             }
         } catch (RemoteException ex) {
@@ -1793,7 +1787,7 @@
             if (iSms != null) {
                 records = iSms.getAllMessagesFromIccEfForSubscriber(
                         getSubscriptionId(),
-                        ActivityThread.currentPackageName());
+                        null);
             }
         } catch (RemoteException ex) {
             // ignore it
@@ -2617,7 +2611,7 @@
         try {
             ISms iccSms = getISmsServiceOrThrow();
             return iccSms.createAppSpecificSmsToken(getSubscriptionId(),
-                    ActivityThread.currentPackageName(), intent);
+                    null, intent);
 
         } catch (RemoteException ex) {
             ex.rethrowFromSystemServer();
@@ -2737,7 +2731,7 @@
         try {
             ISms iccSms = getISmsServiceOrThrow();
             return iccSms.createAppSpecificSmsTokenWithPackageInfo(getSubscriptionId(),
-                    ActivityThread.currentPackageName(), prefixes, intent);
+                    null, prefixes, intent);
 
         } catch (RemoteException ex) {
             ex.rethrowFromSystemServer();
@@ -2828,7 +2822,7 @@
             ISms iccISms = getISmsServiceOrThrow();
             if (iccISms != null) {
                 return iccISms.checkSmsShortCodeDestination(getSubscriptionId(),
-                        ActivityThread.currentPackageName(), null, destAddress, countryIso);
+                        null, null, destAddress, countryIso);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "checkSmsShortCodeDestination() RemoteException", e);
@@ -2864,7 +2858,7 @@
             ISms iSms = getISmsService();
             if (iSms != null) {
                 smsc = iSms.getSmscAddressFromIccEfForSubscriber(
-                        getSubscriptionId(), ActivityThread.currentPackageName());
+                        getSubscriptionId(), null);
             }
         } catch (RemoteException ex) {
             // ignore it
@@ -2898,7 +2892,7 @@
             ISms iSms = getISmsService();
             if (iSms != null) {
                 return iSms.setSmscAddressOnIccEfForSubscriber(
-                        smsc, getSubscriptionId(), ActivityThread.currentPackageName());
+                        smsc, getSubscriptionId(), null);
             }
         } catch (RemoteException ex) {
             // ignore it
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 36f541f..b42ce35 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2232,6 +2232,7 @@
         } else {
             logd("putPhoneIdAndSubIdExtra: no valid subs");
             intent.putExtra(PhoneConstants.PHONE_KEY, phoneId);
+            intent.putExtra(EXTRA_SLOT_INDEX, phoneId);
         }
     }
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 13aad7e..56ca8c7 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -37,7 +37,6 @@
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.annotation.WorkerThread;
-import android.app.ActivityThread;
 import android.app.PendingIntent;
 import android.compat.Compatibility;
 import android.compat.annotation.ChangeId;
@@ -380,8 +379,17 @@
         // effort and get the context from the current activity thread.
         if (mContext != null) {
             return mContext.getOpPackageName();
+        } else {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) return null;
+            try {
+                return telephony.getCurrentPackageName();
+            } catch (RemoteException ex) {
+                return null;
+            } catch (NullPointerException ex) {
+                return null;
+            }
         }
-        return ActivityThread.currentOpPackageName();
     }
 
     private String getFeatureId() {
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 9b739d3..8c11df4 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -27,6 +27,7 @@
 import android.annotation.WorkerThread;
 import android.os.Binder;
 import android.os.RemoteException;
+import android.os.ServiceSpecificException;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyFrameworkInitializer;
@@ -383,6 +384,8 @@
         callback.setExecutor(executor);
         try {
             getITelephony().registerImsProvisioningChangedCallback(mSubId, callback.getBinder());
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
         } catch (RemoteException | IllegalStateException e) {
             throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index cdb95a8..a8e76b9 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2087,6 +2087,11 @@
     int getRadioHalVersion();
 
     /**
+     * Get the current calling package name.
+     */
+    String getCurrentPackageName();
+
+    /**
      * Returns true if the specified type of application (e.g. {@link #APPTYPE_CSIM} is present
      * on the UICC card.
      * @hide
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 656628eb..8cc8cf4 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -18,10 +18,13 @@
 
 import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
@@ -36,12 +39,14 @@
 import android.net.ConnectivityModuleConnector;
 import android.net.ConnectivityModuleConnector.ConnectivityModuleHealthListener;
 import android.os.Handler;
+import android.os.SystemProperties;
 import android.os.test.TestLooper;
 import android.provider.DeviceConfig;
 import android.util.AtomicFile;
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.server.PackageWatchdog.HealthCheckState;
 import com.android.server.PackageWatchdog.MonitoredPackage;
 import com.android.server.PackageWatchdog.PackageHealthObserver;
@@ -54,11 +59,15 @@
 import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
 
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
@@ -88,6 +97,8 @@
     private PackageManager mMockPackageManager;
     @Captor
     private ArgumentCaptor<ConnectivityModuleHealthListener> mConnectivityModuleCallbackCaptor;
+    private MockitoSession mSession;
+    private HashMap<String, String> mSystemSettingsMap;
 
     @Before
     public void setUp() throws Exception {
@@ -104,11 +115,47 @@
             res.setLongVersionCode(VERSION_CODE);
             return res;
         });
+        mSession = ExtendedMockito.mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.LENIENT)
+                .spyStatic(SystemProperties.class)
+                .startMocking();
+        mSystemSettingsMap = new HashMap<>();
+
+
+        // Mock SystemProperties setter and various getters
+        doAnswer((Answer<Void>) invocationOnMock -> {
+                    String key = invocationOnMock.getArgument(0);
+                    String value = invocationOnMock.getArgument(1);
+
+                    mSystemSettingsMap.put(key, value);
+                    return null;
+                }
+        ).when(() -> SystemProperties.set(anyString(), anyString()));
+
+        doAnswer((Answer<Integer>) invocationOnMock -> {
+                    String key = invocationOnMock.getArgument(0);
+                    int defaultValue = invocationOnMock.getArgument(1);
+
+                    String storedValue = mSystemSettingsMap.get(key);
+                    return storedValue == null ? defaultValue : Integer.parseInt(storedValue);
+                }
+        ).when(() -> SystemProperties.getInt(anyString(), anyInt()));
+
+        doAnswer((Answer<Long>) invocationOnMock -> {
+                    String key = invocationOnMock.getArgument(0);
+                    long defaultValue = invocationOnMock.getArgument(1);
+
+                    String storedValue = mSystemSettingsMap.get(key);
+                    return storedValue == null ? defaultValue : Long.parseLong(storedValue);
+                }
+        ).when(() -> SystemProperties.getLong(anyString(), anyLong()));
     }
 
     @After
     public void tearDown() throws Exception {
         dropShellPermissions();
+        mSession.finishMocking();
     }
 
     @Test
@@ -968,6 +1015,54 @@
         assertThat(persistentObserver.mHealthCheckFailedPackages).isEmpty();
     }
 
+
+    /** Ensure that boot loop mitigation is done when the number of boots meets the threshold. */
+    @Test
+    public void testBootLoopDetection_meetsThreshold() {
+        PackageWatchdog watchdog = createWatchdog();
+        TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
+        watchdog.registerHealthObserver(bootObserver);
+        for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
+            watchdog.noteBoot();
+        }
+        assertThat(bootObserver.mitigatedBootLoop()).isTrue();
+    }
+
+
+    /**
+     * Ensure that boot loop mitigation is not done when the number of boots does not meet the
+     * threshold.
+     */
+    @Test
+    public void testBootLoopDetection_doesNotMeetThreshold() {
+        PackageWatchdog watchdog = createWatchdog();
+        TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
+        watchdog.registerHealthObserver(bootObserver);
+        for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT - 1; i++) {
+            watchdog.noteBoot();
+        }
+        assertThat(bootObserver.mitigatedBootLoop()).isFalse();
+    }
+
+    /**
+     * Ensure that boot loop mitigation is done for the observer with the lowest user impact
+     */
+    @Test
+    public void testBootLoopMitigationDoneForLowestUserImpact() {
+        PackageWatchdog watchdog = createWatchdog();
+        TestObserver bootObserver1 = new TestObserver(OBSERVER_NAME_1);
+        bootObserver1.setImpact(PackageHealthObserverImpact.USER_IMPACT_LOW);
+        TestObserver bootObserver2 = new TestObserver(OBSERVER_NAME_2);
+        bootObserver2.setImpact(PackageHealthObserverImpact.USER_IMPACT_MEDIUM);
+        watchdog.registerHealthObserver(bootObserver1);
+        watchdog.registerHealthObserver(bootObserver2);
+        for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
+            watchdog.noteBoot();
+        }
+        assertThat(bootObserver1.mitigatedBootLoop()).isTrue();
+        assertThat(bootObserver2.mitigatedBootLoop()).isFalse();
+    }
+
     private void adoptShellPermissions(String... permissions) {
         InstrumentationRegistry
                 .getInstrumentation()
@@ -1046,6 +1141,7 @@
         private int mLastFailureReason;
         private boolean mIsPersistent = false;
         private boolean mMayObservePackages = false;
+        private boolean mMitigatedBootLoop = false;
         final List<String> mHealthCheckFailedPackages = new ArrayList<>();
         final List<String> mMitigatedPackages = new ArrayList<>();
 
@@ -1082,6 +1178,19 @@
             return mMayObservePackages;
         }
 
+        public int onBootLoop() {
+            return mImpact;
+        }
+
+        public boolean executeBootLoopMitigation() {
+            mMitigatedBootLoop = true;
+            return true;
+        }
+
+        public boolean mitigatedBootLoop() {
+            return mMitigatedBootLoop;
+        }
+
         public int getLastFailureReason() {
             return mLastFailureReason;
         }
@@ -1090,6 +1199,10 @@
             mIsPersistent = persistent;
         }
 
+        public void setImpact(int impact) {
+            mImpact = impact;
+        }
+
         public void setMayObservePackages(boolean mayObservePackages) {
             mMayObservePackages = mayObservePackages;
         }
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 091edd4..98e7b4e 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -19,7 +19,10 @@
     static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"],
     test_suites: ["general-tests"],
     test_config: "RollbackTest.xml",
-    java_resources: [":com.android.apex.apkrollback.test_v2"],
+    java_resources: [
+        ":com.android.apex.apkrollback.test_v2",
+        ":com.android.apex.apkrollback.test_v2Crashing"
+    ],
 }
 
 java_test_host {
@@ -79,4 +82,14 @@
   key: "com.android.apex.apkrollback.test.key",
   apps: ["TestAppAv2"],
   installable: false,
+}
+
+apex {
+  name: "com.android.apex.apkrollback.test_v2Crashing",
+  manifest: "testdata/manifest_v2.json",
+  androidManifest: "testdata/AndroidManifest.xml",
+  file_contexts: ":apex.test-file_contexts",
+  key: "com.android.apex.apkrollback.test.key",
+  apps: ["TestAppACrashingV2"],
+  installable: false,
 }
\ No newline at end of file
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index f6699fa..5a92d68 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -392,9 +392,6 @@
                     RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS,
                     Long.toString(expirationTime), false /* makeDefault*/);
 
-            // Pull the new expiration time from DeviceConfig
-            rm.reloadPersistedData();
-
             // Uninstall TestApp.A
             Uninstall.packages(TestApp.A);
             assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
@@ -457,9 +454,6 @@
                     RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS,
                     Long.toString(expirationTime), false /* makeDefault*/);
 
-            // Pull the new expiration time from DeviceConfig
-            rm.reloadPersistedData();
-
             // Install app A with rollback enabled
             Uninstall.packages(TestApp.A);
             Install.single(TestApp.A1).commit();
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 ce20311..80491cd 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -500,8 +500,9 @@
             APK_IN_APEX_TESTAPEX_NAME, 1, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v1.apex");
     private static final TestApp TEST_APEX_WITH_APK_V2 = new TestApp("TestApexWithApkV2",
             APK_IN_APEX_TESTAPEX_NAME, 2, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v2.apex");
-    private static final TestApp TEST_APP_A_V2_UNKNOWN = new TestApp("Av2Unknown", TestApp.A, 0,
-            /*isApex*/false, "TestAppAv2.apk");
+    private static final TestApp TEST_APEX_WITH_APK_V2_CRASHING = new TestApp(
+            "TestApexWithApkV2Crashing", APK_IN_APEX_TESTAPEX_NAME, 2, /*isApex*/true,
+            APK_IN_APEX_TESTAPEX_NAME + "_v2Crashing.apex");
 
     @Test
     public void testRollbackApexWithApk_Phase1() throws Exception {
@@ -523,7 +524,7 @@
         assertThat(available).isStaged();
         assertThat(available).packagesContainsExactly(
                 Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1),
-                Rollback.from(TEST_APP_A_V2_UNKNOWN).to(TestApp.A1));
+                Rollback.from(TestApp.A, 0).to(TestApp.A1));
 
         RollbackUtils.rollback(available.getRollbackId(), TEST_APEX_WITH_APK_V2);
         RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId());
@@ -531,7 +532,7 @@
         assertThat(committed).isStaged();
         assertThat(committed).packagesContainsExactly(
                 Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1),
-                Rollback.from(TEST_APP_A_V2_UNKNOWN).to(TestApp.A1));
+                Rollback.from(TestApp.A, 0).to(TestApp.A1));
         assertThat(committed).causePackagesContainsExactly(TEST_APEX_WITH_APK_V2);
         assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1);
 
@@ -548,6 +549,41 @@
         InstallUtils.processUserData(TestApp.A);
     }
 
+    /**
+     * Installs an apex with an apk that can crash.
+     */
+    @Test
+    public void testRollbackApexWithApkCrashing_Phase1() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+        int sessionId = Install.single(TEST_APEX_WITH_APK_V2_CRASHING).setStaged()
+                .setEnableRollback().commit();
+        InstallUtils.waitForSessionReady(sessionId);
+    }
+
+    /**
+     * Verifies rollback has been enabled successfully. Then makes TestApp.A crash.
+     */
+    @Test
+    public void testRollbackApexWithApkCrashing_Phase2() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(2);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+
+        RollbackInfo available = RollbackUtils.getAvailableRollback(APK_IN_APEX_TESTAPEX_NAME);
+        assertThat(available).isStaged();
+        assertThat(available).packagesContainsExactly(
+                Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1),
+                Rollback.from(TestApp.A, 0).to(TestApp.A1));
+
+        // Crash TestApp.A PackageWatchdog#TRIGGER_FAILURE_COUNT times to trigger rollback
+        RollbackUtils.sendCrashBroadcast(TestApp.A, 5);
+    }
+
+    @Test
+    public void testRollbackApexWithApkCrashing_Phase3() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(1);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+    }
+
     private static void runShellCommand(String cmd) {
         ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
                 .executeShellCommand(cmd);
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 181e29a..672cbb0 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
@@ -261,6 +261,34 @@
         runPhase("testRollbackApexWithApk_Phase3");
     }
 
+    /**
+     * Tests that RollbackPackageHealthObserver is observing apk-in-apex.
+     */
+    @Test
+    public void testRollbackApexWithApkCrashing() throws Exception {
+        getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A");
+        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
+        final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex";
+        final File apex = buildHelper.getTestFile(fileName);
+        if (!getDevice().isAdbRoot()) {
+            getDevice().enableAdbRoot();
+        }
+        getDevice().remountSystemWritable();
+        assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName));
+        getDevice().reboot();
+
+        // Install an apex with apk that crashes
+        runPhase("testRollbackApexWithApkCrashing_Phase1");
+        getDevice().reboot();
+        // Verify apex was installed and then crash the apk
+        runPhase("testRollbackApexWithApkCrashing_Phase2");
+        // Wait for crash to trigger rollback
+        assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5)));
+        getDevice().waitForDeviceAvailable();
+        // Verify rollback occurred due to crash of apk-in-apex
+        runPhase("testRollbackApexWithApkCrashing_Phase3");
+    }
+
     private void crashProcess(String processName, int numberOfCrashes) throws Exception {
         String pid = "";
         String lastPid = "invalid";
diff --git a/tests/WindowInsetsTests/Android.bp b/tests/WindowInsetsTests/Android.bp
new file mode 100644
index 0000000..12395e7
--- /dev/null
+++ b/tests/WindowInsetsTests/Android.bp
@@ -0,0 +1,22 @@
+// 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.
+
+android_test {
+    name: "WindowInsetsTests",
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+    certificate: "platform",
+    platform_apis: true,
+}
+
diff --git a/tests/WindowInsetsTests/AndroidManifest.xml b/tests/WindowInsetsTests/AndroidManifest.xml
new file mode 100644
index 0000000..8d33f70
--- /dev/null
+++ b/tests/WindowInsetsTests/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (018C) 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
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.google.android.test.windowinsetstests">
+
+    <application android:label="@string/activity_title">
+        <activity android:name=".WindowInsetsActivity"
+            android:theme="@android:style/Theme.Material"
+            android:windowSoftInputMode="adjustResize">
+
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/WindowInsetsTests/res/layout/window_inset_activity.xml b/tests/WindowInsetsTests/res/layout/window_inset_activity.xml
new file mode 100644
index 0000000..38e0029
--- /dev/null
+++ b/tests/WindowInsetsTests/res/layout/window_inset_activity.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center"
+    android:id="@+id/root">
+
+    <Button
+        android:id="@+id/button"
+        android:layout_width="wrap_content"
+        android:layout_height="48dp"
+        android:text="Hello insets" />
+
+</LinearLayout>
+
diff --git a/tests/WindowInsetsTests/res/values/strings.xml b/tests/WindowInsetsTests/res/values/strings.xml
new file mode 100644
index 0000000..242823d
--- /dev/null
+++ b/tests/WindowInsetsTests/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<resources>
+    <string name="activity_title">Window Insets Tests</string>
+</resources>
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
new file mode 100644
index 0000000..b8b2de5
--- /dev/null
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
@@ -0,0 +1,159 @@
+/*
+ * 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 com.google.android.test.windowinsetstests;
+
+import android.animation.ObjectAnimator;
+import android.animation.TypeEvaluator;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.graphics.Insets;
+import android.os.Bundle;
+import android.util.Property;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowInsets.Type;
+import android.view.WindowInsetsAnimationCallback;
+import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
+import android.view.WindowInsetsAnimationControlListener;
+import android.view.WindowInsetsAnimationController;
+
+import com.google.android.test.windowinsetstests.R;
+
+public class WindowInsetsActivity extends Activity {
+
+    private View mRoot;
+    private View mButton;
+
+    private static class InsetsProperty extends Property<WindowInsetsAnimationController, Insets> {
+
+        private final View mViewToAnimate;
+        private final Insets mShowingInsets;
+
+        public InsetsProperty(View viewToAnimate, Insets showingInsets) {
+            super(Insets.class, "Insets");
+            mViewToAnimate = viewToAnimate;
+            mShowingInsets = showingInsets;
+        }
+
+        @Override
+        public Insets get(WindowInsetsAnimationController object) {
+            return object.getCurrentInsets();
+        }
+
+        @Override
+        public void set(WindowInsetsAnimationController object, Insets value) {
+            object.setInsetsAndAlpha(value, 1.0f, 0.5f);
+            if (mShowingInsets.bottom != 0) {
+                mViewToAnimate.setTranslationY(mShowingInsets.bottom - value.bottom);
+            } else if (mShowingInsets.right != 0) {
+                mViewToAnimate.setTranslationX(mShowingInsets.right - value.right);
+            } else if (mShowingInsets.left != 0) {
+                mViewToAnimate.setTranslationX(value.left - mShowingInsets.left);
+            }
+        }
+    };
+
+    float showY;
+    float hideY;
+    InsetsAnimation imeAnim;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.window_inset_activity);
+        mRoot = findViewById(R.id.root);
+        mButton = findViewById(R.id.button);
+        mButton.setOnClickListener(v -> {
+            if (!v.getRootWindowInsets().isVisible(Type.ime())) {
+                v.getWindowInsetsController().show(Type.ime());
+            } else {
+                v.getWindowInsetsController().hide(Type.ime());
+            }
+        });
+        mRoot.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
+            if (imeAnim == null) {
+                return;
+            }
+            if (mRoot.getRootWindowInsets().isVisible(Type.ime())) {
+                showY = mButton.getTop();
+            } else {
+                hideY = mButton.getTop();
+            }
+        });
+        mRoot.setWindowInsetsAnimationCallback(new WindowInsetsAnimationCallback() {
+
+            @Override
+            public void onPrepare(InsetsAnimation animation) {
+                if ((animation.getTypeMask() & Type.ime()) != 0) {
+                    imeAnim = animation;
+                }
+                if (mRoot.getRootWindowInsets().isVisible(Type.ime())) {
+                    showY = mButton.getTop();
+                } else {
+                    hideY = mButton.getTop();
+                }
+            }
+
+            @Override
+            public WindowInsets onProgress(WindowInsets insets) {
+                mButton.setY(hideY + (showY - hideY) * imeAnim.getInterpolatedFraction());
+                return insets;
+            }
+
+            @Override
+            public AnimationBounds onStart(InsetsAnimation animation,
+                    AnimationBounds bounds) {
+                return bounds;
+            }
+
+            @Override
+            public void onFinish(InsetsAnimation animation) {
+                imeAnim = null;
+            }
+        });
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        TypeEvaluator<Insets> evaluator = (fraction, startValue, endValue) -> Insets.of(
+                (int)(startValue.left + fraction * (endValue.left - startValue.left)),
+                (int)(startValue.top + fraction * (endValue.top - startValue.top)),
+                (int)(startValue.right + fraction * (endValue.right - startValue.right)),
+                (int)(startValue.bottom + fraction * (endValue.bottom - startValue.bottom)));
+
+        WindowInsetsAnimationControlListener listener = new WindowInsetsAnimationControlListener() {
+            @Override
+            public void onReady(WindowInsetsAnimationController controller, int types) {
+                ObjectAnimator animator = ObjectAnimator.ofObject(controller,
+                        new InsetsProperty(findViewById(R.id.button),
+                                controller.getShownStateInsets()),
+                        evaluator, controller.getShownStateInsets(),
+                        controller.getHiddenStateInsets());
+                animator.setRepeatCount(ValueAnimator.INFINITE);
+                animator.setRepeatMode(ValueAnimator.REVERSE);
+                animator.start();
+            }
+
+            @Override
+            public void onCancelled() {
+
+            }
+        };
+    }
+}
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index 11d5b25..1c69209 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
@@ -74,6 +75,7 @@
         final String typeName = ConnectivityManager.getNetworkTypeName(type);
         mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
         mNetworkCapabilities = new NetworkCapabilities();
+        mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
         mNetworkCapabilities.addTransportType(transport);
         switch (transport) {
             case TRANSPORT_ETHERNET:
@@ -206,13 +208,11 @@
     }
 
     public void suspend() {
-        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, null, null);
-        mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+        removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
     }
 
     public void resume() {
-        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
-        mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+        addCapability(NET_CAPABILITY_NOT_SUSPENDED);
     }
 
     public void disconnect() {
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index 8d99ac7..8eac3ea 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -234,6 +234,7 @@
             try {
                 mLooper = setAsMain ? Looper.getMainLooper() : createLooper();
                 mTestableLooper = new TestableLooper(mLooper, false);
+                mTestableLooper.getLooper().getThread().setName(test.getClass().getName());
             } catch (Exception e) {
                 throw new RuntimeException(e);
             }
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 58e232c..cbce8a5 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -59,10 +59,22 @@
   dst[i] = 0;
 }
 
+static bool cmp_style_ids(ResourceId a, ResourceId b) {
+  // If one of a and b is from the framework package (package ID 0x01), and the
+  // other is a dynamic ID (package ID 0x00), then put the dynamic ID after the
+  // framework ID. This ensures that when AssetManager resolves the dynamic IDs,
+  // they will be in sorted order as expected by AssetManager.
+  if ((a.package_id() == kFrameworkPackageId && b.package_id() == 0x00) ||
+      (a.package_id() == 0x00 && b.package_id() == kFrameworkPackageId)) {
+    return b < a;
+  }
+  return a < b;
+}
+
 static bool cmp_style_entries(const Style::Entry& a, const Style::Entry& b) {
   if (a.key.id) {
     if (b.key.id) {
-      return a.key.id.value() < b.key.id.value();
+      return cmp_style_ids(a.key.id.value(), b.key.id.value());
     }
     return true;
   } else if (!b.key.id) {
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index 8fbdd7f..af2293f 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -431,6 +431,47 @@
   EXPECT_EQ("lib", iter->second);
 }
 
+TEST_F(TableFlattenerTest, FlattenSharedLibraryWithStyle) {
+  std::unique_ptr<IAaptContext> context =
+      test::ContextBuilder().SetCompilationPackage("lib").SetPackageId(0x00).Build();
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .SetPackageId("lib", 0x00)
+          .AddValue("lib:style/Theme",
+                    ResourceId(0x00030001),
+                    test::StyleBuilder()
+                    .AddItem("lib:attr/bar", ResourceId(0x00010002),
+                             ResourceUtils::TryParseInt("2"))
+                    .AddItem("lib:attr/foo", ResourceId(0x00010001),
+                             ResourceUtils::TryParseInt("1"))
+                    .AddItem("android:attr/bar", ResourceId(0x01010002),
+                             ResourceUtils::TryParseInt("4"))
+                    .AddItem("android:attr/foo", ResourceId(0x01010001),
+                             ResourceUtils::TryParseInt("3"))
+                    .Build())
+          .Build();
+  ResourceTable result;
+  ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result));
+
+  Maybe<ResourceTable::SearchResult> search_result =
+      result.FindResource(test::ParseNameOrDie("lib:style/Theme"));
+  ASSERT_TRUE(search_result);
+  EXPECT_EQ(0x00u, search_result.value().package->id.value());
+  EXPECT_EQ(0x03u, search_result.value().type->id.value());
+  EXPECT_EQ(0x01u, search_result.value().entry->id.value());
+  ASSERT_EQ(1u, search_result.value().entry->values.size());
+  Value* value = search_result.value().entry->values[0]->value.get();
+  Style* style = ValueCast<Style>(value);
+  ASSERT_TRUE(style);
+  ASSERT_EQ(4u, style->entries.size());
+  // Ensure the attributes from the shared library come after the items from
+  // android.
+  EXPECT_EQ(0x01010001, style->entries[0].key.id.value());
+  EXPECT_EQ(0x01010002, style->entries[1].key.id.value());
+  EXPECT_EQ(0x00010001, style->entries[2].key.id.value());
+  EXPECT_EQ(0x00010002, style->entries[3].key.id.value());
+}
+
 TEST_F(TableFlattenerTest, FlattenTableReferencingSharedLibraries) {
   std::unique_ptr<IAaptContext> context =
       test::ContextBuilder().SetCompilationPackage("app").SetPackageId(0x7f).Build();
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 27960c8..954d401 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -349,6 +349,7 @@
     }
     return true;
   });
+  manifest_action["uses-sdk"]["extension-sdk"];
 
   // Instrumentation actions.
   manifest_action["instrumentation"].Action(RequiredNameIsJavaClassName);
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
index 99a26dc..3c55237 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
@@ -53,36 +53,38 @@
 
         val executor = newThreadPool()
 
-        command.javaSourceArgs.map { path ->
-            executor.submitCallable {
-                val transformer = SourceTransformer(command.protoLogImplClassNameArg,
-                        command.protoLogCacheClassNameArg, processor)
-                val file = File(path)
-                val text = injector.readText(file)
-                val outSrc = try {
-                    val code = tryParse(text, path)
-                    if (containsProtoLogText(text, command.protoLogClassNameArg)) {
-                        transformer.processClass(text, path, packagePath(file, code), code)
-                    } else {
+        try {
+            command.javaSourceArgs.map { path ->
+                executor.submitCallable {
+                    val transformer = SourceTransformer(command.protoLogImplClassNameArg,
+                            command.protoLogCacheClassNameArg, processor)
+                    val file = File(path)
+                    val text = injector.readText(file)
+                    val outSrc = try {
+                        val code = tryParse(text, path)
+                        if (containsProtoLogText(text, command.protoLogClassNameArg)) {
+                            transformer.processClass(text, path, packagePath(file, code), code)
+                        } else {
+                            text
+                        }
+                    } catch (ex: ParsingException) {
+                        // If we cannot parse this file, skip it (and log why). Compilation will
+                        // fail in a subsequent build step.
+                        injector.reportParseError(ex)
                         text
                     }
-                } catch (ex: ParsingException) {
-                    // If we cannot parse this file, skip it (and log why). Compilation will fail
-                    // in a subsequent build step.
-                    injector.reportParseError(ex)
-                    text
+                    path to outSrc
                 }
-                path to outSrc
+            }.map { future ->
+                val (path, outSrc) = future.get()
+                outJar.putNextEntry(ZipEntry(path))
+                outJar.write(outSrc.toByteArray())
+                outJar.closeEntry()
             }
-        }.map { future ->
-            val (path, outSrc) = future.get()
-            outJar.putNextEntry(ZipEntry(path))
-            outJar.write(outSrc.toByteArray())
-            outJar.closeEntry()
+        } finally {
+            executor.shutdown()
         }
 
-        executor.shutdown()
-
         val cacheSplit = command.protoLogCacheClassNameArg.split(".")
         val cacheName = cacheSplit.last()
         val cachePackage = cacheSplit.dropLast(1).joinToString(".")
@@ -153,30 +155,32 @@
 
         val executor = newThreadPool()
 
-        command.javaSourceArgs.map { path ->
-            executor.submitCallable {
-                val file = File(path)
-                val text = injector.readText(file)
-                if (containsProtoLogText(text, command.protoLogClassNameArg)) {
-                    try {
-                        val code = tryParse(text, path)
-                        builder.findLogCalls(code, path, packagePath(file, code))
-                    } catch (ex: ParsingException) {
-                        // If we cannot parse this file, skip it (and log why). Compilation will fail
-                        // in a subsequent build step.
-                        injector.reportParseError(ex)
+        try {
+            command.javaSourceArgs.map { path ->
+                executor.submitCallable {
+                    val file = File(path)
+                    val text = injector.readText(file)
+                    if (containsProtoLogText(text, command.protoLogClassNameArg)) {
+                        try {
+                            val code = tryParse(text, path)
+                            builder.findLogCalls(code, path, packagePath(file, code))
+                        } catch (ex: ParsingException) {
+                            // If we cannot parse this file, skip it (and log why). Compilation will
+                            // fail in a subsequent build step.
+                            injector.reportParseError(ex)
+                            null
+                        }
+                    } else {
                         null
                     }
-                } else {
-                    null
                 }
+            }.forEach { future ->
+                builder.addLogCalls(future.get() ?: return@forEach)
             }
-        }.forEach { future ->
-            builder.addLogCalls(future.get() ?: return@forEach)
+        } finally {
+            executor.shutdown()
         }
 
-        executor.shutdown()
-
         val out = injector.fileOutputStream(command.viewerConfigJsonArg)
         out.write(builder.build().toByteArray())
         out.close()
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 70c9bef..4c9ee85 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -70,6 +70,7 @@
         "framework-annotations-lib",
         "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
         "unsupportedappusage-annotation", // for dalvik.annotation.compat.UnsupportedAppUsage
+        "framework-telephony-stubs",
     ],
     srcs: [
         ":framework-wifi-updatable-sources",
diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt
index 8f72040..d377ee6 100644
--- a/wifi/jarjar-rules.txt
+++ b/wifi/jarjar-rules.txt
@@ -1,5 +1,7 @@
 rule android.net.InterfaceConfigurationParcel* @0
 rule android.net.InterfaceConfiguration* com.android.server.x.wifi.net.InterfaceConfiguration@1
+rule android.net.NetworkFactory* com.android.server.x.wifi.net.NetworkFactory@1
+rule android.net.util.NetUtils* com.android.server.x.wifi.net.util.NetUtils@1
 
 # We don't jar-jar the entire package because, we still use some classes (like
 # AsyncChannel in com.android.internal.util) from these packages which are not
@@ -32,6 +34,7 @@
 rule android.content.pm.BaseParceledListSlice* android.x.net.wifi.util.BaseParceledListSlice@1
 rule android.content.pm.ParceledListSlice* android.x.net.wifi.util.ParceledListSlice@1
 rule android.net.shared.Inet4AddressUtils* android.x.net.wifi.util.Inet4AddressUtils@1
+rule android.net.util.MacAddressUtils* android.x.net.wifi.util.MacAddressUtils@1
 rule android.os.HandlerExecutor* android.x.net.wifi.util.HandlerExecutor@1
 rule android.telephony.Annotation* android.x.net.wifi.util.TelephonyAnnotation@1
 rule com.android.internal.util.AsyncChannel* android.x.net.wifi.util.AsyncChannel@1
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index f490766..67f1663 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -92,6 +92,8 @@
 
     void allowAutojoinPasspoint(String fqdn, boolean enableAutoJoin);
 
+    void setMacRandomizationSettingPasspointEnabled(String fqdn, boolean enable);
+
     boolean startScan(String packageName, String featureId);
 
     List<ScanResult> getScanResults(String callingPackage, String callingFeatureId);
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index a9621fc..b2fbb40 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -856,18 +856,6 @@
 
     /**
      * @hide
-     * For debug: date at which the config was last updated
-     */
-    public String updateTime;
-
-    /**
-     * @hide
-     * For debug: date at which the config was last updated
-     */
-    public String creationTime;
-
-    /**
-     * @hide
      * The WiFi configuration is considered to have no internet access for purpose of autojoining
      * if there has been a report of it having no internet access, and, it never have had
      * internet access in the past.
@@ -1493,12 +1481,6 @@
         private String mConnectChoice;
 
         /**
-         * The system timestamp when we records the connectChoice. This value is obtained from
-         * System.currentTimeMillis
-         */
-        private long mConnectChoiceTimestamp = INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
-
-        /**
          * Used to cache the temporary candidate during the network selection procedure. It will be
          * kept updating once a new scan result has a higher score than current one
          */
@@ -1601,25 +1583,6 @@
             mConnectChoice = newConnectChoice;
         }
 
-        /**
-         * get the timeStamp when user select a choice over this configuration
-         * @return returns when current connectChoice is set (time from System.currentTimeMillis)
-         * @hide
-         */
-        public long getConnectChoiceTimestamp() {
-            return mConnectChoiceTimestamp;
-        }
-
-        /**
-         * set the timeStamp when user select a choice over this configuration
-         * @param timeStamp, the timestamp set to connectChoiceTimestamp, expected timestamp should
-         *        be obtained from System.currentTimeMillis
-         * @hide
-         */
-        public void setConnectChoiceTimestamp(long timeStamp) {
-            mConnectChoiceTimestamp = timeStamp;
-        }
-
         /** Get the current Quality network selection status as a String (for debugging). */
         @NonNull
         public String getNetworkStatusString() {
@@ -1902,7 +1865,6 @@
             setCandidate(source.getCandidate());
             setCandidateScore(source.getCandidateScore());
             setConnectChoice(source.getConnectChoice());
-            setConnectChoiceTimestamp(source.getConnectChoiceTimestamp());
             setHasEverConnected(source.getHasEverConnected());
         }
 
@@ -1919,7 +1881,6 @@
             if (getConnectChoice() != null) {
                 dest.writeInt(CONNECT_CHOICE_EXISTS);
                 dest.writeString(getConnectChoice());
-                dest.writeLong(getConnectChoiceTimestamp());
             } else {
                 dest.writeInt(CONNECT_CHOICE_NOT_EXISTS);
             }
@@ -1938,10 +1899,8 @@
             setNetworkSelectionBSSID(in.readString());
             if (in.readInt() == CONNECT_CHOICE_EXISTS) {
                 setConnectChoice(in.readString());
-                setConnectChoiceTimestamp(in.readLong());
             } else {
                 setConnectChoice(null);
-                setConnectChoiceTimestamp(INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
             }
             setHasEverConnected(in.readInt() != 0);
         }
@@ -2165,9 +2124,6 @@
         }
         if (mNetworkSelectionStatus.getConnectChoice() != null) {
             sbuf.append(" connect choice: ").append(mNetworkSelectionStatus.getConnectChoice());
-            sbuf.append(" connect choice set time: ")
-                    .append(logTimeOfDay(
-                            mNetworkSelectionStatus.getConnectChoiceTimestamp()));
         }
         sbuf.append(" hasEverConnected: ")
                 .append(mNetworkSelectionStatus.getHasEverConnected()).append("\n");
@@ -2179,12 +2135,6 @@
             sbuf.append(" numNoInternetAccessReports ");
             sbuf.append(this.numNoInternetAccessReports).append("\n");
         }
-        if (this.updateTime != null) {
-            sbuf.append(" update ").append(this.updateTime).append("\n");
-        }
-        if (this.creationTime != null) {
-            sbuf.append(" creation ").append(this.creationTime).append("\n");
-        }
         if (this.validatedInternetAccess) sbuf.append(" validatedInternetAccess");
         if (this.ephemeral) sbuf.append(" ephemeral");
         if (this.osu) sbuf.append(" osu");
@@ -2731,8 +2681,6 @@
             allowAutojoin = source.allowAutojoin;
             numNoInternetAccessReports = source.numNoInternetAccessReports;
             noInternetAccessExpected = source.noInternetAccessExpected;
-            creationTime = source.creationTime;
-            updateTime = source.updateTime;
             shared = source.shared;
             recentFailure.setAssociationStatus(source.recentFailure.getAssociationStatus());
             mRandomizedMacAddress = source.mRandomizedMacAddress;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 2f7cf54..ec3de43 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2369,6 +2369,15 @@
     }
 
     /**
+     * Query whether the device supports Station (STA) + Access point (AP) concurrency or not.
+     *
+     * @return true if this device supports STA + AP concurrency, false otherwise.
+     */
+    public boolean isStaApConcurrencySupported() {
+        return isFeatureSupported(WIFI_FEATURE_AP_STA);
+    }
+
+    /**
      * @deprecated Please use {@link android.content.pm.PackageManager#hasSystemFeature(String)}
      * with {@link android.content.pm.PackageManager#FEATURE_WIFI_RTT} and
      * {@link android.content.pm.PackageManager#FEATURE_WIFI_AWARE}.
@@ -4234,6 +4243,24 @@
     }
 
     /**
+     * Configure MAC randomization setting for a Passpoint profile.
+     * MAC randomization is enabled by default.
+     *
+     * @param fqdn the FQDN (fully qualified domain name) of the passpoint profile.
+     * @param enable true to enable MAC randomization, false to disable MAC randomization.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public void setMacRandomizationSettingPasspointEnabled(@NonNull String fqdn, boolean enable) {
+        try {
+            mService.setMacRandomizationSettingPasspointEnabled(fqdn, enable);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Disable an ephemeral network.
      *
      * @param ssid in the format of WifiConfiguration's SSID.
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index c0e0890..2fba5a3 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -122,6 +122,12 @@
          * Whether the setCredentialSharedWithUser have been called.
          */
         private boolean mIsSharedWithUserSet;
+
+        /**
+         * Whether this network is initialized with auto-join enabled (the default) or not.
+         */
+        private boolean mIsInitialAutoJoinEnabled;
+
         /**
          * Pre-shared key for use with WAPI-PSK networks.
          */
@@ -148,6 +154,7 @@
             mIsMetered = false;
             mIsSharedWithUser = true;
             mIsSharedWithUserSet = false;
+            mIsInitialAutoJoinEnabled = true;
             mPriority = UNASSIGNED_PRIORITY;
             mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
             mWapiPskPassphrase = null;
@@ -440,6 +447,27 @@
             return this;
         }
 
+        /**
+         * Specifies whether the suggestion is created with auto-join enabled or disabled. The
+         * user may modify the auto-join configuration of a suggestion directly once the device
+         * associates to the network.
+         * <p>
+         * If auto-join is initialized as disabled the user may still be able to manually connect
+         * to the network. Therefore, disabling auto-join only makes sense if
+         * {@link #setCredentialSharedWithUser(boolean)} is set to true (the default) which
+         * itself implies a secure (non-open) network.
+         * <p>
+         * If not set, defaults to true (i.e. auto-join is initialized as enabled).
+         *
+         * @param enabled true for initializing with auto-join enabled (the default), false to
+         *                initializing with auto-join disabled.
+         * @return Instance of (@link {@link Builder} to enable chaining of the builder method.
+         */
+        public @NonNull Builder setIsInitialAutoJoinEnabled(boolean enabled) {
+            mIsInitialAutoJoinEnabled = enabled;
+            return this;
+        }
+
         private void setSecurityParamsInWifiConfiguration(
                 @NonNull WifiConfiguration configuration) {
             if (!TextUtils.isEmpty(mWpa2PskPassphrase)) { // WPA-PSK network.
@@ -587,7 +615,6 @@
                             + "suggestion with Passpoint configuration");
                 }
                 wifiConfiguration = buildWifiConfigurationForPasspoint();
-
             } else {
                 if (mSsid == null) {
                     throw new IllegalStateException("setSsid should be invoked for suggestion");
@@ -608,6 +635,12 @@
                     }
                     mIsSharedWithUser = false;
                 }
+
+                if (!mIsSharedWithUser && !mIsInitialAutoJoinEnabled) {
+                    throw new IllegalStateException("Should have not a network with both "
+                            + "setIsUserAllowedToManuallyConnect and "
+                            + "setIsAutoJoinEnabled set to false");
+                }
             }
 
             return new WifiNetworkSuggestion(
@@ -615,7 +648,8 @@
                     mPasspointConfiguration,
                     mIsAppInteractionRequired,
                     mIsUserInteractionRequired,
-                    mIsSharedWithUser);
+                    mIsSharedWithUser,
+                    mIsInitialAutoJoinEnabled);
         }
     }
 
@@ -642,6 +676,7 @@
      * @hide
      */
     public final boolean isUserInteractionRequired;
+
     /**
      * Whether app share credential with the user, allow user use provided credential to
      * connect network manually.
@@ -649,6 +684,12 @@
      */
     public final boolean isUserAllowedToManuallyConnect;
 
+    /**
+     * Whether the suggestion will be initialized as auto-joined or not.
+     * @hide
+     */
+    public final boolean isInitialAutoJoinEnabled;
+
     /** @hide */
     public WifiNetworkSuggestion() {
         this.wifiConfiguration = null;
@@ -656,6 +697,7 @@
         this.isAppInteractionRequired = false;
         this.isUserInteractionRequired = false;
         this.isUserAllowedToManuallyConnect = true;
+        this.isInitialAutoJoinEnabled = true;
     }
 
     /** @hide */
@@ -663,7 +705,8 @@
                                  @Nullable PasspointConfiguration passpointConfiguration,
                                  boolean isAppInteractionRequired,
                                  boolean isUserInteractionRequired,
-                                 boolean isUserAllowedToManuallyConnect) {
+                                 boolean isUserAllowedToManuallyConnect,
+                                 boolean isInitialAutoJoinEnabled) {
         checkNotNull(networkConfiguration);
         this.wifiConfiguration = networkConfiguration;
         this.passpointConfiguration = passpointConfiguration;
@@ -671,6 +714,7 @@
         this.isAppInteractionRequired = isAppInteractionRequired;
         this.isUserInteractionRequired = isUserInteractionRequired;
         this.isUserAllowedToManuallyConnect = isUserAllowedToManuallyConnect;
+        this.isInitialAutoJoinEnabled = isInitialAutoJoinEnabled;
     }
 
     public static final @NonNull Creator<WifiNetworkSuggestion> CREATOR =
@@ -682,7 +726,8 @@
                             in.readParcelable(null), // PasspointConfiguration
                             in.readBoolean(), // isAppInteractionRequired
                             in.readBoolean(), // isUserInteractionRequired
-                            in.readBoolean()  // isSharedCredentialWithUser
+                            in.readBoolean(), // isSharedCredentialWithUser
+                            in.readBoolean()  // isAutoJoinEnabled
                     );
                 }
 
@@ -704,6 +749,7 @@
         dest.writeBoolean(isAppInteractionRequired);
         dest.writeBoolean(isUserInteractionRequired);
         dest.writeBoolean(isUserAllowedToManuallyConnect);
+        dest.writeBoolean(isInitialAutoJoinEnabled);
     }
 
     @Override
@@ -744,6 +790,7 @@
                 .append(", isAppInteractionRequired=").append(isAppInteractionRequired)
                 .append(", isUserInteractionRequired=").append(isUserInteractionRequired)
                 .append(", isUserAllowedToManuallyConnect=").append(isUserAllowedToManuallyConnect)
+                .append(", isInitialAutoJoinEnabled=").append(isInitialAutoJoinEnabled)
                 .append(" ]");
         return sb.toString();
     }
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 1822e84..7c335fc 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -431,6 +431,13 @@
     private boolean mIsAutoJoinEnabled = true;
 
     /**
+     * The mac randomization setting specifies whether a randomized or device MAC address will
+     * be used to connect to the passpoint network. If true, a randomized MAC will be used.
+     * Otherwise, the device MAC address will be used.
+     */
+    private boolean mIsMacRandomizationEnabled = true;
+
+    /**
      * Configures the auto-association status of this Passpoint configuration. A value of true
      * indicates that the configuration will be considered for auto-connection, a value of false
      * indicates that only manual connection will work - the framework will not auto-associate to
@@ -444,6 +451,18 @@
     }
 
     /**
+     * Configures the MAC randomization setting for this Passpoint configuration.
+     * If set to true, the framework will use a randomized MAC address to connect to this Passpoint
+     * network. Otherwise, the framework will use the device MAC address.
+     *
+     * @param enabled true to use randomized MAC address, false to use device MAC address.
+     * @hide
+     */
+    public void setMacRandomizationEnabled(boolean enabled) {
+        mIsMacRandomizationEnabled = enabled;
+    }
+
+    /**
      * Indicates whether the Passpoint configuration may be auto-connected to by the framework. A
      * value of true indicates that auto-connection can happen, a value of false indicates that it
      * cannot. However, even when auto-connection is not possible manual connection by the user is
@@ -459,6 +478,19 @@
     }
 
     /**
+     * Indicates whether a randomized MAC address or device MAC address will be used for
+     * connections to this Passpoint network. If true, a randomized MAC address will be used.
+     * Otherwise, the device MAC address will be used.
+     *
+     * @return true for MAC randomization enabled. False for disabled.
+     * @hide
+     */
+    @SystemApi
+    public boolean isMacRandomizationEnabled() {
+        return mIsMacRandomizationEnabled;
+    }
+
+    /**
      * Constructor for creating PasspointConfiguration with default values.
      */
     public PasspointConfiguration() {}
@@ -501,6 +533,7 @@
         mAaaServerTrustedNames = source.mAaaServerTrustedNames;
         mCarrierId = source.mCarrierId;
         mIsAutoJoinEnabled = source.mIsAutoJoinEnabled;
+        mIsMacRandomizationEnabled = source.mIsMacRandomizationEnabled;
     }
 
     @Override
@@ -531,6 +564,7 @@
         dest.writeBundle(bundle);
         dest.writeInt(mCarrierId);
         dest.writeBoolean(mIsAutoJoinEnabled);
+        dest.writeBoolean(mIsMacRandomizationEnabled);
     }
 
     @Override
@@ -562,6 +596,7 @@
                 && mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes
                 && mCarrierId == that.mCarrierId
                 && mIsAutoJoinEnabled == that.mIsAutoJoinEnabled
+                && mIsMacRandomizationEnabled == that.mIsMacRandomizationEnabled
                 && (mServiceFriendlyNames == null ? that.mServiceFriendlyNames == null
                 : mServiceFriendlyNames.equals(that.mServiceFriendlyNames));
     }
@@ -572,7 +607,7 @@
                 mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMillis,
                 mSubscriptionExpirationTimeInMillis, mUsageLimitUsageTimePeriodInMinutes,
                 mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes,
-                mServiceFriendlyNames, mCarrierId, mIsAutoJoinEnabled);
+                mServiceFriendlyNames, mCarrierId, mIsAutoJoinEnabled, mIsMacRandomizationEnabled);
     }
 
     @Override
@@ -627,6 +662,7 @@
         }
         builder.append("CarrierId:" + mCarrierId);
         builder.append("IsAutoJoinEnabled:" + mIsAutoJoinEnabled);
+        builder.append("mIsMacRandomizationEnabled:" + mIsMacRandomizationEnabled);
         return builder.toString();
     }
 
@@ -733,6 +769,7 @@
                 config.setServiceFriendlyNames(friendlyNamesMap);
                 config.mCarrierId = in.readInt();
                 config.mIsAutoJoinEnabled = in.readBoolean();
+                config.mIsMacRandomizationEnabled = in.readBoolean();
                 return config;
             }
 
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index 3c13562d..1cf3825 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -187,6 +187,11 @@
     }
 
     @Override
+    public void setMacRandomizationSettingPasspointEnabled(String fqdn, boolean enable) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public boolean startScan(String packageName, String featureId) {
         throw new UnsupportedOperationException();
     }
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 5bdc344..983ac82 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -1706,6 +1706,17 @@
         verify(mWifiService).allowAutojoinPasspoint(fqdn, true);
     }
 
+    /**
+     * Test behavior of
+     * {@link WifiManager#setMacRandomizationSettingPasspointEnabled(String, boolean)}
+     */
+    @Test
+    public void testSetMacRandomizationSettingPasspointEnabled() throws Exception {
+        final String fqdn = "FullyQualifiedDomainName";
+        mWifiManager.setMacRandomizationSettingPasspointEnabled(fqdn, true);
+        verify(mWifiService).setMacRandomizationSettingPasspointEnabled(fqdn, true);
+    }
+
 
     /**
      * Test behavior of {@link WifiManager#disconnect()}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index ac91544..cb1b774 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -32,8 +32,6 @@
  */
 @SmallTest
 public class WifiNetworkSuggestionTest {
-    private static final int TEST_UID = 45677;
-    private static final int TEST_UID_OTHER = 45673;
     private static final String TEST_SSID = "\"Test123\"";
     private static final String TEST_BSSID = "12:12:12:12:12:12";
     private static final String TEST_SSID_1 = "\"Test1234\"";
@@ -61,7 +59,8 @@
         assertEquals(WifiConfiguration.METERED_OVERRIDE_NONE,
                 suggestion.wifiConfiguration.meteredOverride);
         assertEquals(-1, suggestion.wifiConfiguration.priority);
-        assertEquals(false, suggestion.isUserAllowedToManuallyConnect);
+        assertFalse(suggestion.isUserAllowedToManuallyConnect);
+        assertTrue(suggestion.isInitialAutoJoinEnabled);
     }
 
     /**
@@ -90,7 +89,8 @@
         assertEquals(WifiConfiguration.METERED_OVERRIDE_NONE,
                 suggestion.wifiConfiguration.meteredOverride);
         assertEquals(0, suggestion.wifiConfiguration.priority);
-        assertEquals(false, suggestion.isUserAllowedToManuallyConnect);
+        assertFalse(suggestion.isUserAllowedToManuallyConnect);
+        assertTrue(suggestion.isInitialAutoJoinEnabled);
     }
 
     /**
@@ -105,6 +105,7 @@
                 .setSsid(TEST_SSID)
                 .setWpa2Passphrase(TEST_PRESHARED_KEY)
                 .setIsUserInteractionRequired(true)
+                .setIsInitialAutoJoinEnabled(false)
                 .setIsMetered(true)
                 .build();
 
@@ -119,6 +120,7 @@
                 suggestion.wifiConfiguration.meteredOverride);
         assertEquals(-1, suggestion.wifiConfiguration.priority);
         assertTrue(suggestion.isUserAllowedToManuallyConnect);
+        assertFalse(suggestion.isInitialAutoJoinEnabled);
     }
 
     /**
@@ -140,6 +142,7 @@
         assertNull(suggestion.wifiConfiguration.preSharedKey);
         assertTrue(suggestion.wifiConfiguration.requirePMF);
         assertFalse(suggestion.isUserAllowedToManuallyConnect);
+        assertTrue(suggestion.isInitialAutoJoinEnabled);
     }
 
     /**
@@ -152,6 +155,7 @@
                 .setSsid(TEST_SSID)
                 .setWpa3Passphrase(TEST_PRESHARED_KEY)
                 .setCredentialSharedWithUser(true)
+                .setIsInitialAutoJoinEnabled(false)
                 .build();
 
         assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
@@ -161,6 +165,7 @@
                 suggestion.wifiConfiguration.preSharedKey);
         assertTrue(suggestion.wifiConfiguration.requirePMF);
         assertTrue(suggestion.isUserAllowedToManuallyConnect);
+        assertFalse(suggestion.isInitialAutoJoinEnabled);
     }
 
 
@@ -191,6 +196,7 @@
         // allowedSuiteBCiphers are set according to the loaded certificate and cannot be tested
         // here.
         assertTrue(suggestion.isUserAllowedToManuallyConnect);
+        assertTrue(suggestion.isInitialAutoJoinEnabled);
     }
 
     /**
@@ -526,7 +532,7 @@
         configuration.BSSID = TEST_BSSID;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion(
-                configuration, null, false, true, true);
+                configuration, null, false, true, true, false);
 
         Parcel parcelW = Parcel.obtain();
         suggestion.writeToParcel(parcelW, 0);
@@ -548,6 +554,8 @@
                 parcelSuggestion.isAppInteractionRequired);
         assertEquals(suggestion.isUserInteractionRequired,
                 parcelSuggestion.isUserInteractionRequired);
+        assertEquals(suggestion.isInitialAutoJoinEnabled,
+                parcelSuggestion.isInitialAutoJoinEnabled);
     }
 
     /**
@@ -580,6 +588,8 @@
                 parcelSuggestion.isAppInteractionRequired);
         assertEquals(suggestion.isUserInteractionRequired,
                 parcelSuggestion.isUserInteractionRequired);
+        assertEquals(suggestion.isInitialAutoJoinEnabled,
+                parcelSuggestion.isInitialAutoJoinEnabled);
     }
 
     /**
@@ -593,14 +603,14 @@
         configuration.BSSID = TEST_BSSID;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
         WifiNetworkSuggestion suggestion =
-                new WifiNetworkSuggestion(configuration, null, true, false, true);
+                new WifiNetworkSuggestion(configuration, null, true, false, true, true);
 
         WifiConfiguration configuration1 = new WifiConfiguration();
         configuration1.SSID = TEST_SSID;
         configuration1.BSSID = TEST_BSSID;
         configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
         WifiNetworkSuggestion suggestion1 =
-                new WifiNetworkSuggestion(configuration1, null, false, true, true);
+                new WifiNetworkSuggestion(configuration1, null, false, true, true, false);
 
         assertEquals(suggestion, suggestion1);
         assertEquals(suggestion.hashCode(), suggestion1.hashCode());
@@ -616,13 +626,13 @@
         configuration.SSID = TEST_SSID;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkSuggestion suggestion =
-                new WifiNetworkSuggestion(configuration, null, false, false, true);
+                new WifiNetworkSuggestion(configuration, null, false, false, true, false);
 
         WifiConfiguration configuration1 = new WifiConfiguration();
         configuration1.SSID = TEST_SSID_1;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkSuggestion suggestion1 =
-                new WifiNetworkSuggestion(configuration1, null, false, false, true);
+                new WifiNetworkSuggestion(configuration1, null, false, false, true, false);
 
         assertNotEquals(suggestion, suggestion1);
     }
@@ -638,13 +648,13 @@
         configuration.BSSID = TEST_BSSID;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkSuggestion suggestion =
-                new WifiNetworkSuggestion(configuration, null,  false, false, true);
+                new WifiNetworkSuggestion(configuration, null,  false, false, true, true);
 
         WifiConfiguration configuration1 = new WifiConfiguration();
         configuration1.SSID = TEST_SSID;
         configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkSuggestion suggestion1 =
-                new WifiNetworkSuggestion(configuration1, null, false, false, true);
+                new WifiNetworkSuggestion(configuration1, null, false, false, true, true);
 
         assertNotEquals(suggestion, suggestion1);
     }
@@ -659,13 +669,13 @@
         configuration.SSID = TEST_SSID;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkSuggestion suggestion =
-                new WifiNetworkSuggestion(configuration, null, false, false, true);
+                new WifiNetworkSuggestion(configuration, null, false, false, true, true);
 
         WifiConfiguration configuration1 = new WifiConfiguration();
         configuration1.SSID = TEST_SSID;
         configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
         WifiNetworkSuggestion suggestion1 =
-                new WifiNetworkSuggestion(configuration1, null, false, false, true);
+                new WifiNetworkSuggestion(configuration1, null, false, false, true, true);
 
         assertNotEquals(suggestion, suggestion1);
     }
@@ -714,9 +724,38 @@
      */
     @Test(expected = IllegalStateException.class)
     public void testSetIsUserAllowedToManuallyConnectToWithOpenNetwork() {
-        WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
+        new WifiNetworkSuggestion.Builder()
                 .setSsid(TEST_SSID)
                 .setCredentialSharedWithUser(true)
                 .build();
     }
+
+    /**
+     * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception
+     * when {@link WifiNetworkSuggestion.Builder#setIsInitialAutoJoinEnabled(boolean)} to
+     * false on a open network suggestion.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testSetIsAutoJoinDisabledWithOpenNetwork() {
+        new WifiNetworkSuggestion.Builder()
+                .setSsid(TEST_SSID)
+                .setIsInitialAutoJoinEnabled(false)
+                .build();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception
+     * when set both {@link WifiNetworkSuggestion.Builder#setIsInitialAutoJoinEnabled(boolean)}
+     * and {@link WifiNetworkSuggestion.Builder#setCredentialSharedWithUser(boolean)} (boolean)}
+     * to false on a network suggestion.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testSetIsAutoJoinDisabledWithSecureNetworkNotSharedWithUser() {
+        new WifiNetworkSuggestion.Builder()
+                .setSsid(TEST_SSID)
+                .setWpa2Passphrase(TEST_PRESHARED_KEY)
+                .setCredentialSharedWithUser(false)
+                .setIsInitialAutoJoinEnabled(false)
+                .build();
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index 94054fd..603e78b 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -172,6 +172,7 @@
         assertFalse(config.validate());
         assertFalse(config.validateForR2());
         assertTrue(config.isAutoJoinEnabled());
+        assertTrue(config.isMacRandomizationEnabled());
     }
 
     /**