Merge changes I2d2aa89c,Ib507b449

* changes:
  Build framework-wifi against core_current & framework-system-stubs
  WifiNetworkSpecifier: get context reflectively
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/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index ce7e04c..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,6 +15,7 @@
  */
 package com.android.server.appsearch;
 
+import android.annotation.NonNull;
 import android.app.appsearch.IAppSearchManager;
 import android.content.Context;
 import android.os.Binder;
@@ -24,9 +25,13 @@
 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
@@ -35,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());
@@ -70,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/api/current.txt b/api/current.txt
index e27c318..119f047 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6831,6 +6831,7 @@
     method public boolean isApplicationHidden(@NonNull android.content.ComponentName, String);
     method public boolean isBackupServiceEnabled(@NonNull android.content.ComponentName);
     method @Deprecated public boolean isCallerApplicationRestrictionsManagingPackage();
+    method public boolean isCommonCriteriaModeEnabled(@NonNull android.content.ComponentName);
     method public boolean isDeviceIdAttestationSupported();
     method public boolean isDeviceOwnerApp(String);
     method public boolean isEphemeralUser(@NonNull android.content.ComponentName);
@@ -6879,6 +6880,7 @@
     method public void setBluetoothContactSharingDisabled(@NonNull android.content.ComponentName, boolean);
     method public void setCameraDisabled(@NonNull android.content.ComponentName, boolean);
     method @Deprecated public void setCertInstallerPackage(@NonNull android.content.ComponentName, @Nullable String) throws java.lang.SecurityException;
+    method public void setCommonCriteriaModeEnabled(@NonNull android.content.ComponentName, boolean);
     method public void setCrossProfileCalendarPackages(@NonNull android.content.ComponentName, @Nullable java.util.Set<java.lang.String>);
     method public void setCrossProfileCallerIdDisabled(@NonNull android.content.ComponentName, boolean);
     method public void setCrossProfileContactsSearchDisabled(@NonNull android.content.ComponentName, boolean);
@@ -24260,6 +24262,9 @@
     method public int write(@NonNull float[], int, int, int);
     method public int write(@NonNull java.nio.ByteBuffer, int, int);
     method public int write(@NonNull java.nio.ByteBuffer, int, int, long);
+    field public static final int ENCAPSULATION_MODE_ELEMENTARY_STREAM = 1; // 0x1
+    field public static final int ENCAPSULATION_MODE_HANDLE = 2; // 0x2
+    field public static final int ENCAPSULATION_MODE_NONE = 0; // 0x0
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
     field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
@@ -24286,10 +24291,12 @@
     method @NonNull public android.media.AudioTrack.Builder setAudioAttributes(@NonNull android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
     method @NonNull public android.media.AudioTrack.Builder setAudioFormat(@NonNull android.media.AudioFormat) throws java.lang.IllegalArgumentException;
     method @NonNull public android.media.AudioTrack.Builder setBufferSizeInBytes(@IntRange(from=0) int) throws java.lang.IllegalArgumentException;
+    method @NonNull public android.media.AudioTrack.Builder setEncapsulationMode(int);
     method @NonNull public android.media.AudioTrack.Builder setOffloadedPlayback(boolean);
     method @NonNull public android.media.AudioTrack.Builder setPerformanceMode(int);
     method @NonNull public android.media.AudioTrack.Builder setSessionId(@IntRange(from=1) int) throws java.lang.IllegalArgumentException;
     method @NonNull public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException;
+    method @NonNull public android.media.AudioTrack.Builder setTunerConfiguration(@NonNull android.media.AudioTrack.TunerConfiguration);
   }
 
   public static final class AudioTrack.MetricsConstants {
@@ -24317,6 +24324,18 @@
     method public void onTearDown(@NonNull android.media.AudioTrack);
   }
 
+  public static class AudioTrack.TunerConfiguration {
+    method public int getContentId();
+    method public int getSyncId();
+  }
+
+  public static class AudioTrack.TunerConfiguration.Builder {
+    ctor public AudioTrack.TunerConfiguration.Builder();
+    method @NonNull public android.media.AudioTrack.TunerConfiguration build();
+    method @NonNull public android.media.AudioTrack.TunerConfiguration.Builder setContentId(@IntRange(from=1) int);
+    method @NonNull public android.media.AudioTrack.TunerConfiguration.Builder setSyncId(@IntRange(from=1) int);
+  }
+
   public class CamcorderProfile {
     method public static android.media.CamcorderProfile get(int);
     method public static android.media.CamcorderProfile get(int, int);
@@ -36226,6 +36245,7 @@
     method public boolean isUserUnlocked();
     method public boolean isUserUnlocked(android.os.UserHandle);
     method public boolean requestQuietModeEnabled(boolean, @NonNull android.os.UserHandle);
+    method public boolean requestQuietModeEnabled(boolean, @NonNull android.os.UserHandle, int);
     method @Deprecated public boolean setRestrictionsChallenge(String);
     method @Deprecated public void setUserRestriction(String, boolean);
     method @Deprecated public void setUserRestrictions(android.os.Bundle);
@@ -36287,6 +36307,7 @@
     field public static final String DISALLOW_USER_SWITCH = "no_user_switch";
     field public static final String ENSURE_VERIFY_APPS = "ensure_verify_apps";
     field public static final String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
+    field public static final int QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED = 1; // 0x1
     field public static final int USER_CREATION_FAILED_NOT_PERMITTED = 1; // 0x1
     field public static final int USER_CREATION_FAILED_NO_MORE_USERS = 2; // 0x2
     field public static final int USER_OPERATION_ERROR_CURRENT_USER = 4; // 0x4
@@ -39750,8 +39771,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";
   }
@@ -55697,7 +55718,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 4307e67..db774ef 100644
--- a/api/module-app-current.txt
+++ b/api/module-app-current.txt
@@ -7,3 +7,11 @@
 
 }
 
+package android.provider {
+
+  public static final class Settings.Global extends android.provider.Settings.NameValueTable {
+    field public static final String COMMON_CRITERIA_MODE = "common_criteria_mode";
+  }
+
+}
+
diff --git a/api/system-current.txt b/api/system-current.txt
index e532a3a..0167bec 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -661,6 +661,7 @@
     method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long[] getRegisteredExperimentIds() throws android.app.StatsManager.StatsUnavailableException;
     method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getReports(long) throws android.app.StatsManager.StatsUnavailableException;
     method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getStatsMetadata() throws android.app.StatsManager.StatsUnavailableException;
+    method public void registerPullAtomCallback(int, @Nullable android.app.StatsManager.PullAtomMetadata, @NonNull java.util.concurrent.Executor, @NonNull android.app.StatsManager.StatsPullAtomCallback);
     method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void removeConfig(long) throws android.app.StatsManager.StatsUnavailableException;
     method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean removeConfiguration(long);
     method @NonNull @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long[] setActiveConfigsChangedOperation(@Nullable android.app.PendingIntent) throws android.app.StatsManager.StatsUnavailableException;
@@ -668,6 +669,7 @@
     method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent);
     method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setDataFetchOperation(long, android.app.PendingIntent);
     method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void setFetchReportsOperation(android.app.PendingIntent, long) throws android.app.StatsManager.StatsUnavailableException;
+    method public void unregisterPullAtomCallback(int);
     field public static final String ACTION_STATSD_STARTED = "android.app.action.STATSD_STARTED";
     field public static final String EXTRA_STATS_ACTIVE_CONFIG_KEYS = "android.app.extra.STATS_ACTIVE_CONFIG_KEYS";
     field public static final String EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES = "android.app.extra.STATS_BROADCAST_SUBSCRIBER_COOKIES";
@@ -676,6 +678,23 @@
     field public static final String EXTRA_STATS_DIMENSIONS_VALUE = "android.app.extra.STATS_DIMENSIONS_VALUE";
     field public static final String EXTRA_STATS_SUBSCRIPTION_ID = "android.app.extra.STATS_SUBSCRIPTION_ID";
     field public static final String EXTRA_STATS_SUBSCRIPTION_RULE_ID = "android.app.extra.STATS_SUBSCRIPTION_RULE_ID";
+    field public static final int PULL_SKIP = 1; // 0x1
+    field public static final int PULL_SUCCESS = 0; // 0x0
+  }
+
+  public static class StatsManager.PullAtomMetadata {
+  }
+
+  public static class StatsManager.PullAtomMetadata.Builder {
+    ctor public StatsManager.PullAtomMetadata.Builder();
+    method @NonNull public android.app.StatsManager.PullAtomMetadata build();
+    method @NonNull public android.app.StatsManager.PullAtomMetadata.Builder setAdditiveFields(@NonNull int[]);
+    method @NonNull public android.app.StatsManager.PullAtomMetadata.Builder setCoolDownNs(long);
+    method @NonNull public android.app.StatsManager.PullAtomMetadata.Builder setTimeoutNs(long);
+  }
+
+  public static interface StatsManager.StatsPullAtomCallback {
+    method public int onPullAtom(int, @NonNull java.util.List<android.util.StatsEvent>);
   }
 
   public static class StatsManager.StatsUnavailableException extends android.util.AndroidException {
@@ -4660,7 +4679,10 @@
 
   public final class Tuner implements java.lang.AutoCloseable {
     ctor public Tuner(@NonNull android.content.Context);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void clearOnTuneEventListener();
     method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.Tuner.Descrambler openDescrambler();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int stopTune();
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int tune(@NonNull android.media.tv.tuner.FrontendSettings);
   }
 
@@ -4742,6 +4764,17 @@
 
 }
 
+package android.media.tv.tuner.frontend {
+
+  public interface OnTuneEventListener {
+    method public void onTuneEvent(int);
+    field public static final int SIGNAL_LOCKED = 0; // 0x0
+    field public static final int SIGNAL_LOST_LOCK = 2; // 0x2
+    field public static final int SIGNAL_NO_SIGNAL = 1; // 0x1
+  }
+
+}
+
 package android.metrics {
 
   public class LogMaker {
@@ -6205,6 +6238,7 @@
     method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoin(int, boolean);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoinPasspoint(@NonNull String, boolean);
+    method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void clearWifiConnectedNetworkScorer();
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener);
@@ -6249,6 +6283,7 @@
     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(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public boolean setWifiConnectedNetworkScorer(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.WifiConnectedNetworkScorer);
     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);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeInitiator(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startLocalOnlyHotspot(@NonNull android.net.wifi.SoftApConfiguration, @Nullable java.util.concurrent.Executor, @Nullable android.net.wifi.WifiManager.LocalOnlyHotspotCallback);
@@ -6338,6 +6373,11 @@
     method public void onWifiUsabilityStats(int, boolean, @NonNull android.net.wifi.WifiUsabilityStatsEntry);
   }
 
+  public static interface WifiManager.ScoreChangeCallback {
+    method public void onStatusChange(int, boolean);
+    method public void onTriggerUpdateOfWifiUsabilityStats(int);
+  }
+
   public static interface WifiManager.SoftApCallback {
     method public default void onBlockedClientConnecting(@NonNull android.net.wifi.WifiClient, int);
     method public default void onCapabilityChanged(@NonNull android.net.wifi.SoftApCapability);
@@ -6354,6 +6394,12 @@
     field public static final int DATA_ACTIVITY_OUT = 2; // 0x2
   }
 
+  public static interface WifiManager.WifiConnectedNetworkScorer {
+    method public void setScoreChangeCallback(@NonNull android.net.wifi.WifiManager.ScoreChangeCallback);
+    method public void start(int);
+    method public void stop(int);
+  }
+
   public class WifiNetworkConnectionStatistics implements android.os.Parcelable {
     ctor public WifiNetworkConnectionStatistics(int, int);
     ctor public WifiNetworkConnectionStatistics();
@@ -6730,6 +6776,15 @@
 
 package android.net.wifi.wificond {
 
+  public final class DeviceWiphyCapabilities implements android.os.Parcelable {
+    ctor public DeviceWiphyCapabilities();
+    method public int describeContents();
+    method public boolean isWifiStandardSupported(int);
+    method public void setWifiStandardSupport(int, boolean);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.wificond.DeviceWiphyCapabilities> CREATOR;
+  }
+
   public final class NativeScanResult implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public byte[] getBssid();
@@ -6794,6 +6849,7 @@
     method public void abortScan(@NonNull String);
     method public void enableVerboseLogging(boolean);
     method @NonNull public int[] getChannelsMhzForBand(int);
+    method @Nullable public android.net.wifi.wificond.DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String);
     method @NonNull public java.util.List<android.net.wifi.wificond.NativeScanResult> getScanResults(@NonNull String, int);
     method @Nullable public android.net.wifi.wificond.WifiCondManager.TxPacketCounters getTxPacketCounters(@NonNull String);
     method public boolean initialize(@NonNull Runnable);
@@ -10681,6 +10737,7 @@
     method public boolean isDataConnectivityPossible();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isGlobalModeEnabled();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isManualNetworkSelectionAllowed();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index a11f41f..bc7e1e5 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -4342,6 +4342,9 @@
                     mHistoricalUidOps.removeAt(i);
                 } else {
                     uidOp.filter(packageName, featureId, opNames, filter, scaleFactor);
+                    if (uidOp.getPackageCount() == 0) {
+                        mHistoricalUidOps.removeAt(i);
+                    }
                 }
             }
         }
@@ -4681,6 +4684,9 @@
                     mHistoricalPackageOps.removeAt(i);
                 } else {
                     packageOps.filter(featureId, opNames, filter, fractionToRemove);
+                    if (packageOps.getFeatureCount() == 0) {
+                        mHistoricalPackageOps.removeAt(i);
+                    }
                 }
             }
         }
@@ -4930,6 +4936,9 @@
                     mHistoricalFeatureOps.removeAt(i);
                 } else {
                     featureOps.filter(opNames, filter, fractionToRemove);
+                    if (featureOps.getOpCount() == 0) {
+                        mHistoricalFeatureOps.removeAt(i);
+                    }
                 }
             }
         }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 7af7a4a..f6014e5 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -95,7 +95,7 @@
     void updateNotificationChannelForPackage(String pkg, int uid, in NotificationChannel channel);
     NotificationChannel getNotificationChannel(String callingPkg, int userId, String pkg, String channelId);
     NotificationChannel getConversationNotificationChannel(String callingPkg, int userId, String pkg, String channelId, boolean returnParentIfNoConversationChannel, String conversationId);
-    void createConversationNotificationChannelForPackage(String pkg, int uid, in NotificationChannel parentChannel, String conversationId);
+    void createConversationNotificationChannelForPackage(String pkg, int uid, String triggeringKey, in NotificationChannel parentChannel, String conversationId);
     NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, boolean includeDeleted);
     void deleteNotificationChannel(String pkg, String channelId);
     void deleteConversationNotificationChannels(String pkg, int uid, String conversationId);
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/StatsManager.java b/core/java/android/app/StatsManager.java
index dde6dda..0beceb0 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -18,11 +18,13 @@
 import static android.Manifest.permission.DUMP;
 import static android.Manifest.permission.PACKAGE_USAGE_STATS;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.Context;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.IPullAtomCallback;
 import android.os.IPullAtomResultReceiver;
@@ -108,13 +110,11 @@
     /**
      * Value indicating that this pull was successful and that the result should be used.
      *
-     * @hide
      **/
     public static final int PULL_SUCCESS = 0;
 
     /**
      * Value indicating that this pull was unsuccessful and that the result should not be used.
-     * @hide
      **/
     public static final int PULL_SKIP = 1;
 
@@ -512,6 +512,17 @@
 
 
     /**
+     * Temp registration for while the migration is in progress.
+     *
+     * @hide
+     */
+    public void registerPullAtomCallback(int atomTag, @Nullable PullAtomMetadata metadata,
+            @NonNull StatsPullAtomCallback callback,
+            @NonNull @CallbackExecutor Executor executor) {
+        registerPullAtomCallback(atomTag, metadata, executor, callback);
+    }
+
+    /**
      * Registers a callback for an atom when that atom is to be pulled. The stats service will
      * invoke pullData in the callback when the stats service determines that this atom needs to be
      * pulled.
@@ -520,18 +531,19 @@
      * @param metadata          Optional metadata specifying the timeout, cool down time, and
      *                          additive fields for mapping isolated to host uids.
      * @param callback          The callback to be invoked when the stats service pulls the atom.
-     * @param executor          The executor in which to run the callback
+     * @param executor          The executor in which to run the callback.
      *
-     * @hide
      */
     public void registerPullAtomCallback(int atomTag, @Nullable PullAtomMetadata metadata,
-            @NonNull StatsPullAtomCallback callback, @NonNull Executor executor) {
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull StatsPullAtomCallback callback) {
         long coolDownNs = metadata == null ? DEFAULT_COOL_DOWN_NS : metadata.mCoolDownNs;
         long timeoutNs = metadata == null ? DEFAULT_TIMEOUT_NS : metadata.mTimeoutNs;
         int[] additiveFields = metadata == null ? new int[0] : metadata.mAdditiveFields;
         if (additiveFields == null) {
             additiveFields = new int[0];
         }
+
         synchronized (sLock) {
             try {
                 IStatsManagerService service = getIStatsManagerServiceLocked();
@@ -551,7 +563,6 @@
      *
      * @param atomTag           The tag of the atom of which to unregister
      *
-     * @hide
      */
     public void unregisterPullAtomCallback(int atomTag) {
         synchronized (sLock) {
@@ -577,21 +588,26 @@
 
         @Override
         public void onPullAtom(int atomTag, IPullAtomResultReceiver resultReceiver) {
-            mExecutor.execute(() -> {
-                List<StatsEvent> data = new ArrayList<>();
-                int successInt = mCallback.onPullAtom(atomTag, data);
-                boolean success = successInt == PULL_SUCCESS;
-                StatsEventParcel[] parcels = new StatsEventParcel[data.size()];
-                for (int i = 0; i < data.size(); i++) {
-                    parcels[i] = new StatsEventParcel();
-                    parcels[i].buffer = data.get(i).getBytes();
-                }
-                try {
-                    resultReceiver.pullFinished(atomTag, success, parcels);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "StatsPullResultReceiver failed for tag " + mAtomId);
-                }
-            });
+            long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> {
+                    List<StatsEvent> data = new ArrayList<>();
+                    int successInt = mCallback.onPullAtom(atomTag, data);
+                    boolean success = successInt == PULL_SUCCESS;
+                    StatsEventParcel[] parcels = new StatsEventParcel[data.size()];
+                    for (int i = 0; i < data.size(); i++) {
+                        parcels[i] = new StatsEventParcel();
+                        parcels[i].buffer = data.get(i).getBytes();
+                    }
+                    try {
+                        resultReceiver.pullFinished(atomTag, success, parcels);
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "StatsPullResultReceiver failed for tag " + mAtomId);
+                    }
+                });
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
     }
 
@@ -599,7 +615,6 @@
      * Metadata required for registering a StatsPullAtomCallback.
      * All fields are optional, and defaults will be used for fields that are unspecified.
      *
-     * @hide
      */
     public static class PullAtomMetadata {
         private final long mCoolDownNs;
@@ -614,22 +629,27 @@
         }
 
         /**
-         * Returns a new PullAtomMetadata.Builder object for constructing PullAtomMetadata for
-         * StatsManager#registerPullAtomCallback
+         * Temp for while migrations are in progress.
+         *
+         * @hide
          */
         public static PullAtomMetadata.Builder newBuilder() {
             return new PullAtomMetadata.Builder();
         }
 
         /**
-         * Builder for PullAtomMetadata.
+         *  Builder for PullAtomMetadata.
          */
         public static class Builder {
             private long mCoolDownNs;
             private long mTimeoutNs;
             private int[] mAdditiveFields;
 
-            private Builder() {
+            /**
+             * Returns a new PullAtomMetadata.Builder object for constructing PullAtomMetadata for
+             * StatsManager#registerPullAtomCallback
+             */
+            public Builder() {
                 mCoolDownNs = DEFAULT_COOL_DOWN_NS;
                 mTimeoutNs = DEFAULT_TIMEOUT_NS;
                 mAdditiveFields = null;
@@ -662,7 +682,7 @@
              * will be combined when the non-additive fields are the same.
              */
             @NonNull
-            public Builder setAdditiveFields(int[] additiveFields) {
+            public Builder setAdditiveFields(@NonNull int[] additiveFields) {
                 mAdditiveFields = additiveFields;
                 return this;
             }
@@ -705,14 +725,13 @@
     /**
      * Callback interface for pulling atoms requested by the stats service.
      *
-     * @hide
      */
     public interface StatsPullAtomCallback {
         /**
          * Pull data for the specified atom tag, filling in the provided list of StatsEvent data.
          * @return {@link #PULL_SUCCESS} if the pull was successful, or {@link #PULL_SKIP} if not.
          */
-        int onPullAtom(int atomTag, List<StatsEvent> data);
+        int onPullAtom(int atomTag, @NonNull List<StatsEvent> data);
     }
 
     private class StatsdDeathRecipient implements IBinder.DeathRecipient {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index be8e1d6..54a64ef 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -11493,4 +11493,48 @@
         }
         return Collections.emptyList();
     }
+
+    /**
+     * Called by device owner or profile owner of an organization-owned managed profile to toggle
+     * Common Criteria mode for the device. When the device is in Common Criteria mode,
+     * certain device functionalities are tuned to meet the higher
+     * security level required by Common Criteria certification. For example:
+     * <ul>
+     * <li> Bluetooth long term key material is additionally integrity-protected with AES-GCM. </li>
+     * <li> WiFi configuration store is additionally integrity-protected with AES-GCM. </li>
+     * </ul>
+     * Common Criteria mode is disabled by default.
+     *
+     * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+     * @param enabled whether Common Criteria mode should be enabled or not.
+     */
+    public void setCommonCriteriaModeEnabled(@NonNull ComponentName admin, boolean enabled) {
+        throwIfParentInstance("setCommonCriteriaModeEnabled");
+        if (mService != null) {
+            try {
+                mService.setCommonCriteriaModeEnabled(admin, enabled);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Called by device owner or profile owner of an organization-owned managed profile to return
+     * whether Common Criteria mode is currently enabled for the device.
+     *
+     * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+     * @return {@code true} if Common Criteria mode is enabled, {@code false} otherwise.
+     */
+    public boolean isCommonCriteriaModeEnabled(@NonNull ComponentName admin) {
+        throwIfParentInstance("isCommonCriteriaModeEnabled");
+        if (mService != null) {
+            try {
+                return mService.isCommonCriteriaModeEnabled(admin);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 21c9eb5..f649286 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -461,4 +461,7 @@
     void setProtectedPackages(in ComponentName admin, in List<String> packages);
 
     List<String> getProtectedPackages(in ComponentName admin);
+
+    void setCommonCriteriaModeEnabled(in ComponentName admin, boolean enabled);
+    boolean isCommonCriteriaModeEnabled(in ComponentName admin);
 }
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 679de8a..3df94a7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -30,6 +30,7 @@
 import android.annotation.StringRes;
 import android.annotation.StyleRes;
 import android.annotation.StyleableRes;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UserIdInt;
@@ -3930,6 +3931,7 @@
      */
     @SystemApi
     @TestApi
+    @SuppressLint("ServiceName")
     public static final String STATUS_BAR_SERVICE = "statusbar";
 
     /**
@@ -4024,6 +4026,7 @@
     public static final String NETWORK_STATS_SERVICE = "netstats";
     /** {@hide} */
     @SystemApi
+    @SuppressLint("ServiceName")
     public static final String NETWORK_POLICY_SERVICE = "netpolicy";
     /** {@hide} */
     public static final String NETWORK_WATCHLIST_SERVICE = "network_watchlist";
@@ -4048,6 +4051,7 @@
      * @hide
      */
     @SystemApi
+    @SuppressLint("ServiceName")
     public static final String WIFI_COND_SERVICE = "wificond";
 
     /**
@@ -4379,6 +4383,7 @@
      * @see #getSystemService(String)
      */
     @TestApi
+    @SuppressLint("ServiceName")  // TODO: This should be renamed to CONTENT_CAPTURE_SERVICE
     public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
 
     /**
@@ -4474,6 +4479,7 @@
      * @hide
      */
     @TestApi
+    @SuppressLint("ServiceName")  // TODO: This should be renamed to DEVICE_IDLE_SERVICE
     public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
 
     /**
@@ -4483,6 +4489,7 @@
      * @hide
      */
     @TestApi
+    @SuppressLint("ServiceName")  // TODO: This should be renamed to POWER_WHITELIST_SERVICE
     public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
 
     /**
@@ -5053,6 +5060,7 @@
      * @hide
      */
     @SystemApi
+    @SuppressLint("ServiceName")
     public static final String BATTERY_STATS_SERVICE = "batterystats";
 
     /**
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/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index edaaf81..33d6131 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -113,7 +113,7 @@
     boolean isUserRunning(int userId);
     boolean isUserNameSet(int userId);
     boolean hasRestrictedProfiles();
-    boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userId, in IntentSender target);
+    boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userId, in IntentSender target, int flags);
     String getUserName();
     long getUserStartRealtime();
     long getUserUnlockRealtime();
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 82b04a6..0414b14 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -373,9 +373,15 @@
     public static final int GO_TO_SLEEP_REASON_INATTENTIVE = 9;
 
     /**
+     * Go to sleep reason code: Going to sleep due to quiescent boot.
      * @hide
      */
-    public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_INATTENTIVE;
+    public static final int GO_TO_SLEEP_REASON_QUIESCENT = 10;
+
+    /**
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_QUIESCENT;
 
     /**
      * @hide
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 2eaefca..12e843c 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -133,6 +133,22 @@
     public static final String USER_TYPE_SYSTEM_HEADLESS = "android.os.usertype.system.HEADLESS";
 
     /**
+     * Flag passed to {@link #requestQuietModeEnabled} to request disabling quiet mode only if
+     * there is no need to confirm the user credentials. If credentials are required to disable
+     * quiet mode, {@link #requestQuietModeEnabled} will do nothing and return {@code false}.
+     */
+    public static final int QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED = 0x1;
+
+    /**
+     * List of flags available for the {@link #requestQuietModeEnabled} method.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "QUIET_MODE_" }, value = {
+            QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED })
+    public @interface QuietModeFlag {}
+
+    /**
      * @hide
      * No user restriction.
      */
@@ -3216,6 +3232,25 @@
     }
 
     /**
+     * Perform the same operation as {@link #requestQuietModeEnabled(boolean, UserHandle)}, but
+     * with a flag to tweak the behavior of the request.
+     *
+     * @param enableQuietMode whether quiet mode should be enabled or disabled
+     * @param userHandle user handle of the profile
+     * @param flags Can be 0 or {@link #QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED}.
+     * @return {@code false} if user's credential is needed in order to turn off quiet mode,
+     *         {@code true} otherwise
+     * @throws SecurityException if the caller is invalid
+     * @throws IllegalArgumentException if {@code userHandle} is not a managed profile
+     *
+     * @see #isQuietModeEnabled(UserHandle)
+     */
+    public boolean requestQuietModeEnabled(boolean enableQuietMode, @NonNull UserHandle userHandle,
+            @QuietModeFlag int flags) {
+        return requestQuietModeEnabled(enableQuietMode, userHandle, null, flags);
+    }
+
+    /**
      * Similar to {@link #requestQuietModeEnabled(boolean, UserHandle)}, except you can specify
      * a target to start when user is unlocked. If {@code target} is specified, caller must have
      * the {@link android.Manifest.permission#MANAGE_USERS} permission.
@@ -3225,9 +3260,23 @@
      */
     public boolean requestQuietModeEnabled(
             boolean enableQuietMode, @NonNull UserHandle userHandle, IntentSender target) {
+        return requestQuietModeEnabled(enableQuietMode, userHandle, target, 0);
+    }
+    /**
+     * Similar to {@link #requestQuietModeEnabled(boolean, UserHandle)}, except you can specify
+     * a target to start when user is unlocked. If {@code target} is specified, caller must have
+     * the {@link android.Manifest.permission#MANAGE_USERS} permission.
+     *
+     * @see {@link #requestQuietModeEnabled(boolean, UserHandle)}
+     * @hide
+     */
+    public boolean requestQuietModeEnabled(
+            boolean enableQuietMode, @NonNull UserHandle userHandle, IntentSender target,
+            int flags) {
         try {
             return mService.requestQuietModeEnabled(
-                    mContext.getPackageName(), enableQuietMode, userHandle.getIdentifier(), target);
+                    mContext.getPackageName(), enableQuietMode, userHandle.getIdentifier(), target,
+                    flags);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index f0a1174..3ea64f1 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -876,14 +876,7 @@
      */
     public @NonNull UUID getUuidForPath(@NonNull File path) throws IOException {
         Preconditions.checkNotNull(path);
-        String pathString = path.getCanonicalPath();
-        if (path.getPath().startsWith("/sdcard")) {
-            // On FUSE enabled devices, realpath(2) /sdcard is /mnt/user/<userid>/emulated/<userid>
-            // as opposed to /storage/emulated/<userid>.
-            // And vol.path below expects to match with a path starting with /storage
-            pathString = pathString.replaceFirst("^/mnt/user/[0-9]+/", "/storage/");
-        }
-
+        final String pathString = path.getCanonicalPath();
         if (FileUtils.contains(Environment.getDataDirectory().getAbsolutePath(), pathString)) {
             return UUID_DEFAULT;
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f6633201..0e3dd3a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -13929,6 +13929,19 @@
          */
         public static final String POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE =
                 "power_button_suppression_delay_after_gesture_wake";
+
+        /**
+         * An integer indicating whether the device is in Common Criteria mode. When enabled,
+         * certain device functionalities are tuned to meet the higher security level required
+         * by Common Criteria certification. Examples include:
+         *   Bluetooth long term key material is additionally integrity-protected with AES-GCM.
+         *   WiFi configuration store is additionally integrity-protected with AES-GCM.
+         * A value of 0 means Common Criteria mode is not enabled (default), a value of non-zero
+         * means Common Criteria mode is enabled.
+         * @hide
+         */
+        @SystemApi(client = SystemApi.Client.MODULE_APPS)
+        public static final String COMMON_CRITERIA_MODE = "common_criteria_mode";
     }
 
     /**
@@ -14360,46 +14373,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/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 389040c..4f400a8 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -16,6 +16,9 @@
 
 package android.service.notification;
 
+import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
+import static android.util.FeatureFlagUtils.*;
+
 import android.annotation.NonNull;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -29,6 +32,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.FeatureFlagUtils;
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -436,6 +441,19 @@
         return logMaker;
     }
 
+    /**
+     * @hide
+     */
+    public String getShortcutId(Context context) {
+        String conversationId = getNotification().getShortcutId();
+        if (isEnabled(context,  NOTIF_CONVO_BYPASS_SHORTCUT_REQ)
+                && getNotification().getNotificationStyle() == Notification.MessagingStyle.class
+                && TextUtils.isEmpty(conversationId)) {
+            conversationId = getId() + getTag() + PLACEHOLDER_CONVERSATION_ID;
+        }
+        return conversationId;
+    }
+
     private String getGroupLogTag() {
         return shortenTag(getGroup());
     }
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/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index cc4278b..38416ee 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -159,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
@@ -1482,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
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/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/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index c979133..041019e 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -102,6 +102,47 @@
     }
 };
 
+class TunerConfigurationHelper {
+    JNIEnv *const mEnv;
+    jobject const mTunerConfiguration;
+
+    struct Ids {
+        Ids(JNIEnv *env)
+              : mClass(FindClassOrDie(env, "android/media/AudioTrack$TunerConfiguration")),
+                mContentId(GetFieldIDOrDie(env, mClass, "mContentId", "I")),
+                mSyncId(GetFieldIDOrDie(env, mClass, "mSyncId", "I")) {}
+        const jclass mClass;
+        const jfieldID mContentId;
+        const jfieldID mSyncId;
+    };
+
+    static const Ids &getIds(JNIEnv *env) {
+        // Meyer's singleton, initializes first time control passes through
+        // declaration in a block and is thread-safe per ISO/IEC 14882:2011 6.7.4.
+        static Ids ids(env);
+        return ids;
+    }
+
+public:
+    TunerConfigurationHelper(JNIEnv *env, jobject tunerConfiguration)
+          : mEnv(env), mTunerConfiguration(tunerConfiguration) {}
+
+    int32_t getContentId() const {
+        if (mEnv == nullptr || mTunerConfiguration == nullptr) return 0;
+        const Ids &ids = getIds(mEnv);
+        return (int32_t)mEnv->GetIntField(mTunerConfiguration, ids.mContentId);
+    }
+
+    int32_t getSyncId() const {
+        if (mEnv == nullptr || mTunerConfiguration == nullptr) return 0;
+        const Ids &ids = getIds(mEnv);
+        return (int32_t)mEnv->GetIntField(mTunerConfiguration, ids.mSyncId);
+    }
+
+    // optional check to confirm class and field ids can be found.
+    static void initCheckOrDie(JNIEnv *env) { (void)getIds(env); }
+};
+
 static Mutex sLock;
 static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies;
 
@@ -213,24 +254,36 @@
 }
 
 // ----------------------------------------------------------------------------
-static jint
-android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa,
-        jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask,
-        jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,
-        jlong nativeAudioTrack, jboolean offload) {
-
+static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this,
+                                           jobject jaa, jintArray jSampleRate,
+                                           jint channelPositionMask, jint channelIndexMask,
+                                           jint audioFormat, jint buffSizeInBytes, jint memoryMode,
+                                           jintArray jSession, jlong nativeAudioTrack,
+                                           jboolean offload, jint encapsulationMode,
+                                           jobject tunerConfiguration) {
     ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d,"
-        " nativeAudioTrack=0x%" PRIX64 ", offload=%d",
-        jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,
-        nativeAudioTrack, offload);
-
-    sp<AudioTrack> lpTrack = 0;
+          " nativeAudioTrack=0x%" PRIX64 ", offload=%d encapsulationMode=%d tuner=%p",
+          jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,
+          nativeAudioTrack, offload, encapsulationMode, tunerConfiguration);
 
     if (jSession == NULL) {
         ALOGE("Error creating AudioTrack: invalid session ID pointer");
         return (jint) AUDIO_JAVA_ERROR;
     }
 
+    // TODO: replace when we land matching AudioTrack::set() in frameworks/av in r or r-tv-dev.
+    if (tunerConfiguration != nullptr) {
+        const TunerConfigurationHelper tunerHelper(env, tunerConfiguration);
+        ALOGE("Error creating AudioTrack: unsupported tuner contentId:%d syncId:%d",
+              tunerHelper.getContentId(), tunerHelper.getSyncId());
+        return (jint)AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
+    }
+    // TODO: replace when we land matching AudioTrack::set() in frameworks/av in r or r-tv-dev.
+    if (encapsulationMode != 0 /* ENCAPSULATION_MODE_NONE */) {
+        ALOGE("Error creating AudioTrack: unsupported encapsulationMode %d", encapsulationMode);
+        return (jint)AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
+    }
+
     jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
     if (nSession == NULL) {
         ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
@@ -249,6 +302,7 @@
     }
 
     // if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one.
+    sp<AudioTrack> lpTrack;
     if (nativeAudioTrack == 0) {
         if (jaa == 0) {
             ALOGE("Error creating AudioTrack: invalid audio attributes");
@@ -1304,82 +1358,75 @@
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 static const JNINativeMethod gMethods[] = {
-    // name,              signature,     funcPtr
-    {"native_is_direct_output_supported",
-                             "(IIIIIII)Z",
-                                         (void *)android_media_AudioTrack_is_direct_output_supported},
-    {"native_start",         "()V",      (void *)android_media_AudioTrack_start},
-    {"native_stop",          "()V",      (void *)android_media_AudioTrack_stop},
-    {"native_pause",         "()V",      (void *)android_media_AudioTrack_pause},
-    {"native_flush",         "()V",      (void *)android_media_AudioTrack_flush},
-    {"native_setup",     "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZ)I",
-                                         (void *)android_media_AudioTrack_setup},
-    {"native_finalize",      "()V",      (void *)android_media_AudioTrack_finalize},
-    {"native_release",       "()V",      (void *)android_media_AudioTrack_release},
-    {"native_write_byte",    "([BIIIZ)I",(void *)android_media_AudioTrack_writeArray<jbyteArray>},
-    {"native_write_native_bytes",
-                             "(Ljava/nio/ByteBuffer;IIIZ)I",
-                                         (void *)android_media_AudioTrack_write_native_bytes},
-    {"native_write_short",   "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>},
-    {"native_write_float",   "([FIIIZ)I",(void *)android_media_AudioTrack_writeArray<jfloatArray>},
-    {"native_setVolume",     "(FF)V",    (void *)android_media_AudioTrack_set_volume},
-    {"native_get_buffer_size_frames",
-                             "()I",      (void *)android_media_AudioTrack_get_buffer_size_frames},
-    {"native_set_buffer_size_frames",
-                             "(I)I",     (void *)android_media_AudioTrack_set_buffer_size_frames},
-    {"native_get_buffer_capacity_frames",
-                             "()I",      (void *)android_media_AudioTrack_get_buffer_capacity_frames},
-    {"native_set_playback_rate",
-                             "(I)I",     (void *)android_media_AudioTrack_set_playback_rate},
-    {"native_get_playback_rate",
-                             "()I",      (void *)android_media_AudioTrack_get_playback_rate},
-    {"native_set_playback_params",
-                             "(Landroid/media/PlaybackParams;)V",
-                                         (void *)android_media_AudioTrack_set_playback_params},
-    {"native_get_playback_params",
-                             "()Landroid/media/PlaybackParams;",
-                                         (void *)android_media_AudioTrack_get_playback_params},
-    {"native_set_marker_pos","(I)I",     (void *)android_media_AudioTrack_set_marker_pos},
-    {"native_get_marker_pos","()I",      (void *)android_media_AudioTrack_get_marker_pos},
-    {"native_set_pos_update_period",
-                             "(I)I",     (void *)android_media_AudioTrack_set_pos_update_period},
-    {"native_get_pos_update_period",
-                             "()I",      (void *)android_media_AudioTrack_get_pos_update_period},
-    {"native_set_position",  "(I)I",     (void *)android_media_AudioTrack_set_position},
-    {"native_get_position",  "()I",      (void *)android_media_AudioTrack_get_position},
-    {"native_get_latency",   "()I",      (void *)android_media_AudioTrack_get_latency},
-    {"native_get_underrun_count", "()I",      (void *)android_media_AudioTrack_get_underrun_count},
-    {"native_get_flags",     "()I",      (void *)android_media_AudioTrack_get_flags},
-    {"native_get_timestamp", "([J)I",    (void *)android_media_AudioTrack_get_timestamp},
-    {"native_getMetrics",    "()Landroid/os/PersistableBundle;",
-                                         (void *)android_media_AudioTrack_native_getMetrics},
-    {"native_set_loop",      "(III)I",   (void *)android_media_AudioTrack_set_loop},
-    {"native_reload_static", "()I",      (void *)android_media_AudioTrack_reload},
-    {"native_get_output_sample_rate",
-                             "(I)I",      (void *)android_media_AudioTrack_get_output_sample_rate},
-    {"native_get_min_buff_size",
-                             "(III)I",   (void *)android_media_AudioTrack_get_min_buff_size},
-    {"native_setAuxEffectSendLevel",
-                             "(F)I",     (void *)android_media_AudioTrack_setAuxEffectSendLevel},
-    {"native_attachAuxEffect",
-                             "(I)I",     (void *)android_media_AudioTrack_attachAuxEffect},
-    {"native_setOutputDevice", "(I)Z",
-                             (void *)android_media_AudioTrack_setOutputDevice},
-    {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
-    {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback},
-    {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback},
-    {"native_applyVolumeShaper",
-            "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
-                                         (void *)android_media_AudioTrack_apply_volume_shaper},
-    {"native_getVolumeShaperState",
-            "(I)Landroid/media/VolumeShaper$State;",
-                                        (void *)android_media_AudioTrack_get_volume_shaper_state},
-    {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation},
-    {"native_getPortId", "()I", (void *)android_media_AudioTrack_get_port_id},
-    {"native_set_delay_padding", "(II)V", (void *)android_media_AudioTrack_set_delay_padding},
+        // name,              signature,     funcPtr
+        {"native_is_direct_output_supported", "(IIIIIII)Z",
+         (void *)android_media_AudioTrack_is_direct_output_supported},
+        {"native_start", "()V", (void *)android_media_AudioTrack_start},
+        {"native_stop", "()V", (void *)android_media_AudioTrack_stop},
+        {"native_pause", "()V", (void *)android_media_AudioTrack_pause},
+        {"native_flush", "()V", (void *)android_media_AudioTrack_flush},
+        {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZILjava/lang/Object;)I",
+         (void *)android_media_AudioTrack_setup},
+        {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize},
+        {"native_release", "()V", (void *)android_media_AudioTrack_release},
+        {"native_write_byte", "([BIIIZ)I", (void *)android_media_AudioTrack_writeArray<jbyteArray>},
+        {"native_write_native_bytes", "(Ljava/nio/ByteBuffer;IIIZ)I",
+         (void *)android_media_AudioTrack_write_native_bytes},
+        {"native_write_short", "([SIIIZ)I",
+         (void *)android_media_AudioTrack_writeArray<jshortArray>},
+        {"native_write_float", "([FIIIZ)I",
+         (void *)android_media_AudioTrack_writeArray<jfloatArray>},
+        {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume},
+        {"native_get_buffer_size_frames", "()I",
+         (void *)android_media_AudioTrack_get_buffer_size_frames},
+        {"native_set_buffer_size_frames", "(I)I",
+         (void *)android_media_AudioTrack_set_buffer_size_frames},
+        {"native_get_buffer_capacity_frames", "()I",
+         (void *)android_media_AudioTrack_get_buffer_capacity_frames},
+        {"native_set_playback_rate", "(I)I", (void *)android_media_AudioTrack_set_playback_rate},
+        {"native_get_playback_rate", "()I", (void *)android_media_AudioTrack_get_playback_rate},
+        {"native_set_playback_params", "(Landroid/media/PlaybackParams;)V",
+         (void *)android_media_AudioTrack_set_playback_params},
+        {"native_get_playback_params", "()Landroid/media/PlaybackParams;",
+         (void *)android_media_AudioTrack_get_playback_params},
+        {"native_set_marker_pos", "(I)I", (void *)android_media_AudioTrack_set_marker_pos},
+        {"native_get_marker_pos", "()I", (void *)android_media_AudioTrack_get_marker_pos},
+        {"native_set_pos_update_period", "(I)I",
+         (void *)android_media_AudioTrack_set_pos_update_period},
+        {"native_get_pos_update_period", "()I",
+         (void *)android_media_AudioTrack_get_pos_update_period},
+        {"native_set_position", "(I)I", (void *)android_media_AudioTrack_set_position},
+        {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position},
+        {"native_get_latency", "()I", (void *)android_media_AudioTrack_get_latency},
+        {"native_get_underrun_count", "()I", (void *)android_media_AudioTrack_get_underrun_count},
+        {"native_get_flags", "()I", (void *)android_media_AudioTrack_get_flags},
+        {"native_get_timestamp", "([J)I", (void *)android_media_AudioTrack_get_timestamp},
+        {"native_getMetrics", "()Landroid/os/PersistableBundle;",
+         (void *)android_media_AudioTrack_native_getMetrics},
+        {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop},
+        {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload},
+        {"native_get_output_sample_rate", "(I)I",
+         (void *)android_media_AudioTrack_get_output_sample_rate},
+        {"native_get_min_buff_size", "(III)I", (void *)android_media_AudioTrack_get_min_buff_size},
+        {"native_setAuxEffectSendLevel", "(F)I",
+         (void *)android_media_AudioTrack_setAuxEffectSendLevel},
+        {"native_attachAuxEffect", "(I)I", (void *)android_media_AudioTrack_attachAuxEffect},
+        {"native_setOutputDevice", "(I)Z", (void *)android_media_AudioTrack_setOutputDevice},
+        {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
+        {"native_enableDeviceCallback", "()V",
+         (void *)android_media_AudioTrack_enableDeviceCallback},
+        {"native_disableDeviceCallback", "()V",
+         (void *)android_media_AudioTrack_disableDeviceCallback},
+        {"native_applyVolumeShaper",
+         "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
+         (void *)android_media_AudioTrack_apply_volume_shaper},
+        {"native_getVolumeShaperState", "(I)Landroid/media/VolumeShaper$State;",
+         (void *)android_media_AudioTrack_get_volume_shaper_state},
+        {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation},
+        {"native_getPortId", "()I", (void *)android_media_AudioTrack_get_port_id},
+        {"native_set_delay_padding", "(II)V", (void *)android_media_AudioTrack_set_delay_padding},
 };
 
-
 // field names found in android/media/AudioTrack.java
 #define JAVA_POSTEVENT_CALLBACK_NAME                    "postEventFromNative"
 #define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME            "mNativeTrackInJavaObj"
@@ -1436,6 +1483,10 @@
     gPlaybackParamsFields.init(env);
 
     gVolumeShaperFields.init(env);
+
+    // optional check that the TunerConfiguration class and fields exist.
+    TunerConfigurationHelper::initCheckOrDie(env);
+
     return res;
 }
 
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 573f378..50a60a9 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -826,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;
@@ -1400,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/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 0fca1d1..0ae11a1 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -155,4 +155,5 @@
   SET_AUTO_TIME_ZONE = 128;
   SET_PACKAGES_PROTECTED = 129;
   SET_FACTORY_RESET_PROTECTION = 130;
+  SET_COMMON_CRITERIA_MODE = 131;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f8c5166..6b27348 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -131,7 +131,6 @@
 
     <protected-broadcast android:name="android.os.action.SETTING_RESTORED" />
 
-    <protected-broadcast android:name="android.app.backup.intent.RUN" />
     <protected-broadcast android:name="android.app.backup.intent.CLEAR" />
     <protected-broadcast android:name="android.app.backup.intent.INIT" />
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a78195b..2585197 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3545,6 +3545,12 @@
     <!-- Whether the device supports quick settings and its associated APIs -->
     <bool name="config_quickSettingsSupported">true</bool>
 
+    <!-- Comma separated list of extra quick settings tiles to be added to the default set as
+         defined in SystemUi (com.android.systemui.R.string.quick_settings_tiles_default).
+         Custom tiles (TileService) must be specified as "custom(pkg_name/class_in_package)"
+         (without the quotes, both absolute and relative class works). -->
+    <string name="config_defaultExtraQuickSettingsTiles" translatable="false"></string>
+
     <!-- The component name, flattened to a string, for the default autofill service
          to  enabled for an user. This service must be trusted, as it can be activated
          without explicit consent of the user. If no autofill service with the
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 669b41e..379d0aa 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3409,6 +3409,7 @@
   <java-symbol type="string" name="etws_primary_default_message_others" />
 
   <java-symbol type="bool" name="config_quickSettingsSupported" />
+  <java-symbol type="string" name="config_defaultExtraQuickSettingsTiles" />
 
   <java-symbol type="style" name="Theme.DeviceDefault.QuickSettings" />
 
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/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/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 0ced68ef..4dbc79b 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -213,6 +213,36 @@
 
     private final static String TAG = "android.media.AudioTrack";
 
+    /** @hide */
+    @IntDef({
+        ENCAPSULATION_MODE_NONE,
+        ENCAPSULATION_MODE_ELEMENTARY_STREAM,
+        ENCAPSULATION_MODE_HANDLE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EncapsulationMode {}
+
+    // Important: The ENCAPSULATION_MODE values must be kept in sync with native header files.
+    /**
+     * This mode indicates no metadata encapsulation,
+     * which is the default mode for sending audio data
+     * through {@code AudioTrack}.
+     */
+    public static final int ENCAPSULATION_MODE_NONE = 0;
+    /**
+     * This mode indicates metadata encapsulation with an elementary stream payload.
+     * Both compressed and PCM format is allowed.
+     *
+     * TODO(b/147778408) Link: See the Android developers guide for more information.
+     */
+    public static final int ENCAPSULATION_MODE_ELEMENTARY_STREAM = 1;
+    /**
+     * This mode indicates metadata encapsulation with a handle payload.
+     * The handle is a 64 bit long, provided by the Tuner API.
+     *
+     * TODO(b/147778408) Link: Fill in Tuner API to obtain the handle.
+     */
+    public static final int ENCAPSULATION_MODE_HANDLE = 2;
 
     /** @hide */
     @IntDef({
@@ -592,11 +622,13 @@
     public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
             int mode, int sessionId)
                     throws IllegalArgumentException {
-        this(attributes, format, bufferSizeInBytes, mode, sessionId, false /*offload*/);
+        this(attributes, format, bufferSizeInBytes, mode, sessionId, false /*offload*/,
+                ENCAPSULATION_MODE_NONE, null /* tunerConfiguration */);
     }
 
     private AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
-            int mode, int sessionId, boolean offload)
+            int mode, int sessionId, boolean offload, int encapsulationMode,
+            @Nullable TunerConfiguration tunerConfiguration)
                     throws IllegalArgumentException {
         super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_JAM_AUDIOTRACK);
         // mState already == STATE_UNINITIALIZED
@@ -663,7 +695,7 @@
         int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
                 sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
                 mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/,
-                offload);
+                offload, encapsulationMode, tunerConfiguration);
         if (initResult != SUCCESS) {
             loge("Error code "+initResult+" when initializing AudioTrack.");
             return; // with mState == STATE_UNINITIALIZED
@@ -672,6 +704,8 @@
         mSampleRate = sampleRate[0];
         mSessionId = session[0];
 
+        // TODO: consider caching encapsulationMode and tunerConfiguration in the Java object.
+
         if ((mAttributes.getFlags() & AudioAttributes.FLAG_HW_AV_SYNC) != 0) {
             int frameSizeInBytes;
             if (AudioFormat.isEncodingLinearFrames(mAudioFormat)) {
@@ -745,7 +779,9 @@
                     0 /*mDataLoadMode - NA*/,
                     session,
                     nativeTrackInJavaObj,
-                    false /*offload*/);
+                    false /*offload*/,
+                    ENCAPSULATION_MODE_NONE,
+                    null /* tunerConfiguration */);
             if (initResult != SUCCESS) {
                 loge("Error code "+initResult+" when initializing AudioTrack.");
                 return; // with mState == STATE_UNINITIALIZED
@@ -758,6 +794,99 @@
     }
 
     /**
+     * TunerConfiguration is used to convey tuner information
+     * from the android.media.tv.Tuner API to AudioTrack construction.
+     *
+     * Use the Builder to construct the TunerConfiguration object,
+     * which is then used by the {@link AudioTrack.Builder} to create an AudioTrack.
+     */
+    public static class TunerConfiguration {
+        private final int mContentId;
+        private final int mSyncId;
+
+        private TunerConfiguration(int contentId, int syncId) {
+            mContentId = contentId;
+            mSyncId = syncId;
+        }
+
+        /**
+         * Returns the contentId.
+         */
+        public int getContentId() {
+            return mContentId;
+        }
+
+        /**
+         * Returns the syncId.
+         */
+        public int getSyncId() {
+            return mSyncId;
+        }
+
+        /**
+         * Builder class for {@link AudioTrack.TunerConfiguration} objects.
+         */
+        public static class Builder {
+            private int mContentId;
+            private int mSyncId;
+
+            /**
+             * Sets the contentId from the Tuner filter.
+             *
+             * @param contentId selects the audio stream to use.
+             *     See android.media.tv.tuner.filter.Filter#getId().
+             *     This is always a positive number.
+             *     TODO(b/147778408) Link to tuner filter doc when unhidden.
+             * @return the same Builder instance.
+             */
+            public @NonNull Builder setContentId(@IntRange(from = 1) int contentId) {
+                if (contentId < 1) {
+                    throw new IllegalArgumentException(
+                            "contentId " + contentId + " must be positive");
+                }
+                mContentId = contentId;
+                return this;
+            }
+
+            /**
+             * Sets the syncId from the Tuner filter.
+             *
+             * @param syncId selects the clock to use for synchronization
+             *     of audio with other streams such as video.
+             *     See android.media.tv.tuner.Tuner#getAvSyncHwId().
+             *     This is always a positive number.
+             *     TODO(b/147778408) Link to tuner filter doc when unhidden.
+             * @return the same Builder instance.
+             */
+            public @NonNull Builder setSyncId(@IntRange(from = 1) int syncId) {
+                if (syncId < 1) {
+                    throw new IllegalArgumentException("syncId " + syncId + " must be positive");
+                }
+                mSyncId = syncId;
+                return this;
+            }
+
+            /**
+             * Builds a {@link AudioTrack.TunerConfiguration} instance initialized with
+             * the parameters set on this {@code Builder}.
+             *
+             * @return a new successfully initialized {@link AudioTrack.TunerConfiguration}.
+             * @throws UnsupportedOperationException if the parameters set on the
+             *     {@code Builder} are incompatible.
+             */
+            public @NonNull TunerConfiguration build() {
+                if (mContentId < 1 || mSyncId < 1) {
+                    throw new UnsupportedOperationException(
+                            "contentId " + mContentId
+                            + " syncId " + mSyncId
+                            + " must be set");
+                }
+                return new TunerConfiguration(mContentId, mSyncId);
+            }
+        }
+    }
+
+    /**
      * Builder class for {@link AudioTrack} objects.
      * Use this class to configure and create an <code>AudioTrack</code> instance. By setting audio
      * attributes and audio format parameters, you indicate which of those vary from the default
@@ -799,10 +928,12 @@
         private AudioAttributes mAttributes;
         private AudioFormat mFormat;
         private int mBufferSizeInBytes;
+        private int mEncapsulationMode = ENCAPSULATION_MODE_NONE;
         private int mSessionId = AudioManager.AUDIO_SESSION_ID_GENERATE;
         private int mMode = MODE_STREAM;
         private int mPerformanceMode = PERFORMANCE_MODE_NONE;
         private boolean mOffload = false;
+        private TunerConfiguration mTunerConfiguration;
 
         /**
          * Constructs a new Builder with the default values as described above.
@@ -869,6 +1000,34 @@
         }
 
         /**
+         * Sets the encapsulation mode.
+         *
+         * Encapsulation mode allows metadata to be sent together with
+         * the audio data payload in a {@code ByteBuffer}.
+         * The data format is specified in the Android developers site.
+         *
+         * TODO(b/147778408) Link to doc page.
+         *
+         * @param encapsulationMode one of {@link AudioTrack#ENCAPSULATION_MODE_NONE},
+         *        {@link AudioTrack#ENCAPSULATION_MODE_ELEMENTARY_STREAM},
+         *        {@link AudioTrack#ENCAPSULATION_MODE_HANDLE}.
+         * @return the same Builder instance.
+         */
+        public @NonNull Builder setEncapsulationMode(@EncapsulationMode int encapsulationMode) {
+            switch (encapsulationMode) {
+                case ENCAPSULATION_MODE_NONE:
+                case ENCAPSULATION_MODE_ELEMENTARY_STREAM:
+                case ENCAPSULATION_MODE_HANDLE:
+                    mEncapsulationMode = encapsulationMode;
+                    break;
+                default:
+                    throw new IllegalArgumentException(
+                            "Invalid encapsulation mode " + encapsulationMode);
+            }
+            return this;
+        }
+
+        /**
          * Sets the mode under which buffers of audio data are transferred from the
          * {@link AudioTrack} to the framework.
          * @param mode one of {@link AudioTrack#MODE_STREAM}, {@link AudioTrack#MODE_STATIC}.
@@ -949,6 +1108,25 @@
         }
 
         /**
+         * Sets the tuner configuration for the {@code AudioTrack}.
+         *
+         * The {@link AudioTrack.TunerConfiguration} consists of parameters obtained from
+         * the Android TV tuner API which indicate the audio content stream id and the
+         * synchronization id for the {@code AudioTrack}.
+         *
+         * @param tunerConfiguration obtained by {@link AudioTrack.TunerConfiguration.Builder}.
+         * @return the same Builder instance.
+         */
+        public @NonNull Builder setTunerConfiguration(
+                @NonNull TunerConfiguration tunerConfiguration) {
+            if (tunerConfiguration == null) {
+                throw new IllegalArgumentException("tunerConfiguration is null");
+            }
+            mTunerConfiguration = tunerConfiguration;
+            return this;
+        }
+
+        /**
          * Builds an {@link AudioTrack} instance initialized with all the parameters set
          * on this <code>Builder</code>.
          * @return a new successfully initialized {@link AudioTrack} instance.
@@ -1003,6 +1181,8 @@
                 }
             }
 
+            // TODO: Check mEncapsulationMode compatibility with MODE_STATIC, etc?
+
             try {
                 // If the buffer size is not specified in streaming mode,
                 // use a single frame for the buffer size and let the
@@ -1012,7 +1192,8 @@
                             * mFormat.getBytesPerSample(mFormat.getEncoding());
                 }
                 final AudioTrack track = new AudioTrack(
-                        mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId, mOffload);
+                        mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId, mOffload,
+                        mEncapsulationMode, mTunerConfiguration);
                 if (track.getState() == STATE_UNINITIALIZED) {
                     // release is not necessary
                     throw new UnsupportedOperationException("Cannot create AudioTrack");
@@ -3595,7 +3776,7 @@
             Object /*AudioAttributes*/ attributes,
             int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
             int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack,
-            boolean offload);
+            boolean offload, int encapsulationMode, Object tunerConfiguration);
 
     private native final void native_finalize();
 
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index aa38e51..a25aff6 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -18,13 +18,15 @@
 
 import android.content.Intent;
 import android.media.IMediaRoute2ProviderClient;
+import android.os.Bundle;
 
 /**
  * {@hide}
  */
 oneway interface IMediaRoute2Provider {
     void setClient(IMediaRoute2ProviderClient client);
-    void requestCreateSession(String packageName, String routeId, long requestId);
+    void requestCreateSession(String packageName, String routeId, long requestId,
+            in @nullable Bundle sessionHints);
     void releaseSession(String sessionId);
 
     void selectRoute(String sessionId, String routeId);
diff --git a/media/java/android/media/IMediaRouter2Manager.aidl b/media/java/android/media/IMediaRouter2Manager.aidl
index e9add17..ffad659 100644
--- a/media/java/android/media/IMediaRouter2Manager.aidl
+++ b/media/java/android/media/IMediaRouter2Manager.aidl
@@ -18,12 +18,14 @@
 
 import android.media.MediaRoute2ProviderInfo;
 import android.media.MediaRoute2Info;
+import android.media.RoutingSessionInfo;
 
 /**
  * {@hide}
  */
 oneway interface IMediaRouter2Manager {
-    void notifyRouteSelected(String packageName, in MediaRoute2Info route);
+    void notifySessionCreated(in RoutingSessionInfo sessionInfo);
+    void notifySessionsUpdated();
     void notifyPreferredFeaturesChanged(String packageName, in List<String> preferredFeatures);
     void notifyRoutesAdded(in List<MediaRoute2Info> routes);
     void notifyRoutesRemoved(in List<MediaRoute2Info> routes);
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 2d3e185..281e7c6b 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -24,6 +24,7 @@
 import android.media.MediaRouterClientState;
 import android.media.RouteDiscoveryPreference;
 import android.media.RoutingSessionInfo;
+import android.os.Bundle;
 
 /**
  * {@hide}
@@ -52,7 +53,8 @@
     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, int requestId);
+    void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route, int requestId,
+            in @nullable Bundle sessionHints);
     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);
@@ -71,4 +73,12 @@
             in MediaRoute2Info route, int direction);
 
     List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager);
+    void selectClientRoute(IMediaRouter2Manager manager,
+            String sessionId, in MediaRoute2Info route);
+    void deselectClientRoute(IMediaRouter2Manager manager,
+            String sessionId, in MediaRoute2Info route);
+    void transferToClientRoute(IMediaRouter2Manager manager,
+            String sessionId, in MediaRoute2Info route);
+    void releaseClientSession(IMediaRouter2Manager manager, String sessionId);
+
 }
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 5a3de6d..6bfa851 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, long)}
-     * @see #onCreateSession(String, String, long)
+     *                  {@link #onCreateSession(String, String, long, Bundle)}
+     * @see #onCreateSession(String, String, long, Bundle)
      * @hide
      */
     public final void notifySessionCreated(@NonNull RoutingSessionInfo sessionInfo,
@@ -177,7 +177,6 @@
             }
             mSessionInfo.put(sessionInfo.getId(), sessionInfo);
         }
-        schedulePublishState();
 
         if (mClient == null) {
             return;
@@ -196,8 +195,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, long)}.
-     * @see #onCreateSession(String, String, long)
+     *                  {@link #onCreateSession(String, String, long, Bundle)}.
+     * @see #onCreateSession(String, String, long, Bundle)
      * @hide
      */
     public final void notifySessionCreationFailed(long requestId) {
@@ -290,6 +289,9 @@
      * @param packageName the package name of the application that selected the route
      * @param routeId the id of the route initially being connected
      * @param requestId the id of this session creation request
+     * @param sessionHints an optional bundle of app-specific arguments sent by
+     *                     {@link MediaRouter2}, or null if none. The contents of this bundle
+     *                     may affect the result of session creation.
      *
      * @see RoutingSessionInfo.Builder#Builder(String, String)
      * @see RoutingSessionInfo.Builder#addSelectedRoute(String)
@@ -297,7 +299,7 @@
      * @hide
      */
     public abstract void onCreateSession(@NonNull String packageName, @NonNull String routeId,
-            long requestId);
+            long requestId, @Nullable Bundle sessionHints);
 
     /**
      * Called when the session should be released. A client of the session or system can request
@@ -432,12 +434,14 @@
         }
 
         @Override
-        public void requestCreateSession(String packageName, String routeId, long requestId) {
+        public void requestCreateSession(String packageName, String routeId, long requestId,
+                @Nullable Bundle requestCreateSession) {
             if (!checkCallerisSystem()) {
                 return;
             }
             mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession,
-                    MediaRoute2ProviderService.this, packageName, routeId, requestId));
+                    MediaRoute2ProviderService.this, packageName, routeId, requestId,
+                    requestCreateSession));
         }
 
         @Override
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 971b08d..51d08ec 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -92,6 +92,7 @@
     @GuardedBy("sRouterLock")
     private boolean mShouldUpdateRoutes;
     private volatile List<MediaRoute2Info> mFilteredRoutes = Collections.emptyList();
+    private volatile OnCreateSessionListener mOnCreateSessionListener;
 
     /**
      * Gets an instance of the media router associated with the context.
@@ -281,6 +282,19 @@
     }
 
     /**
+     * Sets an {@link OnCreateSessionListener} to send hints when creating a session.
+     * To send the hints, listener should be set <em>BEFORE</em> calling
+     * {@link #requestCreateSession(MediaRoute2Info)}.
+     *
+     * @param listener A listener to send optional app-specific hints when creating a session.
+     *                 {@code null} for unset.
+     * @hide
+     */
+    public void setOnCreateSessionListener(@Nullable OnCreateSessionListener listener) {
+        mOnCreateSessionListener = listener;
+    }
+
+    /**
      * 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.
@@ -300,13 +314,24 @@
         SessionCreationRequest request = new SessionCreationRequest(requestId, route);
         mSessionCreationRequests.add(request);
 
+
+        OnCreateSessionListener listener = mOnCreateSessionListener;
+        Bundle sessionHints = null;
+        if (listener != null) {
+            sessionHints = listener.onCreateSession(route);
+            if (sessionHints != null) {
+                sessionHints = new Bundle(sessionHints);
+            }
+        }
+
         Client2 client;
         synchronized (sRouterLock) {
             client = mClient;
         }
         if (client != null) {
             try {
-                mMediaRouterService.requestCreateSession(client, route, requestId);
+                mMediaRouterService.requestCreateSession(client, route, requestId,
+                        sessionHints);
             } catch (RemoteException ex) {
                 Log.e(TAG, "Unable to request to create session.", ex);
                 mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler,
@@ -707,6 +732,34 @@
     }
 
     /**
+     * A listener interface to send an optional app-specific hints when creating a session.
+     *
+     * @hide
+     */
+    public interface OnCreateSessionListener {
+        /**
+         * Called when the {@link MediaRouter2} is about to request
+         * the media route provider service to create a session with the given route.
+         * The {@link Bundle} returned here will be sent to media route provider service as a hint
+         * for creating a session.
+         * <p>
+         * To send hints when creating the session, set this listener before calling
+         * {@link #requestCreateSession(MediaRoute2Info)}.
+         * <p>
+         * This will be called on the same thread which calls
+         * {@link #requestCreateSession(MediaRoute2Info)}.
+         *
+         * @param route The route to create session with
+         * @return An optional bundle of app-specific arguments to send to the provider,
+         *         or null if none. The contents of this bundle may affect the result of
+         *         session creation.
+         * @see MediaRoute2ProviderService#onCreateSession(String, String, long, Bundle)
+         */
+        @Nullable
+        Bundle onCreateSession(@NonNull MediaRoute2Info route);
+    }
+
+    /**
      * A class to control media routing session in media route provider.
      * For example, selecting/deselcting/transferring routes to session can be done through this
      * class. Instances are created by {@link MediaRouter2}.
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 7022933..5cb32d6 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -22,6 +22,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -131,7 +132,7 @@
         Objects.requireNonNull(callback, "callback must not be null");
 
         if (!mCallbackRecords.remove(new CallbackRecord(null, callback))) {
-            Log.w(TAG, "Ignore removing unknown callback. " + callback);
+            Log.w(TAG, "unregisterCallback: Ignore unknown callback. " + callback);
             return;
         }
 
@@ -175,6 +176,29 @@
         return routes;
     }
 
+    /**
+     * Gets routing controllers of an application with the given package name.
+     * If the application isn't running or it doesn't use {@link MediaRouter2}, an empty list
+     * will be returned.
+     */
+    @NonNull
+    public List<RoutingController> getRoutingControllers(@NonNull String packageName) {
+        Objects.requireNonNull(packageName, "packageName must not be null");
+
+        List<RoutingController> controllers = new ArrayList<>();
+
+        for (RoutingSessionInfo sessionInfo : getActiveSessions()) {
+            if (TextUtils.equals(sessionInfo.getClientPackageName(), packageName)) {
+                controllers.add(new RoutingController(sessionInfo));
+            }
+        }
+        return controllers;
+    }
+
+    /**
+     * Gets the list of all active routing sessions. It doesn't include default routing sessions
+     * of applications.
+     */
     @NonNull
     public List<RoutingSessionInfo> getActiveSessions() {
         Client client;
@@ -192,23 +216,7 @@
     }
 
     /**
-     * Gets the list of routes that are actively used by {@link MediaRouter2}.
-     */
-    @NonNull
-    public List<MediaRoute2Info> getActiveRoutes() {
-        List<MediaRoute2Info> routes = new ArrayList<>();
-        synchronized (mRoutesLock) {
-            for (MediaRoute2Info route : mRoutes.values()) {
-                if (!TextUtils.isEmpty(route.getClientPackageName())) {
-                    routes.add(route);
-                }
-            }
-        }
-        return routes;
-    }
-
-    /**
-     * Gets the list of discovered routes
+     * Gets the list of all discovered routes
      */
     @NonNull
     public List<MediaRoute2Info> getAllRoutes() {
@@ -222,6 +230,10 @@
     /**
      * Selects media route for the specified package name.
      *
+     * If the given route is {@link RoutingController#getTransferrableRoutes() a transferrable
+     * route} of a routing session of the application, the session will be transferred to
+     * the route. If not, a new routing session will be created.
+     *
      * @param packageName the package name of the application that should change it's media route
      * @param route the route to be selected.
      */
@@ -229,6 +241,13 @@
         Objects.requireNonNull(packageName, "packageName must not be null");
         Objects.requireNonNull(route, "route must not be null");
 
+        for (RoutingController controller : getRoutingControllers(packageName)) {
+            if (controller.getSessionInfo().getTransferrableRoutes().contains(route.getId())) {
+                controller.transferToRoute(route);
+                return;
+            }
+        }
+
         Client client;
         synchronized (sLock) {
             client = mClient;
@@ -238,6 +257,7 @@
                 int requestId = mNextRequestId.getAndIncrement();
                 mMediaRouterService.requestCreateClientSession(
                         client, packageName, route, requestId);
+                //TODO: release the previous session?
             } catch (RemoteException ex) {
                 Log.e(TAG, "Unable to select media route", ex);
             }
@@ -245,7 +265,7 @@
     }
 
     /**
-     * Requests a volume change for the route asynchronously.
+     * Requests a volume change for a route asynchronously.
      * <p>
      * It may have no effect if the route is currently not selected.
      * </p>
@@ -346,9 +366,16 @@
         }
     }
 
-    void notifyRouteSelected(String packageName, MediaRoute2Info route) {
+    void notifySessionCreated(RoutingSessionInfo sessionInfo) {
         for (CallbackRecord record : mCallbackRecords) {
-            record.mExecutor.execute(() -> record.mCallback.onRouteSelected(packageName, route));
+            record.mExecutor.execute(() -> record.mCallback.onSessionCreated(
+                    new RoutingController(sessionInfo)));
+        }
+    }
+
+    void notifySessionInfosChanged() {
+        for (CallbackRecord record : mCallbackRecords) {
+            record.mExecutor.execute(() -> record.mCallback.onSessionsUpdated());
         }
     }
 
@@ -365,6 +392,275 @@
     }
 
     /**
+     * @hide
+     */
+    public RoutingController getControllerForSession(@NonNull RoutingSessionInfo sessionInfo) {
+        return new RoutingController(sessionInfo);
+    }
+
+    /**
+     * A class to control media routing session in media route provider.
+     * With routing controller, an application can select a route into the session or deselect
+     * a route in the session.
+     */
+    public final class RoutingController {
+        private final Object mControllerLock = new Object();
+        @GuardedBy("mControllerLock")
+        private RoutingSessionInfo mSessionInfo;
+
+        RoutingController(@NonNull RoutingSessionInfo sessionInfo) {
+            mSessionInfo = sessionInfo;
+        }
+
+        /**
+         * Gets the ID of the session
+         */
+        @NonNull
+        public String getSessionId() {
+            synchronized (mControllerLock) {
+                return mSessionInfo.getId();
+            }
+        }
+
+        /**
+         * Gets the client package name of the session
+         */
+        @NonNull
+        public String getClientPackageName() {
+            synchronized (mControllerLock) {
+                return mSessionInfo.getClientPackageName();
+            }
+        }
+
+        /**
+         * @return the control hints used to control route session if available.
+         */
+        @Nullable
+        public Bundle getControlHints() {
+            synchronized (mControllerLock) {
+                return mSessionInfo.getControlHints();
+            }
+        }
+
+        /**
+         * @return the unmodifiable list of currently selected routes
+         */
+        @NonNull
+        public List<MediaRoute2Info> getSelectedRoutes() {
+            List<String> routeIds;
+            synchronized (mControllerLock) {
+                routeIds = mSessionInfo.getSelectedRoutes();
+            }
+            return getRoutesWithIds(routeIds);
+        }
+
+        /**
+         * @return the unmodifiable list of selectable routes for the session.
+         */
+        @NonNull
+        public List<MediaRoute2Info> getSelectableRoutes() {
+            List<String> routeIds;
+            synchronized (mControllerLock) {
+                routeIds = mSessionInfo.getSelectableRoutes();
+            }
+            return getRoutesWithIds(routeIds);
+        }
+
+        /**
+         * @return the unmodifiable list of deselectable routes for the session.
+         */
+        @NonNull
+        public List<MediaRoute2Info> getDeselectableRoutes() {
+            List<String> routeIds;
+            synchronized (mControllerLock) {
+                routeIds = mSessionInfo.getDeselectableRoutes();
+            }
+            return getRoutesWithIds(routeIds);
+        }
+
+        /**
+         * @return the unmodifiable list of transferrable routes for the session.
+         */
+        @NonNull
+        public List<MediaRoute2Info> getTransferrableRoutes() {
+            List<String> routeIds;
+            synchronized (mControllerLock) {
+                routeIds = mSessionInfo.getTransferrableRoutes();
+            }
+            return getRoutesWithIds(routeIds);
+        }
+
+        /**
+         * Selects a route for the remote session. The given route must satisfy all of the
+         * following conditions:
+         * <ul>
+         * <li>ID should not be included in {@link #getSelectedRoutes()}</li>
+         * <li>ID should be included in {@link #getSelectableRoutes()}</li>
+         * </ul>
+         * If the route doesn't meet any of above conditions, it will be ignored.
+         *
+         * @see #getSelectedRoutes()
+         * @see #getSelectableRoutes()
+         */
+        public void selectRoute(@NonNull MediaRoute2Info route) {
+            Objects.requireNonNull(route, "route must not be null");
+
+            RoutingSessionInfo sessionInfo;
+            synchronized (mControllerLock) {
+                sessionInfo = mSessionInfo;
+            }
+            if (sessionInfo.getSelectedRoutes().contains(route.getId())) {
+                Log.w(TAG, "Ignoring selecting a route that is already selected. route=" + route);
+                return;
+            }
+
+            if (!sessionInfo.getSelectableRoutes().contains(route.getId())) {
+                Log.w(TAG, "Ignoring selecting a non-selectable route=" + route);
+                return;
+            }
+
+            Client client;
+            synchronized (sLock) {
+                client = mClient;
+            }
+            if (client != null) {
+                try {
+                    mMediaRouterService.selectClientRoute(mClient, getSessionId(), route);
+                } catch (RemoteException ex) {
+                    Log.e(TAG, "Unable to select route for session.", ex);
+                }
+            }
+        }
+
+        /**
+         * Deselects a route from the remote session. The given route must satisfy all of the
+         * following conditions:
+         * <ul>
+         * <li>ID should be included in {@link #getSelectedRoutes()}</li>
+         * <li>ID should be included in {@link #getDeselectableRoutes()}</li>
+         * </ul>
+         * If the route doesn't meet any of above conditions, it will be ignored.
+         *
+         * @see #getSelectedRoutes()
+         * @see #getDeselectableRoutes()
+         */
+        public void deselectRoute(@NonNull MediaRoute2Info route) {
+            Objects.requireNonNull(route, "route must not be null");
+            RoutingSessionInfo sessionInfo;
+            synchronized (mControllerLock) {
+                sessionInfo = mSessionInfo;
+            }
+
+            if (!sessionInfo.getSelectedRoutes().contains(route.getId())) {
+                Log.w(TAG, "Ignoring deselecting a route that is not selected. route=" + route);
+                return;
+            }
+
+            if (!sessionInfo.getDeselectableRoutes().contains(route.getId())) {
+                Log.w(TAG, "Ignoring deselecting a non-deselectable route=" + route);
+                return;
+            }
+
+            Client client;
+            synchronized (sLock) {
+                client = mClient;
+            }
+            if (client != null) {
+                try {
+                    mMediaRouterService.deselectClientRoute(mClient, getSessionId(), route);
+                } catch (RemoteException ex) {
+                    Log.e(TAG, "Unable to remove route from session.", ex);
+                }
+            }
+        }
+
+        /**
+         * Transfers to a given route for the remote session. The given route must satisfy
+         * all of the following conditions:
+         * <ul>
+         * <li>ID should not be included in {@link #getSelectedRoutes()}</li>
+         * <li>ID should be included in {@link #getTransferrableRoutes()}</li>
+         * </ul>
+         * If the route doesn't meet any of above conditions, it will be ignored.
+         *
+         * @see #getSelectedRoutes()
+         * @see #getTransferrableRoutes()
+         */
+        public void transferToRoute(@NonNull MediaRoute2Info route) {
+            Objects.requireNonNull(route, "route must not be null");
+            RoutingSessionInfo sessionInfo;
+            synchronized (mControllerLock) {
+                sessionInfo = mSessionInfo;
+            }
+
+            if (sessionInfo.getSelectedRoutes().contains(route.getId())) {
+                Log.w(TAG, "Ignoring transferring to a route that is already added. route="
+                        + route);
+                return;
+            }
+
+            if (!sessionInfo.getTransferrableRoutes().contains(route.getId())) {
+                Log.w(TAG, "Ignoring transferring to a non-transferrable route=" + route);
+                return;
+            }
+
+            Client client;
+            synchronized (sLock) {
+                client = mClient;
+            }
+            if (client != null) {
+                try {
+                    mMediaRouterService.transferToClientRoute(mClient, getSessionId(), route);
+                } catch (RemoteException ex) {
+                    Log.e(TAG, "Unable to transfer to route for session.", ex);
+                }
+            }
+        }
+
+        /**
+         * Release this session.
+         * Any operation on this session after calling this method will be ignored.
+         */
+        public void release() {
+            Client client;
+            synchronized (sLock) {
+                client = mClient;
+            }
+            if (client != null) {
+                try {
+                    mMediaRouterService.releaseClientSession(mClient, getSessionId());
+                } catch (RemoteException ex) {
+                    Log.e(TAG, "Unable to notify of controller release", ex);
+                }
+            }
+        }
+
+        /**
+         * Gets the session info of the session
+         * @hide
+         */
+        @NonNull
+        public RoutingSessionInfo getSessionInfo() {
+            synchronized (mControllerLock) {
+                return mSessionInfo;
+            }
+        }
+
+        private List<MediaRoute2Info> getRoutesWithIds(List<String> routeIds) {
+            List<MediaRoute2Info> routes = new ArrayList<>();
+            synchronized (mRoutesLock) {
+                for (String routeId : routeIds) {
+                    MediaRoute2Info route = mRoutes.get(routeId);
+                    if (route != null) {
+                        routes.add(route);
+                    }
+                }
+            }
+            return Collections.unmodifiableList(routes);
+        }
+    }
+
+    /**
      * Interface for receiving events about media routing changes.
      */
     public static class Callback {
@@ -388,14 +684,17 @@
         public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {}
 
         /**
-         * Called when a route is selected for an application.
+         * Called when a routing session is created.
          *
-         * @param packageName the package name of the application
-         * @param route the selected route of the application.
-         *              It is null if the application has no selected route.
+         * @param controller the controller to control the created session
          */
-        public void onRouteSelected(@NonNull String packageName, @Nullable MediaRoute2Info route) {}
+        public void onSessionCreated(@NonNull RoutingController controller) {}
 
+        /**
+         * Called when at least one session info is changed.
+         * Call {@link #getActiveSessions()} to get current active session info.
+         */
+        public void onSessionsUpdated() {}
 
         /**
          * Called when the preferred route features of an app is changed.
@@ -435,11 +734,19 @@
 
     class Client extends IMediaRouter2Manager.Stub {
         @Override
-        public void notifyRouteSelected(String packageName, MediaRoute2Info route) {
-            mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifyRouteSelected,
-                    MediaRouter2Manager.this, packageName, route));
+        public void notifySessionCreated(RoutingSessionInfo sessionInfo) {
+            mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifySessionCreated,
+                    MediaRouter2Manager.this, sessionInfo));
         }
 
+        @Override
+        public void notifySessionsUpdated() {
+            mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifySessionInfosChanged,
+                    MediaRouter2Manager.this));
+            // do nothing
+        }
+
+        @Override
         public void notifyPreferredFeaturesChanged(String packageName, List<String> features) {
             mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updatePreferredFeatures,
                     MediaRouter2Manager.this, packageName, features));
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 18969ae..b76001f 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -24,18 +24,17 @@
 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.dvr.DvrCallback;
 import android.media.tv.tuner.dvr.DvrSettings;
-import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
+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;
 import android.media.tv.tuner.frontend.FrontendInfo;
 import android.media.tv.tuner.frontend.FrontendStatus;
+import android.media.tv.tuner.frontend.OnTuneEventListener;
 import android.media.tv.tuner.frontend.ScanCallback;
 import android.os.Handler;
 import android.os.Looper;
@@ -58,7 +57,6 @@
     private static final String TAG = "MediaTvTuner";
     private static final boolean DEBUG = false;
 
-    private static final int MSG_ON_FRONTEND_EVENT = 1;
     private static final int MSG_ON_FILTER_EVENT = 2;
     private static final int MSG_ON_FILTER_STATUS = 3;
     private static final int MSG_ON_LNB_EVENT = 4;
@@ -77,6 +75,10 @@
     private List<Integer> mLnbIds;
     private Lnb mLnb;
     @Nullable
+    private OnTuneEventListener mOnTuneEventListener;
+    @Nullable
+    private Executor mOnTunerEventExecutor;
+    @Nullable
     private ScanCallback mScanCallback;
     @Nullable
     private Executor mScanCallbackExecutor;
@@ -102,7 +104,8 @@
      * TODO: replace the other constructor
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
-    public Tuner(@NonNull Context context, @NonNull String tvInputSessionId, int useCase) {
+    public Tuner(@NonNull Context context, @NonNull String tvInputSessionId, int useCase,
+            @Nullable OnResourceLostListener listener) {
         mContext = context;
     }
 
@@ -113,7 +116,8 @@
      *
      * @hide
      */
-    public void shareFrontend(@NonNull Tuner tuner) { }
+    public void shareFrontendFromTuner(@NonNull Tuner tuner) {
+    }
 
 
     private long mNativeContext; // used by native jMediaTuner
@@ -221,11 +225,6 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_ON_FRONTEND_EVENT:
-                    if (mFrontend != null && mFrontend.mCallback != null) {
-                        mFrontend.mCallback.onEvent(msg.arg1);
-                    }
-                    break;
                 case MSG_ON_FILTER_STATUS: {
                     Filter filter = (Filter) msg.obj;
                     if (filter.mCallback != null) {
@@ -241,7 +240,6 @@
 
     private class Frontend {
         private int mId;
-        private FrontendCallback mCallback;
 
         private Frontend(int id) {
             mId = id;
@@ -249,13 +247,59 @@
     }
 
     /**
-     * Tunes the frontend to the settings given.
+     * Listens for tune events.
      *
-     * @return result status of tune operation.
+     * <p>
+     * Tuner events are started when {@link #tune(FrontendSettings)} is called and end when {@link
+     * #stopTune()} is called.
+     *
+     * @param eventListener receives tune events.
      * @throws SecurityException if the caller does not have appropriate permissions.
-     * TODO: add result constants or throw exceptions.
+     * @see #tune(FrontendSettings)
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    public void setOnTuneEventListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OnTuneEventListener eventListener) {
+        TunerUtils.checkTunerPermission(mContext);
+        mOnTuneEventListener = eventListener;
+        mOnTunerEventExecutor = executor;
+    }
+
+    /**
+     * Clears the {@link OnTuneEventListener} and its associated {@link Executor}.
+     *
+     * @throws SecurityException if the caller does not have appropriate permissions.
+     * @see #setOnTuneEventListener(Executor, OnTuneEventListener)
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    public void clearOnTuneEventListener() {
+        TunerUtils.checkTunerPermission(mContext);
+        mOnTuneEventListener = null;
+        mOnTunerEventExecutor = null;
+
+    }
+
+    /**
+     * Tunes the frontend to using the settings given.
+     *
+     * <p>
+     * This locks the frontend to a frequency by providing signal
+     * delivery information. If previous tuning isn't completed, this stop the previous tuning, and
+     * start a new tuning.
+     *
+     * <p>
+     * Tune is an async call, with {@link OnTuneEventListener#LOCKED LOCKED} and {@link
+     * OnTuneEventListener#NO_SIGNAL NO_SIGNAL} events sent to the {@link OnTuneEventListener}
+     * specified in {@link #setOnTuneEventListener(Executor, OnTuneEventListener)}.
+     *
+     * @param settings Signal delivery information the frontend uses to
+     *                 search and lock the signal.
+     * @return result status of tune operation.
+     * @throws SecurityException if the caller does not have appropriate permissions.
+     * @see #setOnTuneEventListener(Executor, OnTuneEventListener)
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @Result
     public int tune(@NonNull FrontendSettings settings) {
         TunerUtils.checkTunerPermission(mContext);
         return nativeTune(settings.getType(), settings);
@@ -268,8 +312,6 @@
      * will be sent to attached filters.
      *
      * @return result status of the operation.
-     *
-     * @hide
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     @Result
@@ -279,17 +321,26 @@
     }
 
     /**
-     * Scan channels.
+     * Scan for channels.
+     *
+     * <p>Details for channels found are returned via {@link ScanCallback}.
      *
      * @param settings A {@link FrontendSettings} to configure the frontend.
      * @param scanType The scan type.
-     *
+     * @throws SecurityException     if the caller does not have appropriate permissions.
+     * @throws IllegalStateException if {@code scan} is called again before {@link #stopScan()} is
+     *                               called.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
-    public int scan(@NonNull FrontendSettings settings, @FrontendScanType int scanType,
+    public int scan(@NonNull FrontendSettings settings, @ScanCallback.ScanType int scanType,
             @NonNull @CallbackExecutor Executor executor, @NonNull ScanCallback scanCallback) {
         TunerUtils.checkTunerPermission(mContext);
+        if (mScanCallback != null || mScanCallbackExecutor != null) {
+            throw new IllegalStateException(
+                    "Scan already in progress.  stopScan must be called before a new scan can be "
+                            + "started.");
+        }
         mScanCallback = scanCallback;
         mScanCallbackExecutor = executor;
         return nativeScan(settings.getType(), settings, scanType);
@@ -304,6 +355,7 @@
      * <p>
      * If the method completes successfully, the frontend stopped previous scanning.
      *
+     * @throws SecurityException if the caller does not have appropriate permissions.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@@ -490,8 +542,8 @@
     }
 
     private void onFrontendEvent(int eventType) {
-        if (mHandler != null) {
-            mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_FRONTEND_EVENT, eventType, 0));
+        if (mOnTunerEventExecutor != null && mOnTuneEventListener != null) {
+            mOnTunerEventExecutor.execute(() -> mOnTuneEventListener.onTuneEvent(eventType));
         }
     }
 
@@ -513,18 +565,18 @@
      * @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 cb the callback to receive notifications from 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(@FilterType int mainType, @FilterSubtype int subType,
-            @BytesLong long bufferSize, @Nullable FilterCallback cb,
-            @CallbackExecutor @Nullable Executor executor) {
+    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);
@@ -540,16 +592,34 @@
     /**
      * Opens an LNB (low-noise block downconverter) object.
      *
-     * @param cb the callback to receive notifications from LNB.
      * @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(LnbCallback cb, @CallbackExecutor @Nullable Executor executor) {
+    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);
@@ -608,17 +678,17 @@
      * @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 cb the callback to receive notifications from DVR.
      * @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, DvrCallback cb,
-            @CallbackExecutor @Nullable Executor executor) {
+    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 fa8f550..20b77e6 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -36,68 +36,17 @@
  */
 @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})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface FrontendEventType {}
-    /** @hide */
-    public static final int FRONTEND_EVENT_TYPE_LOCKED = Constants.FrontendEventType.LOCKED;
-    /** @hide */
-    public static final int FRONTEND_EVENT_TYPE_NO_SIGNAL = Constants.FrontendEventType.NO_SIGNAL;
-    /** @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})
@@ -245,20 +194,6 @@
     public static final int SC_HEVC_INDEX_SLICE_TRAIL_CRA =
             Constants.DemuxScHevcIndex.SLICE_TRAIL_CRA;
 
-
-    /** @hide */
-    @IntDef({FRONTEND_SCAN_UNDEFINED, FRONTEND_SCAN_AUTO, FRONTEND_SCAN_BLIND})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface FrontendScanType {}
-    /** @hide */
-    public static final int FRONTEND_SCAN_UNDEFINED = Constants.FrontendScanType.SCAN_UNDEFINED;
-    /** @hide */
-    public static final int FRONTEND_SCAN_AUTO = Constants.FrontendScanType.SCAN_AUTO;
-    /** @hide */
-    public static final int FRONTEND_SCAN_BLIND = Constants.FrontendScanType.SCAN_BLIND;
-
-
-
     /** @hide */
     @LongDef({FEC_UNDEFINED, FEC_AUTO, FEC_1_2, FEC_1_3, FEC_1_4, FEC_1_5, FEC_2_3, FEC_2_5,
             FEC_2_9, FEC_3_4, FEC_3_5, FEC_4_5, FEC_4_15, FEC_5_6, FEC_5_9, FEC_6_7, FEC_7_8,
@@ -502,23 +437,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/AvSettings.java b/media/java/android/media/tv/tuner/filter/AvSettings.java
index 940b5ae..93eaaa4 100644
--- a/media/java/android/media/tv/tuner/filter/AvSettings.java
+++ b/media/java/android/media/tv/tuner/filter/AvSettings.java
@@ -19,9 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 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 Video and Audio.
@@ -35,8 +33,8 @@
         super(TunerUtils.getFilterSubtype(
                 mainType,
                 isAudio
-                        ? TunerConstants.FILTER_SUBTYPE_AUDIO
-                        : TunerConstants.FILTER_SUBTYPE_VIDEO));
+                        ? Filter.SUBTYPE_AUDIO
+                        : Filter.SUBTYPE_VIDEO));
         mIsPassthrough = isPassthrough;
     }
 
@@ -57,7 +55,7 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     @NonNull
     public static Builder builder(
-            @NonNull Context context, @FilterType int mainType, boolean isAudio) {
+            @NonNull Context context, @Filter.Type int mainType, boolean isAudio) {
         TunerUtils.checkTunerPermission(context);
         return new Builder(mainType, isAudio);
     }
diff --git a/media/java/android/media/tv/tuner/filter/DownloadSettings.java b/media/java/android/media/tv/tuner/filter/DownloadSettings.java
index e3e1df0..fa7744a 100644
--- a/media/java/android/media/tv/tuner/filter/DownloadSettings.java
+++ b/media/java/android/media/tv/tuner/filter/DownloadSettings.java
@@ -19,9 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 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 Download.
@@ -31,7 +29,7 @@
     private final int mDownloadId;
 
     private DownloadSettings(int mainType, int downloadId) {
-        super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_DOWNLOAD));
+        super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_DOWNLOAD));
         mDownloadId = downloadId;
     }
 
@@ -50,7 +48,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/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 68c722f..c901e2b 100644
--- a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
@@ -32,7 +32,10 @@
 @SystemApi
 public abstract class FilterConfiguration {
 
-    /** @hide */
+    /**
+     * 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)
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 83246e5..248f23a 100644
--- a/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
@@ -39,7 +39,7 @@
     }
 
     /**
-     * Gets MMPT PID.
+     * Gets MMTP PID.
      *
      * <p>Packet ID is used to specify packets in MMTP.
      */
@@ -69,7 +69,7 @@
         }
 
         /**
-         * Sets MMPT PID.
+         * Sets MMTP PID.
          */
         @NonNull
         public Builder setMmtpPid(int mmtpPid) {
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 4833709..4e9d67f 100644
--- a/media/java/android/media/tv/tuner/filter/RecordSettings.java
+++ b/media/java/android/media/tv/tuner/filter/RecordSettings.java
@@ -24,7 +24,6 @@
 import android.media.tv.tuner.TunerConstants;
 import android.media.tv.tuner.TunerConstants.ScIndexType;
 import android.media.tv.tuner.TunerUtils;
-import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -151,7 +150,7 @@
     private final int mScIndexMask;
 
     private RecordSettings(int mainType, int tsIndexType, int scIndexType, int scIndexMask) {
-        super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_RECORD));
+        super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_RECORD));
         mTsIndexMask = tsIndexType;
         mScIndexType = scIndexType;
         mScIndexMask = scIndexMask;
@@ -187,7 +186,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/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 0fa982e..a2d42d8 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
@@ -20,7 +20,6 @@
 import android.annotation.RequiresPermission;
 import android.content.Context;
 import android.media.tv.tuner.TunerUtils;
-import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
 
 /**
  * Bits Settings for Section Filters.
@@ -73,7 +72,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/SectionSettingsWithTableInfo.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
index 6542b89..0c9cd2b 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
@@ -20,7 +20,6 @@
 import android.annotation.RequiresPermission;
 import android.content.Context;
 import android.media.tv.tuner.TunerUtils;
-import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
 
 /**
  * Table information for Section Filter.
@@ -57,7 +56,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/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/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/OnTuneEventListener.java b/media/java/android/media/tv/tuner/frontend/OnTuneEventListener.java
new file mode 100644
index 0000000..5cf0d31
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/OnTuneEventListener.java
@@ -0,0 +1,48 @@
+/*
+ * 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.media.tv.tuner.frontend;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.hardware.tv.tuner.V1_0.Constants;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Listens for tune events.
+ *
+ * @hide
+ */
+@SystemApi
+public interface OnTuneEventListener {
+
+    /** @hide */
+    @IntDef(prefix = "SIGNAL_", value = {SIGNAL_LOCKED, SIGNAL_NO_SIGNAL, SIGNAL_LOST_LOCK})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface TuneEvent {}
+
+    /** The frontend has locked to the signal specified by the tune method. */
+    int SIGNAL_LOCKED = Constants.FrontendEventType.LOCKED;
+    /** The frontend is unable to lock to the signal specified by the tune method. */
+    int SIGNAL_NO_SIGNAL = Constants.FrontendEventType.NO_SIGNAL;
+    /** The frontend has lost the lock to the signal specified by the tune method. */
+    int SIGNAL_LOST_LOCK = Constants.FrontendEventType.LOST_LOCK;
+
+    /** Tune Event from the frontend */
+    void onTuneEvent(@TuneEvent int tuneEvent);
+}
diff --git a/media/java/android/media/tv/tuner/frontend/ScanCallback.java b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
index 5e7d218..a825d6d 100644
--- a/media/java/android/media/tv/tuner/frontend/ScanCallback.java
+++ b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
@@ -16,26 +16,55 @@
 
 package android.media.tv.tuner.frontend;
 
+import android.annotation.IntDef;
+import android.hardware.tv.tuner.V1_0.Constants;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Scan callback.
  *
  * @hide
  */
 public interface ScanCallback {
+
+    /** @hide */
+    @IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_AUTO, SCAN_TYPE_BLIND})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ScanType {}
+    /**
+     * Scan type undefined.
+     */
+    int SCAN_TYPE_UNDEFINED = Constants.FrontendScanType.SCAN_UNDEFINED;
+    /**
+     * Scan type auto.
+     *
+     * <p> Tuner will send {@link #onLocked}
+     */
+    int SCAN_TYPE_AUTO = Constants.FrontendScanType.SCAN_AUTO;
+    /**
+     * Blind scan.
+     *
+     * <p>Frequency range is not specified. The {@link android.media.tv.tuner.Tuner} will scan an
+     * implementation specific range.
+     */
+    int SCAN_TYPE_BLIND = Constants.FrontendScanType.SCAN_BLIND;
+
     /** Scan locked the signal. */
-    void onLocked(boolean isLocked);
+    void onLocked();
 
     /** Scan stopped. */
-    void onEnd(boolean isEnd);
+    void onScanStopped();
 
     /** 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 +75,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 cc2d1b1..6595cae 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -19,10 +19,12 @@
 import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_SPEAKER;
 import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV;
 
+import android.annotation.Nullable;
 import android.content.Intent;
 import android.media.MediaRoute2Info;
 import android.media.MediaRoute2ProviderService;
 import android.media.RoutingSessionInfo;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.text.TextUtils;
 
@@ -167,7 +169,8 @@
     }
 
     @Override
-    public void onCreateSession(String packageName, String routeId, long requestId) {
+    public void onCreateSession(String packageName, String routeId, long requestId,
+            @Nullable Bundle sessionHints) {
         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.
@@ -188,6 +191,8 @@
                 .addSelectedRoute(routeId)
                 .addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
                 .addTransferrableRoute(ROUTE_ID5_TO_TRANSFER_TO)
+                // Set control hints with given sessionHints
+                .setControlHints(sessionHints)
                 .build();
         notifySessionCreated(sessionInfo, requestId);
         publishRoutes();
@@ -210,6 +215,7 @@
             }
         }
         notifySessionReleased(sessionId);
+        publishRoutes();
     }
 
     @Override
@@ -265,6 +271,27 @@
     @Override
     public void onTransferToRoute(String sessionId, String routeId) {
         RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
+        MediaRoute2Info route = mRoutes.get(routeId);
+
+        if (sessionInfo == null || route == null) {
+            return;
+        }
+
+        for (String selectedRouteId : sessionInfo.getSelectedRoutes()) {
+            mRouteIdToSessionId.remove(selectedRouteId);
+            MediaRoute2Info selectedRoute = mRoutes.get(selectedRouteId);
+            if (selectedRoute != null) {
+                mRoutes.put(selectedRouteId, new MediaRoute2Info.Builder(selectedRoute)
+                        .setClientPackageName(null)
+                        .build());
+            }
+        }
+
+        mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
+                .setClientPackageName(sessionInfo.getClientPackageName())
+                .build());
+        mRouteIdToSessionId.put(routeId, sessionId);
+
         RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
                 .clearSelectedRoutes()
                 .addSelectedRoute(routeId)
@@ -272,6 +299,7 @@
                 .removeTransferrableRoute(routeId)
                 .build();
         notifySessionUpdated(newSessionInfo);
+        publishRoutes();
     }
 
     void maybeDeselectRoute(String routeId) {
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
index e782aae..32d03db 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
@@ -43,12 +43,14 @@
 import android.content.Context;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2;
+import android.media.MediaRouter2.OnCreateSessionListener;
 import android.media.MediaRouter2.RouteCallback;
 import android.media.MediaRouter2.RoutingController;
 import android.media.MediaRouter2.SessionCallback;
 import android.media.RouteDiscoveryPreference;
 import android.media.RoutingSessionInfo;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
@@ -80,6 +82,9 @@
 
     private static final int TIMEOUT_MS = 5000;
 
+    private static final String TEST_KEY = "test_key";
+    private static final String TEST_VALUE = "test_value";
+
     @Before
     public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getTargetContext();
@@ -328,6 +333,74 @@
     }
 
     @Test
+    public void testSetOnCreateSessionListener() throws Exception {
+        final List<String> sampleRouteFeature = new ArrayList<>();
+        sampleRouteFeature.add(FEATURE_SAMPLE);
+
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteFeature);
+        MediaRoute2Info route = routes.get(ROUTE_ID1);
+        assertNotNull(route);
+
+        final Bundle createSessionHints = new Bundle();
+        createSessionHints.putString(TEST_KEY, TEST_VALUE);
+        final OnCreateSessionListener listener = new OnCreateSessionListener() {
+            @Override
+            public Bundle onCreateSession(MediaRoute2Info route) {
+                return createSessionHints;
+            }
+        };
+
+        final CountDownLatch successLatch = new CountDownLatch(1);
+        final CountDownLatch failureLatch = new CountDownLatch(1);
+        final List<RoutingController> controllers = new ArrayList<>();
+
+        // Create session with this route
+        SessionCallback sessionCallback = new SessionCallback() {
+            @Override
+            public void onSessionCreated(RoutingController controller) {
+                assertNotNull(controller);
+                assertTrue(createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1));
+
+                // The SampleMediaRoute2ProviderService supposed to set control hints
+                // with the given creationSessionHints.
+                Bundle controlHints = controller.getControlHints();
+                assertNotNull(controlHints);
+                assertTrue(controlHints.containsKey(TEST_KEY));
+                assertEquals(TEST_VALUE, controlHints.getString(TEST_KEY));
+
+                controllers.add(controller);
+                successLatch.countDown();
+            }
+
+            @Override
+            public void onSessionCreationFailed(MediaRoute2Info requestedRoute) {
+                failureLatch.countDown();
+            }
+        };
+
+        // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
+        RouteCallback routeCallback = new RouteCallback();
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.EMPTY);
+
+        try {
+            mRouter2.registerSessionCallback(mExecutor, sessionCallback);
+
+            // The SampleMediaRoute2ProviderService supposed to set control hints
+            // with the given creationSessionHints.
+            mRouter2.setOnCreateSessionListener(listener);
+            mRouter2.requestCreateSession(route);
+            assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+            // onSessionCreationFailed should not be called.
+            assertFalse(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        } finally {
+            releaseControllers(controllers);
+            mRouter2.unregisterRouteCallback(routeCallback);
+            mRouter2.unregisterSessionCallback(sessionCallback);
+        }
+    }
+
+    @Test
     public void testSessionCallbackIsNotCalledAfterUnregistered() throws Exception {
         final List<String> sampleRouteType = new ArrayList<>();
         sampleRouteType.add(FEATURE_SAMPLE);
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index 83dd0c0..cba8452 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -31,6 +31,7 @@
 import android.media.MediaRouter2.SessionCallback;
 import android.media.MediaRouter2Manager;
 import android.media.RouteDiscoveryPreference;
+import android.media.RoutingSessionInfo;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -38,7 +39,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -138,6 +138,8 @@
 
     @After
     public void tearDown() {
+        // order matters (callbacks should be cleared at the last)
+        releaseAllSessions();
         // unregister callbacks
         clearCallbacks();
     }
@@ -223,110 +225,83 @@
         MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
         assertNotNull(routeToSelect);
 
-        try {
-            mManager.selectRoute(mPackageName, routeToSelect);
-            assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        } finally {
-            //TODO: release the session
-            //mManager.selectRoute(mPackageName, null);
-        }
-    }
-
-    /**
-     * Tests if MR2Manager.Callback.onRouteSelected is called
-     * when a route is selected by MR2Manager.
-     */
-    @Test
-    @Ignore("TODO: test session created callback instead of onRouteSelected")
-    public void testManagerOnRouteSelected() throws Exception {
-        CountDownLatch latch = new CountDownLatch(1);
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
-
-        addRouterCallback(new RouteCallback());
-        addManagerCallback(new MediaRouter2Manager.Callback() {
-            @Override
-            public void onRouteSelected(String packageName, MediaRoute2Info route) {
-                if (TextUtils.equals(mPackageName, packageName)
-                        && route != null && TextUtils.equals(route.getId(), ROUTE_ID1)) {
-                    latch.countDown();
-                }
-            }
-        });
-
-        MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
-        assertNotNull(routeToSelect);
-
-        try {
-            mManager.selectRoute(mPackageName, routeToSelect);
-            assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        } finally {
-            //TODO: release the session
-            //mManager.selectRoute(mPackageName, null);
-        }
+        mManager.selectRoute(mPackageName, routeToSelect);
+        assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertEquals(1, mManager.getActiveSessions().size());
     }
 
     @Test
-    @Ignore("TODO: enable this when 'releasing session' is implemented")
-    public void testGetActiveRoutes() throws Exception {
+    public void testGetRoutingControllers() throws Exception {
         CountDownLatch latch = new CountDownLatch(1);
 
         Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
         addRouterCallback(new RouteCallback());
         addManagerCallback(new MediaRouter2Manager.Callback() {
             @Override
-            public void onRouteSelected(String packageName, MediaRoute2Info route) {
-                if (TextUtils.equals(mPackageName, packageName)
-                        && route != null && TextUtils.equals(route.getId(), ROUTE_ID1)) {
+            public void onSessionCreated(MediaRouter2Manager.RoutingController controller) {
+                if (TextUtils.equals(mPackageName, controller.getClientPackageName())
+                        && createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1)) {
                     latch.countDown();
                 }
             }
         });
 
-        //TODO: it fails due to not releasing session
-        assertEquals(0, mManager.getActiveSessions().size());
+        assertEquals(0, mManager.getRoutingControllers(mPackageName).size());
 
         mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1));
         latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
 
-        assertEquals(1, mManager.getActiveSessions().size());
+        List<MediaRouter2Manager.RoutingController> controllers =
+                mManager.getRoutingControllers(mPackageName);
 
-        //TODO: release the session
-        /*
+        assertEquals(1, controllers.size());
+
+        MediaRouter2Manager.RoutingController routingController = controllers.get(0);
         awaitOnRouteChangedManager(
-                () -> mManager.selectRoute(mPackageName, null),
+                () -> routingController.release(),
                 ROUTE_ID1,
                 route -> TextUtils.equals(route.getClientPackageName(), null));
-        assertEquals(0, mManager.getActiveRoutes().size());
-        */
+        assertEquals(0, mManager.getRoutingControllers(mPackageName).size());
     }
 
     /**
-     * Tests selecting and unselecting routes of a single provider.
+     * Tests select, transfer, release of routes of a provider
      */
     @Test
-    @Ignore("TODO: enable when session is released")
-    public void testSingleProviderSelect() throws Exception {
+    public void testSelectAndTransferAndRelease() throws Exception {
         Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
         addRouterCallback(new RouteCallback());
 
+        CountDownLatch onSessionCreatedLatch = new CountDownLatch(1);
+
+        addManagerCallback(new MediaRouter2Manager.Callback() {
+            @Override
+            public void onSessionCreated(MediaRouter2Manager.RoutingController controller) {
+                assertNotNull(controller);
+                onSessionCreatedLatch.countDown();
+            }
+        });
         awaitOnRouteChangedManager(
                 () -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1)),
                 ROUTE_ID1,
                 route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
+        assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+        List<MediaRouter2Manager.RoutingController> controllers =
+                mManager.getRoutingControllers(mPackageName);
+
+        assertEquals(1, controllers.size());
+        MediaRouter2Manager.RoutingController routingController = controllers.get(0);
 
         awaitOnRouteChangedManager(
-                () -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID2)),
-                ROUTE_ID2,
+                () -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID5_TO_TRANSFER_TO)),
+                ROUTE_ID5_TO_TRANSFER_TO,
                 route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
 
-        //TODO: release the session
-        /*
         awaitOnRouteChangedManager(
-                () -> mManager.selectRoute(mPackageName, null),
-                ROUTE_ID2,
+                () -> routingController.release(),
+                ROUTE_ID5_TO_TRANSFER_TO,
                 route -> TextUtils.equals(route.getClientPackageName(), null));
-
-        */
     }
 
     @Test
@@ -460,4 +435,13 @@
         }
         mSessionCallbacks.clear();
     }
+
+    private void releaseAllSessions() {
+        // ensure ManagerRecord in MediaRouter2ServiceImpl
+        addManagerCallback(new MediaRouter2Manager.Callback());
+
+        for (RoutingSessionInfo session : mManager.getActiveSessions()) {
+            mManager.getControllerForSession(session).release();
+        }
+    }
 }
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 6ea2c74..a337570 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -195,6 +195,7 @@
                     Settings.Global.CERT_PIN_UPDATE_CONTENT_URL,
                     Settings.Global.CERT_PIN_UPDATE_METADATA_URL,
                     Settings.Global.COMPATIBILITY_MODE,
+                    Settings.Global.COMMON_CRITERIA_MODE,
                     Settings.Global.CONNECTIVITY_CHANGE_DELAY,
                     Settings.Global.CONNECTIVITY_METRICS_BUFFER_SIZE,
                     Settings.Global.CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS,
diff --git a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml b/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
index f04226e..cd90efe 100644
--- a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
+++ b/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
@@ -32,8 +32,8 @@
             android:orientation="horizontal">
 
             <FrameLayout
-                android:layout_width="90dp"
-                android:layout_height="94dp">
+                android:layout_width="45dp"
+                android:layout_height="47dp">
 
                 <View
                     android:id="@+id/icon_container_bg"
@@ -43,35 +43,34 @@
 
                 <FrameLayout
                     android:id="@+id/icon_mic"
-                    android:layout_width="70dp"
-                    android:layout_height="70dp"
-                    android:layout_marginLeft="12dp"
-                    android:layout_marginTop="12dp"
-                    android:layout_marginRight="8dp"
-                    android:layout_marginBottom="12dp">
+                    android:layout_width="35dp"
+                    android:layout_height="35dp"
+                    android:layout_marginLeft="6dp"
+                    android:layout_marginTop="6dp"
+                    android:layout_marginBottom="6dp">
 
                     <View
-                        android:layout_width="54dp"
-                        android:layout_height="54dp"
+                        android:layout_width="27dp"
+                        android:layout_height="27dp"
                         android:layout_gravity="center"
                         android:background="@drawable/tv_circle_dark"/>
 
                     <ImageView
                         android:id="@+id/pulsating_circle"
-                        android:layout_width="54dp"
-                        android:layout_height="54dp"
+                        android:layout_width="27dp"
+                        android:layout_height="27dp"
                         android:layout_gravity="center"
                         android:background="@drawable/tv_circle_white_translucent"/>
 
                     <ImageView
-                        android:layout_width="54dp"
-                        android:layout_height="54dp"
+                        android:layout_width="27dp"
+                        android:layout_height="27dp"
                         android:layout_gravity="center"
                         android:src="@drawable/tv_ring_white"/>
 
                     <ImageView
-                        android:layout_width="32dp"
-                        android:layout_height="32dp"
+                        android:layout_width="16dp"
+                        android:layout_height="16dp"
                         android:layout_gravity="center"
                         android:background="@drawable/tv_ic_mic_white"/>
                 </FrameLayout>
@@ -81,29 +80,30 @@
             <LinearLayout
                 android:id="@+id/texts_container"
                 android:layout_width="wrap_content"
-                android:layout_height="94dp"
+                android:layout_height="47dp"
                 android:background="@color/tv_audio_recording_indicator_background"
-                android:gravity="center_vertical"
                 android:orientation="vertical"
                 android:visibility="visible">
 
                 <TextView
                     android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
+                    android:layout_height="14dp"
+                    android:layout_marginTop="10dp"
+                    android:layout_marginBottom="1dp"
                     android:text="@string/mic_active"
                     android:textColor="@android:color/white"
                     android:fontFamily="sans-serif"
-                    android:textSize="20dp"/>
+                    android:textSize="10dp"/>
 
                 <TextView
                     android:id="@+id/text"
                     android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
+                    android:layout_height="14dp"
                     android:singleLine="true"
                     android:text="SomeApplication accessed your microphone"
                     android:textColor="@android:color/white"
                     android:fontFamily="sans-serif"
-                    android:textSize="16dp"/>
+                    android:textSize="8dp"/>
 
             </LinearLayout>
 
@@ -113,8 +113,8 @@
 
     <View
         android:id="@+id/bg_right"
-        android:layout_width="24dp"
-        android:layout_height="94dp"
+        android:layout_width="12dp"
+        android:layout_height="47dp"
         android:background="@drawable/tv_rect_dark_right_rounded"
         android:visibility="visible"/>
 
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 8963157..e520106 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -484,4 +484,13 @@
     <!-- Package name for controls plugin -->
     <string name="config_controlsPluginPackageName" translatable="false">com.android.systemui.controls.panel</string>
 
+    <!-- Defines the blacklist for system icons.  That is to say, the icons in the status bar that
+         are part of the blacklist are never displayed. Each item in the blacklist must be a string
+         defined in core/res/res/config.xml to properly blacklist the icon.
+     -->
+    <string-array name="config_statusBarIconBlackList" translatable="false">
+        <item>@*android:string/status_bar_rotate</item>
+        <item>@*android:string/status_bar_headset</item>
+    </string-array>
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index d68fe15..a46ab3a 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -288,7 +288,8 @@
     @Override
     public void onTuningChanged(String key, String newValue) {
         if (StatusBarIconController.ICON_BLACKLIST.equals(key)) {
-            ArraySet<String> icons = StatusBarIconController.getIconBlacklist(newValue);
+            ArraySet<String> icons = StatusBarIconController.getIconBlacklist(
+                    getContext(), newValue);
             setVisibility(icons.contains(mSlotBattery) ? View.GONE : View.VISIBLE);
         }
     }
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/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 86ed274..f71150f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -366,7 +366,7 @@
 
     protected static List<String> loadTileSpecs(Context context, String tileList) {
         final Resources res = context.getResources();
-        final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
+
         if (TextUtils.isEmpty(tileList)) {
             tileList = res.getString(R.string.quick_settings_tiles);
             if (DEBUG) Log.d(TAG, "Loaded tile specs from config: " + tileList);
@@ -380,11 +380,7 @@
             if (tile.isEmpty()) continue;
             if (tile.equals("default")) {
                 if (!addedDefault) {
-                    tiles.addAll(Arrays.asList(defaultTileList.split(",")));
-                    if (Build.IS_DEBUGGABLE
-                            && GarbageMonitor.MemoryTile.ADD_TO_DEFAULT_ON_DEBUGGABLE_BUILDS) {
-                        tiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);
-                    }
+                    tiles.addAll(getDefaultSpecs(context));
                     addedDefault = true;
                 }
             } else {
@@ -394,6 +390,28 @@
         return tiles;
     }
 
+    /**
+     * Returns the default QS tiles for the context.
+     * @param context the context to obtain the resources from
+     * @return a list of specs of the default tiles
+     */
+    public static List<String> getDefaultSpecs(Context context) {
+        final ArrayList<String> tiles = new ArrayList<String>();
+
+        final Resources res = context.getResources();
+        final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
+        final String extraTileList = res.getString(
+                com.android.internal.R.string.config_defaultExtraQuickSettingsTiles);
+
+        tiles.addAll(Arrays.asList(defaultTileList.split(",")));
+        tiles.addAll(Arrays.asList(extraTileList.split(",")));
+        if (Build.IS_DEBUGGABLE
+                && GarbageMonitor.MemoryTile.ADD_TO_DEFAULT_ON_DEBUGGABLE_BUILDS) {
+            tiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);
+        }
+        return tiles;
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("QSTileHost:");
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 1de6355..fb89ed2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -266,12 +266,7 @@
     }
 
     private void reset() {
-        ArrayList<String> tiles = new ArrayList<>();
-        String defTiles = mContext.getString(R.string.quick_settings_tiles_default);
-        for (String tile : defTiles.split(",")) {
-            tiles.add(tile);
-        }
-        mTileAdapter.resetTileSpecs(mHost, tiles);
+        mTileAdapter.resetTileSpecs(mHost, QSTileHost.getDefaultSpecs(mContext));
     }
 
     private void setTileSpecs() {
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 3b106cb..64bdd979 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
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.notification.row;
 
 import static android.app.Notification.EXTRA_IS_GROUP_CONVERSATION;
-import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
@@ -54,6 +53,7 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -102,7 +102,6 @@
     private NotificationEntry mEntry;
     private StatusBarNotification mSbn;
     private boolean mIsDeviceProvisioned;
-
     private int mStartingChannelImportance;
     private boolean mStartedAsBubble;
     private boolean mIsBubbleable;
@@ -127,6 +126,8 @@
             mBubbleController.onUserDemotedBubbleFromNotification(mEntry);
         } else {
             mBubbleController.onUserCreatedBubbleFromNotification(mEntry);
+            Settings.Global.putInt(
+                    mContext.getContentResolver(), Settings.Global.NOTIFICATION_BUBBLES, 1);
         }
         closeControls(v, true);
     };
@@ -209,11 +210,10 @@
         mLauncherApps = launcherApps;
         mConversationId = mNotificationChannel.getConversationId();
         if (TextUtils.isEmpty(mNotificationChannel.getConversationId())) {
-            mConversationId = mSbn.getNotification().getShortcutId();
+            mConversationId = mSbn.getShortcutId(mContext);
         }
-        // TODO: flag this when flag exists
         if (TextUtils.isEmpty(mConversationId)) {
-            mConversationId = mSbn.getId() + mSbn.getTag() + PLACEHOLDER_CONVERSATION_ID;
+            throw new IllegalArgumentException("Does not have required information");
         }
         // TODO: consider querying this earlier in the notification pipeline and passing it in
         LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
@@ -240,10 +240,9 @@
         // a custom channel
         if (TextUtils.isEmpty(mNotificationChannel.getConversationId())) {
             try {
-                // TODO: associate this key with this channel service side so the customization
-                // isn't forgotten on the next update
                 mINotificationManager.createConversationNotificationChannelForPackage(
-                        mPackageName, mAppUid, mNotificationChannel, mConversationId);
+                        mPackageName, mAppUid, mSbn.getKey(), mNotificationChannel,
+                        mConversationId);
                 mNotificationChannel = mINotificationManager.getConversationNotificationChannel(
                         mContext.getOpPackageName(), UserHandle.getUserId(mAppUid), mPackageName,
                         mNotificationChannel.getId(), false, mConversationId);
@@ -360,7 +359,6 @@
             image.setImageDrawable(mLauncherApps.getShortcutBadgedIconDrawable(mShortcutInfo,
                     mContext.getResources().getDisplayMetrics().densityDpi));
         } else {
-            // TODO: flag this behavior
             if (mSbn.getNotification().extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION, false)) {
                 // TODO: maybe use a generic group icon, or a composite of recent senders
                 image.setImageDrawable(mPm.getDefaultActivityIcon());
@@ -388,7 +386,6 @@
         if (mShortcutInfo != null) {
             name.setText(mShortcutInfo.getShortLabel());
         } else {
-            // TODO: flag this behavior
             Bundle extras = mSbn.getNotification().extras;
             String nameString = extras.getString(Notification.EXTRA_CONVERSATION_TITLE);
             if (TextUtils.isEmpty(nameString)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index a9a4804..6b4511d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -65,8 +65,6 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.bubbles.BubbleExperimentConfig;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationCounters;
@@ -102,7 +100,6 @@
     // standard controls
     private static final int ACTION_ALERT = 5;
 
-    private TextView mBubbleDescriptionView;
     private TextView mPriorityDescriptionView;
     private TextView mSilentDescriptionView;
 
@@ -120,7 +117,6 @@
     private Set<NotificationChannel> mUniqueChannelsInRow;
     private NotificationChannel mSingleNotificationChannel;
     private int mStartingChannelImportance;
-    private boolean mStartedAsBubble;
     private boolean mWasShownHighPriority;
     private boolean mPressedApply;
     private boolean mPresentingChannelEditorDialog = false;
@@ -130,14 +126,8 @@
      * level; non-null once the user takes an action which indicates an explicit preference.
      */
     @Nullable private Integer mChosenImportance;
-    /**
-     * The last bubble setting chosen by the user. Null if the user has not chosen a bubble level;
-     * non-null once the user takes an action which indicates an explicit preference.
-     */
-    @Nullable private Boolean mChosenBubbleEnabled;
     private boolean mIsSingleDefaultChannel;
     private boolean mIsNonblockable;
-    private boolean mIsBubbleable;
     private NotificationEntry mEntry;
     private StatusBarNotification mSbn;
     private AnimatorSet mExpandAnimation;
@@ -149,8 +139,6 @@
     private NotificationGuts mGutsContainer;
     private Drawable mPkgIcon;
 
-    private BubbleController mBubbleController;
-
     /** Whether this view is being shown as part of the blocking helper. */
     private boolean mIsForBlockingHelper;
 
@@ -167,9 +155,6 @@
     private OnClickListener mOnAlert = v -> {
         mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
         mChosenImportance = IMPORTANCE_DEFAULT;
-        if (mStartedAsBubble) {
-            mChosenBubbleEnabled = false;
-        }
         applyAlertingBehavior(BEHAVIOR_ALERTING, true /* userTriggered */);
     };
 
@@ -177,18 +162,9 @@
     private OnClickListener mOnSilent = v -> {
         mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY;
         mChosenImportance = IMPORTANCE_LOW;
-        if (mStartedAsBubble) {
-            mChosenBubbleEnabled = false;
-        }
         applyAlertingBehavior(BEHAVIOR_SILENT, true /* userTriggered */);
     };
 
-    /** Used by standard ui (in an experiment) {@see BubbleExperimentConfig#allowNotifBubbleMenu} */
-    private OnClickListener mOnBubble = v -> {
-        mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
-        mChosenBubbleEnabled = true;
-        applyAlertingBehavior(BEHAVIOR_BUBBLE, true /* userTriggered */);
-    };
 
     // used by standard ui
     private OnClickListener mOnDismissSettings = v -> {
@@ -255,7 +231,6 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        mBubbleDescriptionView = findViewById(R.id.bubble_summary);
         mPriorityDescriptionView = findViewById(R.id.alert_summary);
         mSilentDescriptionView = findViewById(R.id.silence_summary);
     }
@@ -320,7 +295,6 @@
         mMetricsLogger = Dependency.get(MetricsLogger.class);
         mVisualStabilityManager = visualStabilityManager;
         mChannelEditorDialogController = Dependency.get(ChannelEditorDialogController.class);
-        mBubbleController = Dependency.get(BubbleController.class);
         mPackageName = pkg;
         mUniqueChannelsInRow = uniqueChannelsInRow;
         mNumUniqueChannelsInRow = uniqueChannelsInRow.size();
@@ -352,9 +326,6 @@
                     && numTotalChannels == 1;
         }
 
-        mIsBubbleable = mEntry.getBubbleMetadata() != null;
-        mStartedAsBubble = mEntry.isBubble();
-
         bindHeader();
         bindChannelDetails();
 
@@ -402,7 +373,6 @@
             findViewById(R.id.non_configurable_text).setVisibility(GONE);
             findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE);
             findViewById(R.id.interruptiveness_settings).setVisibility(VISIBLE);
-            findViewById(R.id.bubble).setVisibility(mIsBubbleable ? VISIBLE : GONE);
         }
 
         View turnOffButton = findViewById(R.id.turn_off_notifications);
@@ -416,14 +386,10 @@
 
         View silent = findViewById(R.id.silence);
         View alert = findViewById(R.id.alert);
-        View bubble = findViewById(R.id.bubble);
         silent.setOnClickListener(mOnSilent);
         alert.setOnClickListener(mOnAlert);
-        bubble.setOnClickListener(mOnBubble);
 
-        int behavior = mStartedAsBubble
-                ? BEHAVIOR_BUBBLE
-                : mWasShownHighPriority
+        int behavior = mWasShownHighPriority
                         ? BEHAVIOR_ALERTING
                         : BEHAVIOR_SILENT;
         applyAlertingBehavior(behavior, false /* userTriggered */);
@@ -587,14 +553,6 @@
                 }
             }
 
-            if (mChosenBubbleEnabled != null && mStartedAsBubble != mChosenBubbleEnabled) {
-                if (mChosenBubbleEnabled) {
-                    mBubbleController.onUserCreatedBubbleFromNotification(mEntry);
-                } else {
-                    mBubbleController.onUserDemotedBubbleFromNotification(mEntry);
-                }
-            }
-
             Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
             bgHandler.post(
                     new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
@@ -630,7 +588,6 @@
             TransitionManager.beginDelayedTransition(this, transition);
         }
 
-        View bubble = findViewById(R.id.bubble);
         View alert = findViewById(R.id.alert);
         View silence = findViewById(R.id.silence);
 
@@ -638,33 +595,18 @@
             case BEHAVIOR_ALERTING:
                 mPriorityDescriptionView.setVisibility(VISIBLE);
                 mSilentDescriptionView.setVisibility(GONE);
-                mBubbleDescriptionView.setVisibility(GONE);
                 post(() -> {
                     alert.setSelected(true);
                     silence.setSelected(false);
-                    bubble.setSelected(false);
                 });
                 break;
 
             case BEHAVIOR_SILENT:
                 mSilentDescriptionView.setVisibility(VISIBLE);
                 mPriorityDescriptionView.setVisibility(GONE);
-                mBubbleDescriptionView.setVisibility(GONE);
                 post(() -> {
                     alert.setSelected(false);
                     silence.setSelected(true);
-                    bubble.setSelected(false);
-                });
-                break;
-
-            case BEHAVIOR_BUBBLE:
-                mBubbleDescriptionView.setVisibility(VISIBLE);
-                mSilentDescriptionView.setVisibility(GONE);
-                mPriorityDescriptionView.setVisibility(GONE);
-                post(() -> {
-                    alert.setSelected(false);
-                    silence.setSelected(false);
-                    bubble.setSelected(true);
                 });
                 break;
 
@@ -673,9 +615,8 @@
         }
 
         boolean isAChange = mWasShownHighPriority != (behavior == BEHAVIOR_ALERTING);
-        boolean isABubbleChange = mStartedAsBubble != (behavior == BEHAVIOR_BUBBLE);
         TextView done = findViewById(R.id.done);
-        done.setText((isAChange || isABubbleChange)
+        done.setText(isAChange
                 ? R.string.inline_ok_button
                 : R.string.inline_done_button);
     }
@@ -684,7 +625,6 @@
         switch (action) {
             case ACTION_UNDO:
                 mChosenImportance = mStartingChannelImportance;
-                mChosenBubbleEnabled = mStartedAsBubble;
                 break;
             case ACTION_DELIVER_SILENTLY:
                 mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY;
@@ -767,9 +707,6 @@
         if (mChosenImportance != null) {
             mStartingChannelImportance = mChosenImportance;
         }
-        if (mChosenBubbleEnabled != null) {
-            mStartedAsBubble = mChosenBubbleEnabled;
-        }
         mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
 
         if (mIsForBlockingHelper) {
@@ -969,9 +906,8 @@
     }
 
     @Retention(SOURCE)
-    @IntDef({BEHAVIOR_ALERTING, BEHAVIOR_SILENT, BEHAVIOR_BUBBLE})
+    @IntDef({BEHAVIOR_ALERTING, BEHAVIOR_SILENT})
     private @interface AlertingBehavior {}
     private static final int BEHAVIOR_ALERTING = 0;
     private static final int BEHAVIOR_SILENT = 1;
-    private static final int BEHAVIOR_BUBBLE = 2;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 5daef24..75da5d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -76,12 +76,12 @@
 
     public static final String ICON_BLACKLIST = "icon_blacklist";
 
-    public static ArraySet<String> getIconBlacklist(String blackListStr) {
+    /** Reads the default blacklist from config value unless blacklistStr is provided. */
+    static ArraySet<String> getIconBlacklist(Context context, String blackListStr) {
         ArraySet<String> ret = new ArraySet<>();
-        if (blackListStr == null) {
-            blackListStr = "rotate,headset";
-        }
-        String[] blacklist = blackListStr.split(",");
+        String[] blacklist = blackListStr == null
+            ? context.getResources().getStringArray(R.array.config_statusBarIconBlackList)
+            : blackListStr.split(",");
         for (String slot : blacklist) {
             if (!TextUtils.isEmpty(slot)) {
                 ret.add(slot);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index aa062eb..bfcbcea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -111,7 +111,7 @@
             return;
         }
         mIconBlacklist.clear();
-        mIconBlacklist.addAll(StatusBarIconController.getIconBlacklist(newValue));
+        mIconBlacklist.addAll(StatusBarIconController.getIconBlacklist(mContext, newValue));
         ArrayList<Slot> currentSlots = getSlots();
         ArrayMap<Slot, List<StatusBarIconHolder>> slotsToReAdd = new ArrayMap<>();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 8286d26..d2e9262 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -117,7 +117,7 @@
         if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) {
             return;
         }
-        ArraySet<String> blockList = StatusBarIconController.getIconBlacklist(newValue);
+        ArraySet<String> blockList = StatusBarIconController.getIconBlacklist(mContext, newValue);
         boolean blockAirplane = blockList.contains(mSlotAirplane);
         boolean blockMobile = blockList.contains(mSlotMobile);
         boolean blockWifi = blockList.contains(mSlotWifi);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 4f0af9e..759bad4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -298,7 +298,7 @@
             mShowSeconds = TunerService.parseIntegerSwitch(newValue, false);
             updateShowSeconds();
         } else {
-            setClockVisibleByUser(!StatusBarIconController.getIconBlacklist(newValue)
+            setClockVisibleByUser(!StatusBarIconController.getIconBlacklist(getContext(), newValue)
                     .contains("clock"));
             updateClockVisibility();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
index 905b9a3..66372c3 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
@@ -64,7 +64,7 @@
     @Override
     public void onTuningChanged(String key, String newValue) {
         if (StatusBarIconController.ICON_BLACKLIST.equals(key)) {
-            mBlacklist = StatusBarIconController.getIconBlacklist(newValue);
+            mBlacklist = StatusBarIconController.getIconBlacklist(getContext(), newValue);
             mBatteryEnabled = !mBlacklist.contains(mBattery);
         }
         if (!mHasSetValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java
index a526603..f7d0c9f 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java
@@ -61,7 +61,7 @@
     public void onTuningChanged(String key, String newValue) {
         if (StatusBarIconController.ICON_BLACKLIST.equals(key)) {
             mReceivedClock = true;
-            mBlacklist = StatusBarIconController.getIconBlacklist(newValue);
+            mBlacklist = StatusBarIconController.getIconBlacklist(getContext(), newValue);
             mClockEnabled = !mBlacklist.contains(mClock);
         } else if (Clock.CLOCK_SECONDS.equals(key)) {
             mReceivedSeconds = true;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java
index 6f23e20..de8ccfa 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java
@@ -57,7 +57,7 @@
         if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) {
             return;
         }
-        mBlacklist = StatusBarIconController.getIconBlacklist(newValue);
+        mBlacklist = StatusBarIconController.getIconBlacklist(getContext(), newValue);
         setChecked(!mBlacklist.contains(getKey()));
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 19f0ba2..142fdc2 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -117,7 +117,7 @@
             String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST);
             if (blacklistStr != null) {
                 ArraySet<String> iconBlacklist =
-                        StatusBarIconController.getIconBlacklist(blacklistStr);
+                        StatusBarIconController.getIconBlacklist(mContext, blacklistStr);
 
                 iconBlacklist.add("rotate");
                 iconBlacklist.add("headset");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index fad7cbd..34111e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -44,6 +44,7 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.shared.plugins.PluginManager;
@@ -73,6 +74,7 @@
 public class QSTileHostTest extends SysuiTestCase {
 
     private static String MOCK_STATE_STRING = "MockState";
+    private static final String CUSTOM_TILE_SPEC = "custom(TEST_PKG/.TEST_CLS)";
 
     @Mock
     private StatusBarIconController mIconController;
@@ -92,6 +94,8 @@
     private QSTile.State mMockState;
     @Mock
     private StatusBar mStatusBar;
+    @Mock
+    private CustomTile mCustomTile;
 
     private Handler mHandler;
     private TestableLooper mLooper;
@@ -103,9 +107,8 @@
         mLooper = TestableLooper.get(this);
         mHandler = new Handler(mLooper.getLooper());
         mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
-                mLooper.getLooper(),
-                mPluginManager, mTunerService, mAutoTiles, mDumpController, mBroadcastDispatcher,
-                mStatusBar);
+                mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpController,
+                mBroadcastDispatcher, mStatusBar);
         setUpTileFactory();
         Settings.Secure.putStringForUser(mContext.getContentResolver(), QSTileHost.TILES_SETTING,
                 "", ActivityManager.getCurrentUser());
@@ -121,10 +124,13 @@
                             return new TestTile1(mQSTileHost);
                         case "spec2":
                             return new TestTile2(mQSTileHost);
+                        case CUSTOM_TILE_SPEC:
+                            return mCustomTile;
                         default:
                             return null;
                     }
                 });
+        when(mCustomTile.isAvailable()).thenReturn(true);
     }
 
     @Test
@@ -173,6 +179,39 @@
         assertEquals(output, w.getBuffer().toString());
     }
 
+    @Test
+    public void testDefault() {
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.string.quick_settings_tiles_default, "spec1");
+        mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "default");
+        assertEquals(1, mQSTileHost.getTiles().size());
+        QSTile element = CollectionUtils.firstOrNull(mQSTileHost.getTiles());
+        assertTrue(element instanceof TestTile1);
+    }
+
+    @Test
+    public void testDefaultAndExtra() {
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.string.quick_settings_tiles_default, "spec1");
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.string.config_defaultExtraQuickSettingsTiles, "spec2");
+        mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "default");
+        assertEquals(2, mQSTileHost.getTiles().size());
+        QSTile[] elements = mQSTileHost.getTiles().toArray(new QSTile[0]);
+        assertTrue(elements[0] instanceof TestTile1);
+        assertTrue(elements[1] instanceof TestTile2);
+    }
+
+    @Test
+    public void testExtraCustom() {
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.string.config_defaultExtraQuickSettingsTiles,
+                CUSTOM_TILE_SPEC);
+        mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "default");
+        assertEquals(1, mQSTileHost.getTiles().size());
+        assertEquals(mCustomTile, CollectionUtils.firstOrNull(mQSTileHost.getTiles()));
+    }
+
     private static class TestQSTileHost extends QSTileHost {
         TestQSTileHost(Context context, StatusBarIconController iconController,
                 QSFactoryImpl defaultFactory, Handler mainHandler, Looper bgLooper,
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 61f0b26..9ae477e 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
@@ -797,7 +797,7 @@
                 null,
                 true);
         verify(mMockINotificationManager, times(1)).createConversationNotificationChannelForPackage(
-                anyString(), anyInt(), any(), eq(CONVERSATION_ID));
+                anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID));
     }
 
     @Test
@@ -817,7 +817,7 @@
                 null,
                 true);
         verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage(
-                anyString(), anyInt(), any(), eq(CONVERSATION_ID));
+                anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index f513c2d..c62487a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.notification.row;
 
-import static android.app.Notification.FLAG_BUBBLE;
 import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
@@ -50,13 +49,10 @@
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
-import android.app.PendingIntent;
-import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
 import android.os.IBinder;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -76,9 +72,6 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.bubbles.BubblesTestActivity;
-import com.android.systemui.statusbar.SbnBuilder;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -116,8 +109,6 @@
     private Set<NotificationChannel> mDefaultNotificationChannelSet = new HashSet<>();
     private StatusBarNotification mSbn;
     private NotificationEntry mEntry;
-    private StatusBarNotification mBubbleSbn;
-    private NotificationEntry mBubbleEntry;
 
     @Rule
     public MockitoRule mockito = MockitoJUnit.rule();
@@ -131,8 +122,6 @@
     private NotificationBlockingHelperManager mBlockingHelperManager;
     @Mock
     private VisualStabilityManager mVisualStabilityManager;
-    @Mock
-    private BubbleController mBubbleController;
 
     @Before
     public void setUp() throws Exception {
@@ -143,7 +132,6 @@
 
         mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
         mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
-        mDependency.injectTestDependency(BubbleController.class, mBubbleController);
         // Inflate the layout
         final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
         mNotificationInfo = (NotificationInfo) layoutInflater.inflate(R.layout.notification_info,
@@ -185,15 +173,6 @@
                 new Notification(), UserHandle.CURRENT, null, 0);
         mEntry = new NotificationEntryBuilder().setSbn(mSbn).build();
 
-        PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0,
-                new Intent(mContext, BubblesTestActivity.class), 0);
-        mBubbleSbn = new SbnBuilder(mSbn).setBubbleMetadata(
-                new Notification.BubbleMetadata.Builder()
-                        .setIntent(bubbleIntent)
-                        .setIcon(Icon.createWithResource(mContext, R.drawable.android)).build())
-                .build();
-        mBubbleEntry = new NotificationEntryBuilder().setSbn(mBubbleSbn).build();
-
         Settings.Secure.putInt(mContext.getContentResolver(),
                 NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
     }
@@ -765,7 +744,7 @@
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
                 mNotificationChannelSet,
-                mBubbleEntry,
+                mEntry,
                 null,
                 null,
                 null,
@@ -785,7 +764,7 @@
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
                 mNotificationChannelSet,
-                mBubbleEntry,
+                mEntry,
                 null,
                 null,
                 null,
@@ -797,162 +776,6 @@
     }
 
     @Test
-    public void testBindNotification_bubbleIsSelected() throws Exception {
-        mBubbleEntry.getSbn().getNotification().flags |= FLAG_BUBBLE;
-        mNotificationInfo.bindNotification(
-                mMockPackageManager,
-                mMockINotificationManager,
-                mVisualStabilityManager,
-                TEST_PACKAGE_NAME,
-                mNotificationChannel,
-                mNotificationChannelSet,
-                mBubbleEntry,
-                null,
-                null,
-                null,
-                true,
-                false,
-                IMPORTANCE_DEFAULT,
-                true);
-
-        View bubbleView = mNotificationInfo.findViewById(R.id.bubble);
-        assertEquals(View.VISIBLE, bubbleView.getVisibility());
-        assertTrue(bubbleView.isSelected());
-    }
-
-    @Test
-    public void testBindNotification_whenCanBubble() throws Exception {
-        mNotificationInfo.bindNotification(
-                mMockPackageManager,
-                mMockINotificationManager,
-                mVisualStabilityManager,
-                TEST_PACKAGE_NAME,
-                mNotificationChannel,
-                mNotificationChannelSet,
-                mBubbleEntry,
-                null,
-                null,
-                null,
-                true,
-                false,
-                IMPORTANCE_DEFAULT,
-                true);
-
-        View bubbleView = mNotificationInfo.findViewById(R.id.bubble);
-        assertEquals(View.VISIBLE, bubbleView.getVisibility());
-        assertFalse(bubbleView.isSelected());
-    }
-
-    @Test
-    public void testBindNotification_whenCantBubble() throws Exception {
-        mNotificationInfo.bindNotification(
-                mMockPackageManager,
-                mMockINotificationManager,
-                mVisualStabilityManager,
-                TEST_PACKAGE_NAME,
-                mNotificationChannel,
-                mNotificationChannelSet,
-                mEntry,
-                null,
-                null,
-                null,
-                true,
-                false,
-                IMPORTANCE_DEFAULT,
-                true);
-        View bubbleView = mNotificationInfo.findViewById(R.id.bubble);
-        assertEquals(View.GONE, bubbleView.getVisibility());
-    }
-
-    @Test
-    public void testBubble_promotesBubble() throws Exception {
-        mNotificationInfo.bindNotification(
-                mMockPackageManager,
-                mMockINotificationManager,
-                mVisualStabilityManager,
-                TEST_PACKAGE_NAME,
-                mNotificationChannel,
-                mNotificationChannelSet,
-                mBubbleEntry,
-                null,
-                null,
-                null,
-                true,
-                false,
-                IMPORTANCE_DEFAULT,
-                true);
-
-        assertFalse(mBubbleEntry.isBubble());
-
-        // Promote it
-        mNotificationInfo.findViewById(R.id.bubble).performClick();
-        mNotificationInfo.findViewById(R.id.done).performClick();
-        mNotificationInfo.handleCloseControls(true, false);
-
-        verify(mBubbleController, times(1)).onUserCreatedBubbleFromNotification(mBubbleEntry);
-    }
-
-    @Test
-    public void testAlert_demotesBubble() throws Exception {
-        mBubbleEntry.getSbn().getNotification().flags |= FLAG_BUBBLE;
-
-        mNotificationInfo.bindNotification(
-                mMockPackageManager,
-                mMockINotificationManager,
-                mVisualStabilityManager,
-                TEST_PACKAGE_NAME,
-                mNotificationChannel,
-                mNotificationChannelSet,
-                mBubbleEntry,
-                null,
-                null,
-                null,
-                true,
-                false,
-                IMPORTANCE_DEFAULT,
-                true);
-
-        assertTrue(mBubbleEntry.isBubble());
-
-        // Demote it
-        mNotificationInfo.findViewById(R.id.alert).performClick();
-        mNotificationInfo.findViewById(R.id.done).performClick();
-        mNotificationInfo.handleCloseControls(true, false);
-
-        verify(mBubbleController, times(1)).onUserDemotedBubbleFromNotification(mBubbleEntry);
-    }
-
-    @Test
-    public void testSilence_demotesBubble() throws Exception {
-        mBubbleEntry.getSbn().getNotification().flags |= FLAG_BUBBLE;
-
-        mNotificationInfo.bindNotification(
-                mMockPackageManager,
-                mMockINotificationManager,
-                mVisualStabilityManager,
-                TEST_PACKAGE_NAME,
-                mNotificationChannel,
-                mNotificationChannelSet,
-                mBubbleEntry,
-                null,
-                null,
-                null,
-                true,
-                false,
-                IMPORTANCE_DEFAULT,
-                true);
-
-        assertTrue(mBubbleEntry.isBubble());
-
-        // Demote it
-        mNotificationInfo.findViewById(R.id.silence).performClick();
-        mNotificationInfo.findViewById(R.id.done).performClick();
-        mNotificationInfo.handleCloseControls(true, false);
-
-        verify(mBubbleController, times(1)).onUserDemotedBubbleFromNotification(mBubbleEntry);
-    }
-
-    @Test
     public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
         mNotificationInfo.bindNotification(
                 mMockPackageManager,
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 21100458ad..ad802ff 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -240,6 +240,10 @@
     // Inform the user that EAP failure occurs
     NOTE_WIFI_EAP_FAILURE = 57;
 
+    // Notify the user that their softap disabled because auto shutdown timeout expired.
+    // Package: android
+    NOTE_SOFTAP_AUTO_DISABLED = 58;
+
     // 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 c9fdd5a..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);
         }
@@ -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..00db3294
--- /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/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 2c229b4..f0fa99a 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -31,6 +31,7 @@
 import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_CLEAR;
 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_BACKUP;
 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_RESTORE;
+import static com.android.server.backup.internal.BackupHandler.MSG_RUN_BACKUP;
 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_CLEAR;
 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE;
 import static com.android.server.backup.internal.BackupHandler.MSG_SCHEDULE_BACKUP_PACKAGE;
@@ -111,7 +112,6 @@
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.internal.Operation;
 import com.android.server.backup.internal.PerformInitializeTask;
-import com.android.server.backup.internal.RunBackupReceiver;
 import com.android.server.backup.internal.RunInitializeReceiver;
 import com.android.server.backup.internal.SetupObserver;
 import com.android.server.backup.keyvalue.BackupRequest;
@@ -257,7 +257,6 @@
     // Retry interval for clear/init when the transport is unavailable
     private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR;
 
-    public static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
     public static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
     private static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
     private static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
@@ -319,7 +318,6 @@
     private boolean mSetupComplete;
     private boolean mAutoRestore;
 
-    private final PendingIntent mRunBackupIntent;
     private final PendingIntent mRunInitIntent;
 
     private final ArraySet<String> mPendingInits = new ArraySet<>();  // transport names
@@ -417,7 +415,6 @@
     @Nullable private File mAncestralSerialNumberFile;
 
     private final ContentObserver mSetupObserver;
-    private final BroadcastReceiver mRunBackupReceiver;
     private final BroadcastReceiver mRunInitReceiver;
 
     /**
@@ -566,19 +563,9 @@
         mDataDir = Objects.requireNonNull(dataDir, "dataDir cannot be null");
         mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
 
-        // Receivers for scheduled backups and transport initialization operations.
-        mRunBackupReceiver = new RunBackupReceiver(this);
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(RUN_BACKUP_ACTION);
-        context.registerReceiverAsUser(
-                mRunBackupReceiver,
-                UserHandle.of(userId),
-                filter,
-                android.Manifest.permission.BACKUP,
-                /* scheduler */ null);
-
+        // Receiver for transport initialization.
         mRunInitReceiver = new RunInitializeReceiver(this);
-        filter = new IntentFilter();
+        IntentFilter filter = new IntentFilter();
         filter.addAction(RUN_INITIALIZE_ACTION);
         context.registerReceiverAsUser(
                 mRunInitReceiver,
@@ -587,16 +574,6 @@
                 android.Manifest.permission.BACKUP,
                 /* scheduler */ null);
 
-        Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
-        backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        mRunBackupIntent =
-                PendingIntent.getBroadcastAsUser(
-                        context,
-                        /* requestCode */ 0,
-                        backupIntent,
-                        /* flags */ 0,
-                        UserHandle.of(userId));
-
         Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
         initIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
         mRunInitIntent =
@@ -659,7 +636,6 @@
         mAgentTimeoutParameters.stop();
         mConstants.stop();
         mContext.getContentResolver().unregisterContentObserver(mSetupObserver);
-        mContext.unregisterReceiver(mRunBackupReceiver);
         mContext.unregisterReceiver(mRunInitReceiver);
         mContext.unregisterReceiver(mPackageTrackingReceiver);
         mBackupHandler.stop();
@@ -2538,15 +2514,38 @@
                 KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
             } else {
                 if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
-                    // Fire the intent that kicks off the whole shebang...
-                    try {
-                        mRunBackupIntent.send();
-                    } catch (PendingIntent.CanceledException e) {
-                        // should never happen
-                        Slog.e(TAG, "run-backup intent cancelled!");
+
+                synchronized (getQueueLock()) {
+                    if (getPendingInits().size() > 0) {
+                        // If there are pending init operations, we process those and then settle
+                        // into the usual periodic backup schedule.
+                        if (MORE_DEBUG) {
+                            Slog.v(TAG, "Init pending at scheduled backup");
+                        }
+                        try {
+                            getAlarmManager().cancel(mRunInitIntent);
+                            mRunInitIntent.send();
+                        } catch (PendingIntent.CanceledException ce) {
+                            Slog.w(TAG, "Run init intent cancelled");
+                        }
+                        return;
                     }
-                    // ...and cancel any pending scheduled job, because we've just superseded it
-                    KeyValueBackupJob.cancel(mUserId, mContext);
+                }
+
+                // Don't run backups if we're disabled or not yet set up.
+                if (!isEnabled() || !isSetupComplete()) {
+                    Slog.w(
+                            TAG,
+                            "Backup pass but enabled="  + isEnabled()
+                                    + " setupComplete=" + isSetupComplete());
+                    return;
+                }
+
+                // Fire the msg that kicks off the whole shebang...
+                Message message = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
+                mBackupHandler.sendMessage(message);
+                // ...and cancel any pending scheduled job, because we've just superseded it
+                KeyValueBackupJob.cancel(mUserId, mContext);
             }
         } finally {
             Binder.restoreCallingIdentity(oldId);
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index b06fc52..05396f3 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -158,10 +158,6 @@
                                 .disposeOfTransportClient(transportClient, callerLogString);
                     }
                     Slog.v(TAG, "Backup requested but no transport available");
-                    synchronized (backupManagerService.getQueueLock()) {
-                        backupManagerService.setBackupRunning(false);
-                    }
-                    backupManagerService.getWakelock().release();
                     break;
                 }
 
@@ -169,6 +165,21 @@
                 List<String> queue = new ArrayList<>();
                 DataChangedJournal oldJournal = backupManagerService.getJournal();
                 synchronized (backupManagerService.getQueueLock()) {
+                    // Don't run backups if one is already running.
+                    if (backupManagerService.isBackupRunning()) {
+                        Slog.i(TAG, "Backup time but one already running");
+                        return;
+                    }
+
+                    if (DEBUG) {
+                        Slog.v(TAG, "Running a backup pass");
+                    }
+
+                    // Acquire the wakelock and pass it to the backup thread. It will be released
+                    // once backup concludes.
+                    backupManagerService.setBackupRunning(true);
+                    backupManagerService.getWakelock().acquire();
+
                     // Do we have any work to do?  Construct the work queue
                     // then release the synchronization lock to actually run
                     // the backup.
diff --git a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
deleted file mode 100644
index d37b106..0000000
--- a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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
- */
-
-package com.android.server.backup.internal;
-
-import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.UserBackupManagerService.RUN_BACKUP_ACTION;
-import static com.android.server.backup.internal.BackupHandler.MSG_RUN_BACKUP;
-
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.Message;
-import android.util.Slog;
-
-import com.android.server.backup.UserBackupManagerService;
-
-/**
- * A {@link BroadcastReceiver} for the action {@link UserBackupManagerService#RUN_BACKUP_ACTION}
- * that runs an immediate backup operation if eligible.
- */
-public class RunBackupReceiver extends BroadcastReceiver {
-    private final UserBackupManagerService mUserBackupManagerService;
-
-    public RunBackupReceiver(UserBackupManagerService userBackupManagerService) {
-        mUserBackupManagerService = userBackupManagerService;
-    }
-
-    /**
-     * Run a backup pass if we're eligible. We're eligible if the following conditions are met:
-     *
-     * <ul>
-     *   <li>No transports are pending initialization (otherwise we kick off an initialization
-     *       operation instead).
-     *   <li>Backup is enabled for the user.
-     *   <li>The user has completed setup.
-     *   <li>No backup operation is currently running for the user.
-     * </ul>
-     */
-    public void onReceive(Context context, Intent intent) {
-        if (!RUN_BACKUP_ACTION.equals(intent.getAction())) {
-            return;
-        }
-
-        synchronized (mUserBackupManagerService.getQueueLock()) {
-            if (mUserBackupManagerService.getPendingInits().size() > 0) {
-                // If there are pending init operations, we process those and then settle into the
-                // usual periodic backup schedule.
-                if (MORE_DEBUG) {
-                    Slog.v(TAG, "Init pending at scheduled backup");
-                }
-                try {
-                    PendingIntent runInitIntent = mUserBackupManagerService.getRunInitIntent();
-                    mUserBackupManagerService.getAlarmManager().cancel(runInitIntent);
-                    runInitIntent.send();
-                } catch (PendingIntent.CanceledException ce) {
-                    Slog.w(TAG, "Run init intent cancelled");
-                }
-            } else {
-                // Don't run backups if we're disabled or not yet set up.
-                if (!mUserBackupManagerService.isEnabled()
-                        || !mUserBackupManagerService.isSetupComplete()) {
-                    Slog.w(
-                            TAG,
-                            "Backup pass but enabled="
-                                    + mUserBackupManagerService.isEnabled()
-                                    + " setupComplete="
-                                    + mUserBackupManagerService.isSetupComplete());
-                    return;
-                }
-
-                // Don't run backups if one is already running.
-                if (mUserBackupManagerService.isBackupRunning()) {
-                    Slog.i(TAG, "Backup time but one already running");
-                    return;
-                }
-
-                if (DEBUG) {
-                    Slog.v(TAG, "Running a backup pass");
-                }
-
-                // Acquire the wakelock and pass it to the backup thread. It will be released once
-                // backup concludes.
-                mUserBackupManagerService.setBackupRunning(true);
-                mUserBackupManagerService.getWakelock().acquire();
-
-                Handler backupHandler = mUserBackupManagerService.getBackupHandler();
-                Message message = backupHandler.obtainMessage(MSG_RUN_BACKUP);
-                backupHandler.sendMessage(message);
-            }
-        }
-    }
-}
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/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/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index bc7307b..74c1e63 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.content;
 
+import static android.content.PermissionChecker.PERMISSION_GRANTED;
+
 import android.Manifest;
 import android.accounts.Account;
 import android.annotation.Nullable;
@@ -1212,7 +1214,7 @@
     @RequiresPermission(android.Manifest.permission.CACHE_CONTENT)
     public void putCache(String packageName, Uri key, Bundle value, int userId) {
         Bundle.setDefusable(value, true);
-        enforceCrossUserPermission(userId, TAG);
+        enforceNonFullCrossUserPermission(userId, TAG);
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
         mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
                 packageName);
@@ -1234,7 +1236,7 @@
     @Override
     @RequiresPermission(android.Manifest.permission.CACHE_CONTENT)
     public Bundle getCache(String packageName, Uri key, int userId) {
-        enforceCrossUserPermission(userId, TAG);
+        enforceNonFullCrossUserPermission(userId, TAG);
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
         mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
                 packageName);
@@ -1302,6 +1304,30 @@
         }
     }
 
+    /**
+     * Checks if the request is from the system or an app that has {@code INTERACT_ACROSS_USERS} or
+     * {@code INTERACT_ACROSS_USERS_FULL} permission, if the {@code userHandle} is not for the
+     * caller.
+     *
+     * @param userHandle the user handle of the user we want to act on behalf of.
+     * @param message the message to log on security exception.
+     */
+    private void enforceNonFullCrossUserPermission(int userHandle, String message) {
+        final int callingUser = UserHandle.getCallingUserId();
+        if (callingUser == userHandle) {
+            return;
+        }
+
+        int interactAcrossUsersState = mContext.checkCallingOrSelfPermission(
+                Manifest.permission.INTERACT_ACROSS_USERS);
+        if (interactAcrossUsersState == PERMISSION_GRANTED) {
+            return;
+        }
+
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
+    }
+
     private static int normalizeSyncable(int syncable) {
         if (syncable > 0) {
             return SyncStorageEngine.AuthorityInfo.SYNCABLE;
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 1794df3..7c2ec78 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -491,7 +491,7 @@
             mIoThread.start();
             mIoLooper = mIoThread.getLooper();
         }
-        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
+        mPowerStatus = getInitialPowerStatus();
         mProhibitMode = false;
         mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true);
         mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true);
@@ -538,6 +538,28 @@
         mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, ENABLED);
     }
 
+    private void bootCompleted() {
+        // on boot, if device is interactive, set HDMI CEC state as powered on as well
+        if (mPowerManager.isInteractive() && isPowerStandbyOrTransient()) {
+            onWakeUp();
+        }
+    }
+
+    /**
+     * Returns the initial power status used when the HdmiControlService starts.
+     */
+    @VisibleForTesting
+    int getInitialPowerStatus() {
+        // The initial power status is POWER_STATUS_TRANSIENT_TO_STANDBY.
+        // Once boot completes the service transitions to POWER_STATUS_ON if the device is
+        // interactive.
+        // Quiescent boot is a special boot mode, in which the screen stays off during boot
+        // and the device goes to sleep after boot has finished.
+        // We don't transition to POWER_STATUS_ON initially, as we might be booting in quiescent
+        // mode, during which we don't want to appear powered on to avoid being made active source.
+        return HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
+    }
+
     @VisibleForTesting
     void setCecController(HdmiCecController cecController) {
         mCecController = cecController;
@@ -553,7 +575,9 @@
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
             mTvInputManager = (TvInputManager) getContext().getSystemService(
                     Context.TV_INPUT_SERVICE);
-            mPowerManager = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
+            mPowerManager = getContext().getSystemService(PowerManager.class);
+        } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
+            runOnServiceThread(this::bootCompleted);
         }
     }
 
@@ -579,9 +603,7 @@
      * Called when the initialization of local devices is complete.
      */
     private void onInitializeCecComplete(int initiatedBy) {
-        if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) {
-            mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
-        }
+        updatePowerStatusOnInitializeCecComplete();
         mWakeUpMessageReceived = false;
 
         if (isTvDeviceEnabled()) {
@@ -606,6 +628,17 @@
         }
     }
 
+    /**
+     * Updates the power status once the initialization of local devices is complete.
+     */
+    private void updatePowerStatusOnInitializeCecComplete() {
+        if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) {
+            mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
+        } else if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) {
+            mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
+        }
+    }
+
     private void registerContentObserver() {
         ContentResolver resolver = getContext().getContentResolver();
         String[] settings = new String[] {
@@ -2667,6 +2700,13 @@
     }
 
     @ServiceThreadOnly
+    @VisibleForTesting
+    void setPowerStatus(int powerStatus) {
+        assertRunOnServiceThread();
+        mPowerStatus = powerStatus;
+    }
+
+    @ServiceThreadOnly
     boolean isPowerOnOrTransient() {
         assertRunOnServiceThread();
         return mPowerStatus == HdmiControlManager.POWER_STATUS_ON
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
index 6afadba..f5ed975 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
@@ -56,6 +56,9 @@
 
 /** A helper class to serialize rules from the {@link Rule} model to Binary representation. */
 public class RuleBinarySerializer implements RuleSerializer {
+    static final int TOTAL_RULE_SIZE_LIMIT = 200000;
+    static final int INDEXED_RULE_SIZE_LIMIT = 100000;
+    static final int NONINDEXED_RULE_SIZE_LIMIT = 1000;
 
     // Get the byte representation for a list of rules.
     @Override
@@ -79,10 +82,23 @@
             OutputStream indexingFileOutputStream)
             throws RuleSerializeException {
         try {
+            if (rules == null) {
+                throw new IllegalArgumentException("Null rules cannot be serialized.");
+            }
+
+            if (rules.size() > TOTAL_RULE_SIZE_LIMIT) {
+                throw new IllegalArgumentException("Too many rules provided.");
+            }
+
             // Determine the indexing groups and the order of the rules within each indexed group.
             Map<Integer, Map<String, List<Rule>>> indexedRules =
                     RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets(rules);
 
+            // Validate the rule blocks are not larger than expected limits.
+            verifySize(indexedRules.get(PACKAGE_NAME_INDEXED), INDEXED_RULE_SIZE_LIMIT);
+            verifySize(indexedRules.get(APP_CERTIFICATE_INDEXED), INDEXED_RULE_SIZE_LIMIT);
+            verifySize(indexedRules.get(NOT_INDEXED), NONINDEXED_RULE_SIZE_LIMIT);
+
             // Serialize the rules.
             ByteTrackedOutputStream ruleFileByteTrackedOutputStream =
                     new ByteTrackedOutputStream(rulesFileOutputStream);
@@ -112,6 +128,16 @@
         }
     }
 
+    private void verifySize(Map<String, List<Rule>> ruleListMap, int ruleSizeLimit) {
+        int totalRuleCount =
+                ruleListMap.values().stream()
+                        .map(list -> list.size())
+                        .collect(Collectors.summingInt(Integer::intValue));
+        if (totalRuleCount > ruleSizeLimit) {
+            throw new IllegalArgumentException("Too many rules provided in the indexing group.");
+        }
+    }
+
     private void serializeRuleFileMetadata(
             Optional<Integer> formatVersion, ByteTrackedOutputStream outputStream)
             throws IOException {
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index b186771..1cd8aad 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.media.MediaRoute2ProviderInfo;
 import android.media.RoutingSessionInfo;
+import android.os.Bundle;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -49,7 +50,8 @@
         mCallback = callback;
     }
 
-    public abstract void requestCreateSession(String packageName, String routeId, long requestId);
+    public abstract void requestCreateSession(String packageName, String routeId, long requestId,
+            @Nullable Bundle sessionHints);
     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 4b992be..9761461 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -26,6 +26,7 @@
 import android.media.MediaRoute2ProviderInfo;
 import android.media.MediaRoute2ProviderService;
 import android.media.RoutingSessionInfo;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
@@ -73,9 +74,11 @@
     }
 
     @Override
-    public void requestCreateSession(String packageName, String routeId, long requestId) {
+    public void requestCreateSession(String packageName, String routeId, long requestId,
+            Bundle sessionHints) {
         if (mConnectionReady) {
-            mActiveConnection.requestCreateSession(packageName, routeId, requestId);
+            mActiveConnection.requestCreateSession(
+                    packageName, routeId, requestId, sessionHints);
             updateBinding();
         }
     }
@@ -427,9 +430,10 @@
             mClient.dispose();
         }
 
-        public void requestCreateSession(String packageName, String routeId, long requestId) {
+        public void requestCreateSession(String packageName, String routeId, long requestId,
+                Bundle sessionHints) {
             try {
-                mProvider.requestCreateSession(packageName, routeId, requestId);
+                mProvider.requestCreateSession(packageName, routeId, requestId, sessionHints);
             } 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 45d50b3..2167530 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -22,6 +22,7 @@
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
@@ -63,12 +64,12 @@
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
- * TODO: Merge this to MediaRouterService once it's finished.
+ * Implements features related to {@link android.media.MediaRouter2} and
+ * {@link android.media.MediaRouter2Manager}.
  */
 class MediaRouter2ServiceImpl {
     private static final String TAG = "MR2ServiceImpl";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    private static final long ROUTE_SELECTION_REQUEST_TIMEOUT_MS = 5000L;
 
     private final Context mContext;
     private final Object mLock = new Object();
@@ -178,14 +179,14 @@
     }
 
     public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route,
-            int requestId) {
+            int requestId, Bundle sessionHints) {
         Objects.requireNonNull(client, "client must not be null");
         Objects.requireNonNull(route, "route must not be null");
 
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                requestCreateSessionLocked(client, route, requestId);
+                requestCreateSessionLocked(client, route, requestId, sessionHints);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -377,6 +378,53 @@
         }
     }
 
+    public void selectClientRoute(IMediaRouter2Manager manager, String sessionId,
+            MediaRoute2Info route) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                selectClientRouteLocked(manager, sessionId, route);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    public void deselectClientRoute(IMediaRouter2Manager manager, String sessionId,
+            MediaRoute2Info route) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                deselectClientRouteLocked(manager, sessionId, route);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    public void transferToClientRoute(IMediaRouter2Manager manager, String sessionId,
+            MediaRoute2Info route) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                transferClientRouteLocked(manager, sessionId, route);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    public void releaseClientSession(IMediaRouter2Manager manager, String sessionId) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                releaseClientSessionLocked(manager, sessionId);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     //TODO: Review this is handling multi-user properly.
     void switchUser() {
         synchronized (mLock) {
@@ -417,6 +465,7 @@
             int uid, int pid, String packageName, int userId, boolean trusted) {
         final IBinder binder = client.asBinder();
         if (mAllClientRecords.get(binder) == null) {
+
             UserRecord userRecord = getOrCreateUserRecordLocked(userId);
             Client2Record clientRecord = new Client2Record(userRecord, client, uid, pid,
                     packageName, trusted);
@@ -446,7 +495,7 @@
     }
 
     private void requestCreateSessionLocked(@NonNull IMediaRouter2Client client,
-            @NonNull MediaRoute2Info route, long requestId) {
+            @NonNull MediaRoute2Info route, long requestId, @Nullable Bundle sessionHints) {
         final IBinder binder = client.asBinder();
         final Client2Record clientRecord = mAllClientRecords.get(binder);
 
@@ -458,7 +507,8 @@
         if (clientRecord != null) {
             clientRecord.mUserRecord.mHandler.sendMessage(
                     obtainMessage(UserHandler::requestCreateSessionOnHandler,
-                            clientRecord.mUserRecord.mHandler, clientRecord, route, requestId));
+                            clientRecord.mUserRecord.mHandler,
+                            clientRecord, route, requestId, sessionHints));
         }
     }
 
@@ -617,9 +667,9 @@
             }
             long uniqueRequestId = toUniqueRequestId(managerRecord.mClientId, requestId);
             if (clientRecord != null && managerRecord.mTrusted) {
-                //TODO: select route feature properly
+                //TODO: Use client's OnCreateSessionListener to send proper session hints.
                 requestCreateSessionLocked(clientRecord.mClient, route,
-                        uniqueRequestId);
+                        uniqueRequestId, null /* sessionHints */);
             }
         }
     }
@@ -653,6 +703,7 @@
         ManagerRecord managerRecord = mAllManagerRecords.get(binder);
 
         if (managerRecord == null) {
+            Slog.w(TAG, "getActiveSessionLocked: Ignoring unknown manager");
             return Collections.emptyList();
         }
 
@@ -676,6 +727,93 @@
         return userRecord;
     }
 
+    private void selectClientRouteLocked(IMediaRouter2Manager manager, String sessionId,
+            MediaRoute2Info route) {
+        final IBinder binder = manager.asBinder();
+        ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+        if (managerRecord == null) {
+            Slog.w(TAG, "selectClientRouteLocked: Ignoring unknown manager.");
+            return;
+        }
+        //TODO: we shouldn't ignore selecting request for unknown clients. (RCN?)
+        Client2Record clientRecord = managerRecord.mUserRecord.mHandler
+                .findClientforSessionLocked(sessionId);
+        if (clientRecord == null) {
+            Slog.w(TAG, "selectClientRouteLocked: Ignoring unknown session.");
+            return;
+        }
+
+        clientRecord.mUserRecord.mHandler.sendMessage(
+                obtainMessage(UserHandler::selectRouteOnHandler,
+                            clientRecord.mUserRecord.mHandler,
+                            clientRecord, sessionId, route));
+    }
+
+    private void deselectClientRouteLocked(IMediaRouter2Manager manager, String sessionId,
+            MediaRoute2Info route) {
+        final IBinder binder = manager.asBinder();
+        ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+        if (managerRecord == null) {
+            Slog.w(TAG, "deselectClientRouteLocked: Ignoring unknown manager.");
+            return;
+        }
+        //TODO: we shouldn't ignore selecting request for unknown clients. (RCN?)
+        Client2Record clientRecord = managerRecord.mUserRecord.mHandler
+                .findClientforSessionLocked(sessionId);
+        if (clientRecord == null) {
+            Slog.w(TAG, "deslectClientRouteLocked: Ignoring unknown session.");
+            return;
+        }
+
+        clientRecord.mUserRecord.mHandler.sendMessage(
+                obtainMessage(UserHandler::deselectRouteOnHandler,
+                        clientRecord.mUserRecord.mHandler,
+                        clientRecord, sessionId, route));
+    }
+
+    private void transferClientRouteLocked(IMediaRouter2Manager manager, String sessionId,
+            MediaRoute2Info route) {
+        final IBinder binder = manager.asBinder();
+        ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+        if (managerRecord == null) {
+            Slog.w(TAG, "transferClientRouteLocked: Ignoring unknown manager.");
+            return;
+        }
+        //TODO: we shouldn't ignore selecting request for unknown clients. (RCN?)
+        Client2Record clientRecord = managerRecord.mUserRecord.mHandler
+                .findClientforSessionLocked(sessionId);
+        if (clientRecord == null) {
+            Slog.w(TAG, "transferClientRouteLocked: Ignoring unknown session.");
+            return;
+        }
+
+        clientRecord.mUserRecord.mHandler.sendMessage(
+                obtainMessage(UserHandler::transferToRouteOnHandler,
+                        clientRecord.mUserRecord.mHandler,
+                        clientRecord, sessionId, route));
+    }
+
+    private void releaseClientSessionLocked(IMediaRouter2Manager manager, String sessionId) {
+        final IBinder binder = manager.asBinder();
+        ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+        if (managerRecord == null) {
+            Slog.w(TAG, "releaseClientSessionLocked: Ignoring unknown manager.");
+            return;
+        }
+
+        Client2Record clientRecord = managerRecord.mUserRecord.mHandler
+                .findClientforSessionLocked(sessionId);
+
+        managerRecord.mUserRecord.mHandler.sendMessage(
+                obtainMessage(UserHandler::releaseSessionOnHandler,
+                        managerRecord.mUserRecord.mHandler,
+                        clientRecord, sessionId));
+    }
+
     private void disposeUserIfNeededLocked(UserRecord userRecord) {
         // If there are no records left and the user is no longer current then go ahead
         // and purge the user record and all of its associated state.  If the user is current
@@ -896,6 +1034,11 @@
                     this, provider, sessionInfo));
         }
 
+        @Nullable
+        public Client2Record findClientforSessionLocked(@NonNull String sessionId) {
+            return mSessionToClientMap.get(sessionId);
+        }
+
         //TODO: notify session info updates
         private void onProviderStateChangedOnHandler(MediaRoute2Provider provider) {
             int providerIndex = getProviderInfoIndex(provider.getUniqueId());
@@ -980,7 +1123,7 @@
         }
 
         private void requestCreateSessionOnHandler(Client2Record clientRecord,
-                MediaRoute2Info route, long requestId) {
+                MediaRoute2Info route, long requestId, @Nullable Bundle sessionHints) {
 
             final MediaRoute2Provider provider = findProvider(route.getProviderId());
             if (provider == null) {
@@ -996,7 +1139,7 @@
             mSessionCreationRequests.add(request);
 
             provider.requestCreateSession(clientRecord.mPackageName, route.getOriginalId(),
-                    requestId);
+                    requestId, sessionHints);
         }
 
         private void selectRouteOnHandler(@NonNull Client2Record clientRecord,
@@ -1128,9 +1271,10 @@
         private void onSessionCreatedOnHandler(@NonNull MediaRoute2Provider provider,
                 @NonNull RoutingSessionInfo sessionInfo, long requestId) {
 
+            notifySessionCreatedToManagers(getManagers(), sessionInfo);
+
             if (requestId == MediaRoute2ProviderService.REQUEST_ID_UNKNOWN) {
                 // The session is created without any matching request.
-                // TODO: Tell managers for the session creation
                 return;
             }
 
@@ -1176,7 +1320,6 @@
             notifySessionCreated(matchingRequest.mClientRecord,
                     sessionInfo, toClientRequestId(requestId));
             mSessionToClientMap.put(sessionInfo.getId(), client2Record);
-            // TODO: Tell managers for the session creation
         }
 
         private void onSessionCreationFailedOnHandler(@NonNull MediaRoute2Provider provider,
@@ -1205,29 +1348,29 @@
 
         private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider,
                 @NonNull RoutingSessionInfo sessionInfo) {
+            List<IMediaRouter2Manager> managers = getManagers();
+            notifySessionInfosChangedToManagers(managers);
 
             Client2Record client2Record = mSessionToClientMap.get(
                     sessionInfo.getId());
             if (client2Record == null) {
                 Slog.w(TAG, "No matching client found for session=" + sessionInfo);
-                // TODO: Tell managers for the session update
                 return;
             }
             notifySessionInfoChanged(client2Record, sessionInfo);
-            // TODO: Tell managers for the session update
         }
 
         private void onSessionReleasedOnHandler(@NonNull MediaRoute2Provider provider,
                 @NonNull RoutingSessionInfo sessionInfo) {
+            List<IMediaRouter2Manager> managers = getManagers();
+            notifySessionInfosChangedToManagers(managers);
 
             Client2Record client2Record = mSessionToClientMap.get(sessionInfo.getId());
             if (client2Record == null) {
                 Slog.w(TAG, "No matching client found for session=" + sessionInfo);
-                // TODO: Tell managers for the session release
                 return;
             }
             notifySessionReleased(client2Record, sessionInfo);
-            // TODO: Tell managers for the session release
         }
 
         private void notifySessionCreated(Client2Record clientRecord,
@@ -1333,11 +1476,6 @@
             }
         }
 
-        // TODO: Remove notifyRouteSelected* methods
-        private void notifyRouteSelectedToClient(IMediaRouter2Client client,
-                MediaRoute2Info route, int reason, Bundle controlHints) {
-        }
-
         private void notifyRoutesAddedToClients(List<IMediaRouter2Client> clients,
                 List<MediaRoute2Info> routes) {
             for (IMediaRouter2Client client : clients) {
@@ -1419,6 +1557,29 @@
             }
         }
 
+        private void notifySessionCreatedToManagers(List<IMediaRouter2Manager> managers,
+                RoutingSessionInfo sessionInfo) {
+            for (IMediaRouter2Manager manager : managers) {
+                try {
+                    manager.notifySessionCreated(sessionInfo);
+                } catch (RemoteException ex) {
+                    Slog.w(TAG, "notifySessionCreatedToManagers: "
+                            + "failed to notify. Manager probably died.", ex);
+                }
+            }
+        }
+
+        private void notifySessionInfosChangedToManagers(List<IMediaRouter2Manager> managers) {
+            for (IMediaRouter2Manager manager : managers) {
+                try {
+                    manager.notifySessionsUpdated();
+                } catch (RemoteException ex) {
+                    Slog.w(TAG, "notifySessionInfosChangedToManagers: "
+                            + "failed to notify. Manager probably died.", ex);
+                }
+            }
+        }
+
         private void updateClientUsage(Client2Record clientRecord) {
             MediaRouter2ServiceImpl service = mServiceRef.get();
             if (service == null) {
@@ -1432,8 +1593,6 @@
             }
             for (IMediaRouter2Manager manager : managers) {
                 try {
-                    manager.notifyRouteSelected(clientRecord.mPackageName,
-                            clientRecord.mSelectedRoute);
                     manager.notifyPreferredFeaturesChanged(clientRecord.mPackageName,
                             clientRecord.mDiscoveryPreference.getPreferredFeatures());
                 } catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index aad9636..5437fad 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -42,6 +42,7 @@
 import android.media.RouteDiscoveryPreference;
 import android.media.RoutingSessionInfo;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -460,8 +461,8 @@
     // Binder call
     @Override
     public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route,
-            int requestId) {
-        mService2.requestCreateSession(client, route, requestId);
+            int requestId, Bundle sessionHints) {
+        mService2.requestCreateSession(client, route, requestId, sessionHints);
     }
 
     // Binder call
@@ -556,6 +557,33 @@
         return mService2.getActiveSessions(manager);
     }
 
+    // Binder call
+    @Override
+    public void selectClientRoute(IMediaRouter2Manager manager, String sessionId,
+            MediaRoute2Info route) {
+        mService2.selectClientRoute(manager, sessionId, route);
+    }
+
+    // Binder call
+    @Override
+    public void deselectClientRoute(IMediaRouter2Manager manager, String sessionId,
+            MediaRoute2Info route) {
+        mService2.deselectClientRoute(manager, sessionId, route);
+    }
+
+    // Binder call
+    @Override
+    public void transferToClientRoute(IMediaRouter2Manager manager, String sessionId,
+            MediaRoute2Info route) {
+        mService2.transferToClientRoute(manager, sessionId, route);
+    }
+
+    // Binder call
+    @Override
+    public void releaseClientSession(IMediaRouter2Manager manager, String sessionId) {
+        mService2.releaseClientSession(manager, sessionId);
+    }
+
     void restoreBluetoothA2dp() {
         try {
             boolean a2dpOn;
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 3759ba9..56c33fe 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -26,6 +26,7 @@
 import android.media.IAudioService;
 import android.media.MediaRoute2Info;
 import android.media.MediaRoute2ProviderInfo;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
@@ -96,7 +97,8 @@
     }
 
     @Override
-    public void requestCreateSession(String packageName, String routeId, long requestId) {
+    public void requestCreateSession(String packageName, String routeId, long requestId,
+            Bundle sessionHints) {
         // Do nothing
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
index eaf120e..0e14364 100644
--- a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
@@ -15,7 +15,10 @@
 */
 package com.android.server.notification;
 
+import android.app.Notification;
+import android.app.NotificationChannel;
 import android.content.Context;
+import android.util.FeatureFlagUtils;
 import android.util.Slog;
 
 /**
@@ -26,8 +29,10 @@
     private static final boolean DBG = false;
 
     private RankingConfig mConfig;
+    private Context mContext;
 
     public void initialize(Context ctx, NotificationUsageStats usageStats) {
+        mContext = ctx;
         if (DBG) Slog.d(TAG, "Initializing  " + getClass().getSimpleName() + ".");
     }
 
@@ -41,11 +46,11 @@
             if (DBG) Slog.d(TAG, "missing config");
             return null;
         }
-
-        record.updateNotificationChannel(mConfig.getConversationNotificationChannel(
+        NotificationChannel updatedChannel = mConfig.getConversationNotificationChannel(
                 record.sbn.getPackageName(),
                 record.sbn.getUid(), record.getChannel().getId(),
-                record.getNotification().getShortcutId(), true, false));
+                record.sbn.getShortcutId(mContext), true, false);
+        record.updateNotificationChannel(updatedChannel);
 
         return null;
     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1d49364..385d84a 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -208,6 +208,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.FeatureFlagUtils;
 import android.util.IntArray;
 import android.util.Log;
 import android.util.Pair;
@@ -3038,7 +3039,7 @@
 
         @Override
         public void createConversationNotificationChannelForPackage(String pkg, int uid,
-                NotificationChannel parentChannel, String conversationId) {
+                String triggeringKey, NotificationChannel parentChannel, String conversationId) {
             enforceSystemOrSystemUI("only system can call this");
             Preconditions.checkNotNull(parentChannel);
             Preconditions.checkNotNull(conversationId);
@@ -3049,6 +3050,8 @@
             conversationChannel.setConversationId(parentId, conversationId);
             createNotificationChannelsImpl(
                     pkg, uid, new ParceledListSlice(Arrays.asList(conversationChannel)));
+            mRankingHandler.requestSort();
+            handleSavePolicyFile();
         }
 
         @Override
@@ -5257,9 +5260,10 @@
         if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
             channelId = (new Notification.TvExtender(notification)).getChannelId();
         }
-        // TODO: flag this behavior
         String shortcutId = notification.getShortcutId();
-        if (shortcutId == null
+        if (FeatureFlagUtils.isEnabled(getContext(),
+                FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ)
+            && shortcutId == null
             && notification.getNotificationStyle() == Notification.MessagingStyle.class) {
             shortcutId = id + tag + NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
         }
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 185d75c..b0c1863 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -16,6 +16,7 @@
 
 package com.android.server.notification;
 
+import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 
@@ -40,6 +41,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.FeatureFlagUtils;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
@@ -139,6 +141,8 @@
     private boolean mAreChannelsBypassingDnd;
     private boolean mHideSilentStatusBarIcons = DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS;
 
+    private boolean mAllowInvalidShortcuts = false;
+
     private static final String BADGING_FORCED_TRUE = "force_badging_true_for_bug";
 
     // STOPSHIP (b/142218092) this should be removed before ship
@@ -164,6 +168,8 @@
         updateBadgingEnabled();
         updateBubblesEnabled();
         syncChannelsBypassingDnd(mContext.getUserId());
+        mAllowInvalidShortcuts = FeatureFlagUtils.isEnabled(mContext,
+                FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ);
     }
 
     public void readXml(XmlPullParser parser, boolean forRestore, int userId)
@@ -266,7 +272,14 @@
                                         }
                                         channel.setImportanceLockedByCriticalDeviceFunction(
                                                 r.defaultAppLockedImportance);
-                                        r.channels.put(id, channel);
+                                        boolean isInvalidShortcutChannel =
+                                                channel.getConversationId() != null &&
+                                                        channel.getConversationId().contains(
+                                                                PLACEHOLDER_CONVERSATION_ID);
+                                        if (mAllowInvalidShortcuts || (!mAllowInvalidShortcuts
+                                                && !isInvalidShortcutChannel)) {
+                                            r.channels.put(id, channel);
+                                        }
                                     }
                                 }
                                 // Delegate
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 66a2b01..2a249d2 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -74,6 +74,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.UserManager.EnforcingUser;
+import android.os.UserManager.QuietModeFlag;
 import android.os.UserManagerInternal;
 import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.os.storage.StorageManager;
@@ -914,7 +915,7 @@
 
     @Override
     public boolean requestQuietModeEnabled(@NonNull String callingPackage, boolean enableQuietMode,
-            @UserIdInt int userId, @Nullable IntentSender target) {
+            @UserIdInt int userId, @Nullable IntentSender target, @QuietModeFlag int flags) {
         Objects.requireNonNull(callingPackage);
 
         if (enableQuietMode && target != null) {
@@ -925,24 +926,24 @@
         ensureCanModifyQuietMode(callingPackage, Binder.getCallingUid(), userId, target != null);
         final long identity = Binder.clearCallingIdentity();
         try {
-            boolean result = false;
             if (enableQuietMode) {
                 setQuietModeEnabled(
                         userId, true /* enableQuietMode */, target, callingPackage);
-                result = true;
-            } else {
-                boolean needToShowConfirmCredential =
-                        mLockPatternUtils.isSecure(userId)
-                                && !StorageManager.isUserKeyUnlocked(userId);
-                if (needToShowConfirmCredential) {
-                    showConfirmCredentialToDisableQuietMode(userId, target);
-                } else {
-                    setQuietModeEnabled(
-                            userId, false /* enableQuietMode */, target, callingPackage);
-                    result = true;
-                }
+                return true;
             }
-            return result;
+            boolean needToShowConfirmCredential =
+                    mLockPatternUtils.isSecure(userId)
+                            && !StorageManager.isUserKeyUnlocked(userId);
+            if (needToShowConfirmCredential) {
+                if ((flags & UserManager.QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED) != 0) {
+                    return false;
+                }
+                showConfirmCredentialToDisableQuietMode(userId, target);
+                return false;
+            }
+            setQuietModeEnabled(
+                    userId, false /* enableQuietMode */, target, callingPackage);
+            return true;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 7fc9fdc0..8460ede 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -678,9 +678,20 @@
         final int powerState;
         synchronized (mLock) {
             if (mBroadcastedInteractiveState == INTERACTIVE_STATE_UNKNOWN) {
-                // Broadcasted power state is unknown.  Send wake up.
-                mPendingWakeUpBroadcast = false;
-                mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;
+                // Broadcasted power state is unknown.
+                // Send wake up or go to sleep.
+                switch (mPendingInteractiveState) {
+                    case INTERACTIVE_STATE_ASLEEP:
+                        mPendingGoToSleepBroadcast = false;
+                        mBroadcastedInteractiveState = INTERACTIVE_STATE_ASLEEP;
+                        break;
+
+                    case INTERACTIVE_STATE_AWAKE:
+                    default:
+                        mPendingWakeUpBroadcast = false;
+                        mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;
+                        break;
+                }
             } else if (mBroadcastedInteractiveState == INTERACTIVE_STATE_AWAKE) {
                 // Broadcasted power state is awake.  Send asleep if needed.
                 if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 93d50b8..c1b71aa 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -346,7 +346,8 @@
     // True if systemReady() has been called.
     private boolean mSystemReady;
 
-    // True if boot completed occurred.  We keep the screen on until this happens.
+    // True if boot completed occurred. We keep the screen on until this happens.
+    // The screen will be off if we are in quiescent mode.
     private boolean mBootCompleted;
 
     // True if auto-suspend mode is enabled.
@@ -864,6 +865,12 @@
                 mBatterySaverStateMachine.onBootCompleted();
                 userActivityNoUpdateLocked(
                         now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
+
+                if (sQuiescent) {
+                    goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
+                            PowerManager.GO_TO_SLEEP_REASON_QUIESCENT,
+                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
+                }
                 updatePowerStateLocked();
             }
         }
@@ -1430,8 +1437,7 @@
                     + ", uid=" + uid);
         }
 
-        if (eventTime < mLastSleepTime || eventTime < mLastWakeTime
-                || !mBootCompleted || !mSystemReady) {
+        if (eventTime < mLastSleepTime || eventTime < mLastWakeTime || !mSystemReady) {
             return false;
         }
 
@@ -1508,7 +1514,7 @@
         }
 
         if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE
-                || !mBootCompleted || !mSystemReady || mForceSuspendActive) {
+                || mForceSuspendActive || !mSystemReady) {
             return false;
         }
 
@@ -1530,6 +1536,10 @@
             mNotifier.onWakeUp(reason, details, reasonUid, opPackageName, opUid);
             userActivityNoUpdateLocked(
                     eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid);
+
+            if (sQuiescent) {
+                mDirty |= DIRTY_QUIESCENT;
+            }
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
         }
@@ -1561,7 +1571,8 @@
         if (eventTime < mLastWakeTime
                 || mWakefulness == WAKEFULNESS_ASLEEP
                 || mWakefulness == WAKEFULNESS_DOZING
-                || !mBootCompleted || !mSystemReady) {
+                || !mSystemReady
+                || !mBootCompleted) {
             return false;
         }
 
@@ -2645,6 +2656,10 @@
                 | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
                 | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_VR_MODE_CHANGED |
                 DIRTY_QUIESCENT)) != 0) {
+            if ((dirty & DIRTY_QUIESCENT) != 0) {
+                sQuiescent = false;
+            }
+
             mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked();
 
             // Determine appropriate screen brightness and auto-brightness adjustments.
@@ -2694,9 +2709,6 @@
                     mRequestWaitForNegativeProximity);
             mRequestWaitForNegativeProximity = false;
 
-            if ((dirty & DIRTY_QUIESCENT) != 0) {
-                sQuiescent = false;
-            }
             if (DEBUG_SPEW) {
                 Slog.d(TAG, "updateDisplayPowerStateLocked: mDisplayReady=" + mDisplayReady
                         + ", policy=" + mDisplayPowerRequest.policy
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 3bc860a..1e85688 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -173,7 +173,6 @@
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -349,14 +348,14 @@
     }
     private void registerWifiBytesTransfer() {
         int tagId = StatsLog.WIFI_BYTES_TRANSFER;
-        PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+        PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {2, 3, 4, 5})
                 .build();
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 metadata,
-                (atomTag, data) -> pullWifiBytesTransfer(atomTag, data),
-                Executors.newSingleThreadExecutor()
+                BackgroundThread.getExecutor(),
+                (atomTag, data) -> pullWifiBytesTransfer(atomTag, data)
         );
     }
 
@@ -441,14 +440,14 @@
 
     private void registerWifiBytesTransferBackground() {
         int tagId = StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG;
-        PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+        PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {3, 4, 5, 6})
                 .build();
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 metadata,
-                (atomTag, data) -> pullWifiBytesTransferBackground(atomTag, data),
-                Executors.newSingleThreadExecutor()
+                BackgroundThread.getExecutor(),
+                (atomTag, data) -> pullWifiBytesTransferBackground(atomTag, data)
         );
     }
 
@@ -479,14 +478,14 @@
 
     private void registerMobileBytesTransfer() {
         int tagId = StatsLog.MOBILE_BYTES_TRANSFER;
-        PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+        PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {2, 3, 4, 5})
                 .build();
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 metadata,
-                (atomTag, data) -> pullMobileBytesTransfer(atomTag, data),
-                Executors.newSingleThreadExecutor()
+                BackgroundThread.getExecutor(),
+                (atomTag, data) -> pullMobileBytesTransfer(atomTag, data)
         );
     }
 
@@ -517,14 +516,14 @@
 
     private void registerMobileBytesTransferBackground() {
         int tagId = StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG;
-        PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+        PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {3, 4, 5, 6})
                 .build();
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 metadata,
-                (atomTag, data) -> pullMobileBytesTransferBackground(atomTag, data),
-                Executors.newSingleThreadExecutor()
+                BackgroundThread.getExecutor(),
+                (atomTag, data) -> pullMobileBytesTransferBackground(atomTag, data)
         );
     }
 
@@ -555,14 +554,14 @@
 
     private void registerBluetoothBytesTransfer() {
         int tagId = StatsLog.BLUETOOTH_BYTES_TRANSFER;
-        PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+        PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {2, 3})
                 .build();
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 metadata,
-                (atomTag, data) -> pullBluetoothBytesTransfer(atomTag, data),
-                Executors.newSingleThreadExecutor()
+                BackgroundThread.getExecutor(),
+                (atomTag, data) -> pullBluetoothBytesTransfer(atomTag, data)
         );
     }
 
@@ -634,8 +633,8 @@
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 /* PullAtomMetadata */ null,
-                (atomTag, data) -> pullKernelWakelock(atomTag, data),
-                Executors.newSingleThreadExecutor()
+                BackgroundThread.getExecutor(),
+                (atomTag, data) -> pullKernelWakelock(atomTag, data)
         );
     }
 
@@ -670,14 +669,14 @@
 
     private void registerCpuTimePerFreq() {
         int tagId = StatsLog.CPU_TIME_PER_FREQ;
-        PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+        PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {3})
                 .build();
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 metadata,
-                (atomTag, data) -> pullCpuTimePerFreq(atomTag, data),
-                Executors.newSingleThreadExecutor()
+                BackgroundThread.getExecutor(),
+                (atomTag, data) -> pullCpuTimePerFreq(atomTag, data)
         );
     }
 
@@ -701,14 +700,14 @@
 
     private void registerCpuTimePerUid() {
         int tagId = StatsLog.CPU_TIME_PER_UID;
-        PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+        PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {2, 3})
                 .build();
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 metadata,
-                (atomTag, data) -> pullCpuTimePerUid(atomTag, data),
-                Executors.newSingleThreadExecutor()
+                BackgroundThread.getExecutor(),
+                (atomTag, data) -> pullCpuTimePerUid(atomTag, data)
         );
     }
 
@@ -730,14 +729,14 @@
         // the throttling is 3sec, handled in
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         int tagId = StatsLog.CPU_TIME_PER_UID_FREQ;
-        PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+        PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {4})
                 .build();
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 metadata,
-                (atomTag, data) -> pullCpuTimeperUidFreq(atomTag, data),
-                Executors.newSingleThreadExecutor()
+                BackgroundThread.getExecutor(),
+                (atomTag, data) -> pullCpuTimeperUidFreq(atomTag, data)
         );
     }
 
@@ -762,14 +761,14 @@
         // the throttling is 3sec, handled in
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         int tagId = StatsLog.CPU_ACTIVE_TIME;
-        PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+        PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {2})
                 .build();
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 metadata,
-                (atomTag, data) -> pullCpuActiveTime(atomTag, data),
-                Executors.newSingleThreadExecutor()
+                BackgroundThread.getExecutor(),
+                (atomTag, data) -> pullCpuActiveTime(atomTag, data)
         );
     }
 
@@ -789,14 +788,14 @@
         // the throttling is 3sec, handled in
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         int tagId = StatsLog.CPU_CLUSTER_TIME;
-        PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+        PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {3})
                 .build();
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 metadata,
-                (atomTag, data) -> pullCpuClusterTime(atomTag, data),
-                Executors.newSingleThreadExecutor()
+                BackgroundThread.getExecutor(),
+                (atomTag, data) -> pullCpuClusterTime(atomTag, data)
         );
     }
 
@@ -913,8 +912,8 @@
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 /* metadata */ null,
-                (atomTag, data) -> pullBluetoothActivityInfo(atomTag, data),
-                Executors.newSingleThreadExecutor()
+                BackgroundThread.getExecutor(),
+                (atomTag, data) -> pullBluetoothActivityInfo(atomTag, data)
         );
     }
 
@@ -949,8 +948,8 @@
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
-                (atomTag, data) -> pullSystemUptime(atomTag, data),
-                Executors.newSingleThreadExecutor()
+                BackgroundThread.getExecutor(),
+                (atomTag, data) -> pullSystemUptime(atomTag, data)
         );
     }
 
@@ -1232,8 +1231,8 @@
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 /* PullAtomMetadata */ null,
-                (atomTag, data) -> pullIonHeapSize(atomTag, data),
-                Executors.newSingleThreadExecutor()
+                BackgroundThread.getExecutor(),
+                (atomTag, data) -> pullIonHeapSize(atomTag, data)
         );
     }
 
@@ -1511,8 +1510,8 @@
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 /* PullAtomMetadata */ null,
-                (atomTag, data) -> pullPowerProfile(atomTag, data),
-                Executors.newSingleThreadExecutor()
+                BackgroundThread.getExecutor(),
+                (atomTag, data) -> pullPowerProfile(atomTag, data)
         );
     }
 
@@ -1681,8 +1680,8 @@
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
-                (atomTag, data) -> pullBuildInformation(atomTag, data),
-                BackgroundThread.getExecutor()
+                BackgroundThread.getExecutor(),
+                (atomTag, data) -> pullBuildInformation(atomTag, data)
         );
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3f3408f..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() {
@@ -6918,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
@@ -7017,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.
@@ -7129,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/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 36e9273..a4297416 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3402,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);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b8b0dbf..14bd72b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -7983,12 +7983,12 @@
         if (!mHasFeature) {
             return false;
         }
-        if (parent) {
-            ActiveAdmin ap = getActiveAdminForCallerLocked(who,
-                    DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA, parent);
-            enforceProfileOwnerOfOrganizationOwnedDevice(ap);
-        }
         synchronized (getLockObject()) {
+            if (parent) {
+                final ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+                        DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA, parent);
+                enforceProfileOwnerOfOrganizationOwnedDevice(ap);
+            }
             if (who != null) {
                 ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
                 return (admin != null) ? admin.disableCamera : false;
@@ -15051,4 +15051,29 @@
             Slog.d(LOG_TAG, message);
         }
     }
+
+    @Override
+    public void setCommonCriteriaModeEnabled(ComponentName admin, boolean enabled) {
+        synchronized (getLockObject()) {
+            getActiveAdminForCallerLocked(admin,
+                    DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER);
+        }
+        mInjector.binderWithCleanCallingIdentity(
+                () -> mInjector.settingsGlobalPutInt(Settings.Global.COMMON_CRITERIA_MODE,
+                        enabled ? 1 : 0));
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_COMMON_CRITERIA_MODE)
+                .setAdmin(admin)
+                .setBoolean(enabled)
+                .write();
+    }
+
+    @Override
+    public boolean isCommonCriteriaModeEnabled(ComponentName admin) {
+        synchronized (getLockObject()) {
+            getActiveAdminForCallerLocked(admin,
+                    DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER);
+        }
+        return mInjector.settingsGlobalGetInt(Settings.Global.COMMON_CRITERIA_MODE, 0) != 0;
+    }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 109a6fd..92bdba0 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -223,8 +223,14 @@
             "com.android.server.usb.UsbService$Lifecycle";
     private static final String MIDI_SERVICE_CLASS =
             "com.android.server.midi.MidiService$Lifecycle";
+    private static final String WIFI_APEX_SERVICE_JAR_PATH =
+            "/apex/com.android.wifi/javalib/wifi-service.jar";
     private static final String WIFI_SERVICE_CLASS =
             "com.android.server.wifi.WifiService";
+    private static final String WIFI_SCANNING_SERVICE_CLASS =
+            "com.android.server.wifi.scanner.WifiScanningService";
+    private static final String WIFI_RTT_SERVICE_CLASS =
+            "com.android.server.wifi.rtt.RttService";
     private static final String WIFI_AWARE_SERVICE_CLASS =
             "com.android.server.wifi.aware.WifiAwareService";
     private static final String WIFI_P2P_SERVICE_CLASS =
@@ -1426,33 +1432,36 @@
                     PackageManager.FEATURE_WIFI)) {
                 // Wifi Service must be started first for wifi-related services.
                 t.traceBegin("StartWifi");
-                mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
+                mSystemServiceManager.startServiceFromJar(
+                        WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
                 t.traceEnd();
                 t.traceBegin("StartWifiScanning");
-                mSystemServiceManager.startService(
-                        "com.android.server.wifi.scanner.WifiScanningService");
+                mSystemServiceManager.startServiceFromJar(
+                        WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
                 t.traceEnd();
             }
 
             if (context.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_WIFI_RTT)) {
                 t.traceBegin("StartRttService");
-                mSystemServiceManager.startService(
-                        "com.android.server.wifi.rtt.RttService");
+                mSystemServiceManager.startServiceFromJar(
+                        WIFI_RTT_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
                 t.traceEnd();
             }
 
             if (context.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_WIFI_AWARE)) {
                 t.traceBegin("StartWifiAware");
-                mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS);
+                mSystemServiceManager.startServiceFromJar(
+                        WIFI_AWARE_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
                 t.traceEnd();
             }
 
             if (context.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_WIFI_DIRECT)) {
                 t.traceBegin("StartWifiP2P");
-                mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
+                mSystemServiceManager.startServiceFromJar(
+                        WIFI_P2P_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
                 t.traceEnd();
             }
 
diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
index 9d384e9..ef0ca66 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -44,8 +44,10 @@
 
     // Include the testing libraries
     libs: [
+        "mockito-robolectric-prebuilt",
         "platform-test-annotations",
         "testng",
+        "truth-prebuilt",
     ],
 
     instrumentation_for: "BackupFrameworksServicesLib",
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/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index bfadeea..632a2c1 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5721,6 +5721,31 @@
                 dpm.getAllCrossProfilePackages());
     }
 
+    public void testSetCommonCriteriaMode_asDeviceOwner() throws Exception {
+        setDeviceOwner();
+
+        dpm.setCommonCriteriaModeEnabled(admin1, true);
+        verify(getServices().settings).settingsGlobalPutInt(
+                Settings.Global.COMMON_CRITERIA_MODE, 1);
+
+        when(getServices().settings.settingsGlobalGetInt(Settings.Global.COMMON_CRITERIA_MODE, 0))
+                .thenReturn(1);
+        assertTrue(dpm.isCommonCriteriaModeEnabled(admin1));
+    }
+
+    public void testSetCommonCriteriaMode_asPoOfOrgOwnedDevice() throws Exception {
+        setupProfileOwner();
+        configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
+
+        dpm.setCommonCriteriaModeEnabled(admin1, true);
+        verify(getServices().settings).settingsGlobalPutInt(
+                Settings.Global.COMMON_CRITERIA_MODE, 1);
+
+        when(getServices().settings.settingsGlobalGetInt(Settings.Global.COMMON_CRITERIA_MODE, 0))
+                .thenReturn(1);
+        assertTrue(dpm.isCommonCriteriaModeEnabled(admin1));
+    }
+
     private void setCrossProfileAppsList(String... packages) {
         when(mContext.getResources()
                 .getStringArray(eq(R.array.cross_profile_apps)))
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 1f66074..9e98427 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -18,6 +18,7 @@
 import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
 import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_PLAYBACK;
 
+import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -25,8 +26,17 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiPortInfo;
+import android.os.IPowerManager;
 import android.os.Looper;
+import android.os.PowerManager;
+import android.os.RemoteException;
 import android.os.test.TestLooper;
 
 import androidx.test.InstrumentationRegistry;
@@ -36,6 +46,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
 
@@ -98,6 +110,7 @@
     }
 
     private static final String TAG = "HdmiControlServiceTest";
+    private Context mContextSpy;
     private HdmiControlService mHdmiControlService;
     private HdmiCecController mHdmiCecController;
     private HdmiCecLocalDeviceMyDevice mMyAudioSystemDevice;
@@ -109,15 +122,24 @@
     private boolean mStandbyMessageReceived;
     private HdmiPortInfo[] mHdmiPortInfo;
 
+    @Mock private IPowerManager mIPowerManagerMock;
+
     @Before
-    public void SetUp() {
-        mHdmiControlService =
-                new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
-                    @Override
-                    boolean isStandbyMessageReceived() {
-                        return mStandbyMessageReceived;
-                    }
-                };
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+
+        PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock, null);
+        when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
+        when(mIPowerManagerMock.isInteractive()).thenReturn(true);
+
+        mHdmiControlService = new HdmiControlService(mContextSpy) {
+            @Override
+            boolean isStandbyMessageReceived() {
+                return mStandbyMessageReceived;
+            }
+        };
         mMyLooper = mTestLooper.getLooper();
 
         mMyAudioSystemDevice =
@@ -198,4 +220,33 @@
         mHdmiControlService.initPortInfo();
         assertThat(mHdmiControlService.pathToPortId(0x1000)).isEqualTo(Constants.INVALID_PORT_ID);
     }
+
+    @Test
+    public void initialPowerStatus_normalBoot_isTransientToStandby() {
+        assertThat(mHdmiControlService.getInitialPowerStatus()).isEqualTo(
+                HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY);
+    }
+
+    @Test
+    public void initialPowerStatus_quiescentBoot_isTransientToStandby() throws RemoteException {
+        when(mIPowerManagerMock.isInteractive()).thenReturn(false);
+        assertThat(mHdmiControlService.getInitialPowerStatus()).isEqualTo(
+                HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY);
+    }
+
+    @Test
+    public void powerStatusAfterBootComplete_normalBoot_isOn() {
+        mHdmiControlService.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
+        mHdmiControlService.onBootPhase(PHASE_BOOT_COMPLETED);
+        assertThat(mHdmiControlService.getPowerStatus()).isEqualTo(
+                HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
+    }
+
+    @Test
+    public void powerStatusAfterBootComplete_quiescentBoot_isStandby() throws RemoteException {
+        when(mIPowerManagerMock.isInteractive()).thenReturn(false);
+        mHdmiControlService.onBootPhase(PHASE_BOOT_COMPLETED);
+        assertThat(mHdmiControlService.getPowerStatus()).isEqualTo(
+                HdmiControlManager.POWER_STATUS_STANDBY);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
index 8ee5f5f..e5cbeee 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
@@ -30,6 +30,8 @@
 import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
 import static com.android.server.integrity.model.IndexingFileConstants.INDEXING_BLOCK_SIZE;
 import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
+import static com.android.server.integrity.serializer.RuleBinarySerializer.INDEXED_RULE_SIZE_LIMIT;
+import static com.android.server.integrity.serializer.RuleBinarySerializer.NONINDEXED_RULE_SIZE_LIMIT;
 import static com.android.server.integrity.utils.TestUtils.getBits;
 import static com.android.server.integrity.utils.TestUtils.getBytes;
 import static com.android.server.integrity.utils.TestUtils.getValueBits;
@@ -112,8 +114,7 @@
 
         assertExpectException(
                 RuleSerializeException.class,
-                /* expectedExceptionMessageRegex= */ "Index buckets cannot be created for null"
-                        + " rule list.",
+                /* expectedExceptionMessageRegex= */ "Null rules cannot be serialized.",
                 () -> binarySerializer.serialize(null, /* formatVersion= */ Optional.empty()));
     }
 
@@ -203,8 +204,8 @@
                         + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
                         + SERIALIZED_END_INDEXING_KEY
                         + getBits(
-                                DEFAULT_FORMAT_VERSION_BYTES.length + getBytes(expectedBits).length,
-                                /* numOfBits= */ 32);
+                        DEFAULT_FORMAT_VERSION_BYTES.length + getBytes(expectedBits).length,
+                        /* numOfBits= */ 32);
         expectedIndexingOutputStream.write(
                 getBytes(
                         expectedIndexingBitsForIndexed
@@ -618,6 +619,131 @@
                 .isEqualTo(expectedIndexingOutputStream.toByteArray());
     }
 
+    @Test
+    public void testBinaryString_totalRuleSizeLimitReached() {
+        int ruleCount = INDEXED_RULE_SIZE_LIMIT - 1;
+        String packagePrefix = "package.name.";
+        String appCertificatePrefix = "app.cert.";
+        String installerNamePrefix = "installer.";
+
+        // Create the rule set with more rules than the system can handle in total.
+        List<Rule> ruleList = new ArrayList();
+        for (int count = 0; count < ruleCount; count++) {
+            ruleList.add(
+                    getRuleWithPackageNameAndSampleInstallerName(
+                            String.format("%s%04d", packagePrefix, count)));
+        }
+        for (int count = 0; count < ruleCount; count++) {
+            ruleList.add(
+                    getRuleWithAppCertificateAndSampleInstallerName(
+                            String.format("%s%04d", appCertificatePrefix, count)));
+        }
+        for (int count = 0; count < NONINDEXED_RULE_SIZE_LIMIT - 1; count++) {
+            ruleList.add(
+                    getNonIndexedRuleWithInstallerName(
+                            String.format("%s%04d", installerNamePrefix, count)));
+        }
+
+        // Serialize the rules.
+        ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
+        ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
+        RuleSerializer binarySerializer = new RuleBinarySerializer();
+
+        assertExpectException(
+                RuleSerializeException.class,
+                "Too many rules provided",
+                () ->
+                        binarySerializer.serialize(
+                                ruleList,
+                                /* formatVersion= */ Optional.empty(),
+                                ruleOutputStream,
+                                indexingOutputStream));
+    }
+
+    @Test
+    public void testBinaryString_tooManyPackageNameIndexedRules() {
+        String packagePrefix = "package.name.";
+
+        // Create a rule set with too many package name indexed rules.
+        List<Rule> ruleList = new ArrayList();
+        for (int count = 0; count < INDEXED_RULE_SIZE_LIMIT + 1; count++) {
+            ruleList.add(
+                    getRuleWithPackageNameAndSampleInstallerName(
+                            String.format("%s%04d", packagePrefix, count)));
+        }
+
+        // Serialize the rules.
+        ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
+        ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
+        RuleSerializer binarySerializer = new RuleBinarySerializer();
+
+        assertExpectException(
+                RuleSerializeException.class,
+                "Too many rules provided in the indexing group.",
+                () ->
+                        binarySerializer.serialize(
+                                ruleList,
+                                /* formatVersion= */ Optional.empty(),
+                                ruleOutputStream,
+                                indexingOutputStream));
+    }
+
+    @Test
+    public void testBinaryString_tooManyAppCertificateIndexedRules() {
+        String appCertificatePrefix = "app.cert.";
+
+        // Create a rule set with too many app certificate indexed rules.
+        List<Rule> ruleList = new ArrayList();
+        for (int count = 0; count < INDEXED_RULE_SIZE_LIMIT + 1; count++) {
+            ruleList.add(
+                    getRuleWithAppCertificateAndSampleInstallerName(
+                            String.format("%s%04d", appCertificatePrefix, count)));
+        }
+
+        // Serialize the rules.
+        ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
+        ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
+        RuleSerializer binarySerializer = new RuleBinarySerializer();
+
+        assertExpectException(
+                RuleSerializeException.class,
+                "Too many rules provided in the indexing group.",
+                () ->
+                        binarySerializer.serialize(
+                                ruleList,
+                                /* formatVersion= */ Optional.empty(),
+                                ruleOutputStream,
+                                indexingOutputStream));
+    }
+
+    @Test
+    public void testBinaryString_tooManyNonIndexedRules() {
+        String installerNamePrefix = "installer.";
+
+        // Create a rule set with too many unindexed rules.
+        List<Rule> ruleList = new ArrayList();
+        for (int count = 0; count < NONINDEXED_RULE_SIZE_LIMIT + 1; count++) {
+            ruleList.add(
+                    getNonIndexedRuleWithInstallerName(
+                            String.format("%s%04d", installerNamePrefix, count)));
+        }
+
+        // Serialize the rules.
+        ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
+        ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
+        RuleSerializer binarySerializer = new RuleBinarySerializer();
+
+        assertExpectException(
+                RuleSerializeException.class,
+                "Too many rules provided in the indexing group.",
+                () ->
+                        binarySerializer.serialize(
+                                ruleList,
+                                /* formatVersion= */ Optional.empty(),
+                                ruleOutputStream,
+                                indexingOutputStream));
+    }
+
     private Rule getRuleWithPackageNameAndSampleInstallerName(String packageName) {
         return new Rule(
                 new CompoundFormula(
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 13643a0..25cef56 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -28,6 +28,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
@@ -754,4 +755,55 @@
         SystemClock.sleep(11);
         assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP);
     }
+
+    @Test
+    public void testBoot_ShouldBeAwake() throws Exception {
+        createService();
+        startSystem();
+
+        assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE);
+        verify(mNotifierMock, never()).onWakefulnessChangeStarted(anyInt(), anyInt(), anyLong());
+    }
+
+    @Test
+    public void testBoot_DesiredScreenPolicyShouldBeBright() throws Exception {
+        createService();
+        startSystem();
+
+        assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+                DisplayPowerRequest.POLICY_BRIGHT);
+    }
+
+    @Test
+    public void testQuiescentBoot_ShouldBeAsleep() throws Exception {
+        when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
+        createService();
+        startSystem();
+
+        assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP);
+        verify(mNotifierMock).onWakefulnessChangeStarted(eq(WAKEFULNESS_ASLEEP), anyInt(),
+                anyLong());
+    }
+
+    @Test
+    public void testQuiescentBoot_DesiredScreenPolicyShouldBeOff() throws Exception {
+        when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
+        createService();
+        assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+                DisplayPowerRequest.POLICY_OFF);
+
+        startSystem();
+        assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+                DisplayPowerRequest.POLICY_OFF);
+    }
+
+    @Test
+    public void testQuiescentBoot_WakeUp_DesiredScreenPolicyShouldBeBright() throws Exception {
+        when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
+        createService();
+        startSystem();
+        forceAwake();
+        assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+                DisplayPowerRequest.POLICY_BRIGHT);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
index f5af3ec..d16c232a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
@@ -18,6 +18,7 @@
 
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.util.FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNull;
@@ -30,6 +31,7 @@
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 
 import com.android.server.UiServiceTestCase;
@@ -52,6 +54,7 @@
     public void testExtractsUpdatedChannel() {
         NotificationChannelExtractor extractor = new NotificationChannelExtractor();
         extractor.setConfig(mConfig);
+        extractor.initialize(mContext, null);
 
         NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
         final Notification.Builder builder = new Notification.Builder(getContext())
@@ -71,4 +74,62 @@
         assertNull(extractor.process(r));
         assertEquals(updatedChannel, r.getChannel());
     }
+
+    @Test
+    public void testInvalidShortcutFlagEnabled_looksUpCorrectChannel() {
+        Settings.Global.putString(
+                mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true");
+
+        NotificationChannelExtractor extractor = new NotificationChannelExtractor();
+        extractor.setConfig(mConfig);
+        extractor.initialize(mContext, null);
+
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
+        final Notification.Builder builder = new Notification.Builder(getContext())
+                .setContentTitle("foo")
+                .setStyle(new Notification.MessagingStyle("name"))
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+        Notification n = builder.build();
+        StatusBarNotification sbn = new StatusBarNotification("", "", 0, "tag", 0,
+                0, n, UserHandle.ALL, null, System.currentTimeMillis());
+        NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+
+        NotificationChannel updatedChannel =
+                new NotificationChannel("a", "", IMPORTANCE_HIGH);
+        when(mConfig.getConversationNotificationChannel(
+                any(), anyInt(), eq("a"), eq(r.sbn.getShortcutId(mContext)), eq(true), eq(false)))
+                .thenReturn(updatedChannel);
+
+        assertNull(extractor.process(r));
+        assertEquals(updatedChannel, r.getChannel());
+    }
+
+    @Test
+    public void testInvalidShortcutFlagDisabled_looksUpCorrectChannel() {
+        Settings.Global.putString(
+                mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
+
+        NotificationChannelExtractor extractor = new NotificationChannelExtractor();
+        extractor.setConfig(mConfig);
+        extractor.initialize(mContext, null);
+
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
+        final Notification.Builder builder = new Notification.Builder(getContext())
+                .setContentTitle("foo")
+                .setStyle(new Notification.MessagingStyle("name"))
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+        Notification n = builder.build();
+        StatusBarNotification sbn = new StatusBarNotification("", "", 0, "tag", 0,
+                0, n, UserHandle.ALL, null, System.currentTimeMillis());
+        NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+
+        NotificationChannel updatedChannel =
+                new NotificationChannel("a", "", IMPORTANCE_HIGH);
+        when(mConfig.getConversationNotificationChannel(
+                any(), anyInt(), eq("a"), eq(null), eq(true), eq(false)))
+                .thenReturn(updatedChannel);
+
+        assertNull(extractor.process(r));
+        assertEquals(updatedChannel, r.getChannel());
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 2ac4642..c6c64c9 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -5834,7 +5834,8 @@
         mBinderService.createNotificationChannels(PKG, new ParceledListSlice(Arrays.asList(
                 orig)));
 
-        mBinderService.createConversationNotificationChannelForPackage(PKG, mUid, orig, "friend");
+        mBinderService.createConversationNotificationChannelForPackage(
+                PKG, mUid, "key", orig, "friend");
 
         NotificationChannel friendChannel = mBinderService.getConversationNotificationChannel(
                 PKG, 0, PKG, original.getId(), false, "friend");
@@ -5869,9 +5870,10 @@
         String conversationId = "friend";
 
         mBinderService.createConversationNotificationChannelForPackage(
-                PKG, mUid, NotificationChannel.CREATOR.createFromParcel(msgParcel), conversationId);
+                PKG, mUid, "key", NotificationChannel.CREATOR.createFromParcel(msgParcel),
+                conversationId);
         mBinderService.createConversationNotificationChannelForPackage(
-                PKG, mUid, NotificationChannel.CREATOR.createFromParcel(callParcel),
+                PKG, mUid, "key", NotificationChannel.CREATOR.createFromParcel(callParcel),
                 conversationId);
 
         NotificationChannel messagesChild = mBinderService.getConversationNotificationChannel(
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 6f16574..c1c74da 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -22,6 +22,7 @@
 import static android.app.NotificationManager.IMPORTANCE_MAX;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.util.FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ;
 
 import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_COUNT_LIMIT;
 
@@ -2858,4 +2859,84 @@
             // good
         }
     }
+
+    @Test
+    public void testPlaceholderConversationId_flagOn() throws Exception {
+        Settings.Global.putString(
+                mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true");
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+
+        final String xml = "<ranking version=\"1\">\n"
+                + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+                + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"foo:placeholder_id\"/>"
+                + "</package>"
+                + "</ranking>";
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+                null);
+        parser.nextTag();
+        mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+        assertNotNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true));
+    }
+
+    @Test
+    public void testPlaceholderConversationId_flagOff() throws Exception {
+        Settings.Global.putString(
+                mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+
+        final String xml = "<ranking version=\"1\">\n"
+                + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+                + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"foo:placeholder_id\"/>"
+                + "</package>"
+                + "</ranking>";
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+                null);
+        parser.nextTag();
+        mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+        assertNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true));
+    }
+
+    @Test
+    public void testNormalConversationId_flagOff() throws Exception {
+        Settings.Global.putString(
+                mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+
+        final String xml = "<ranking version=\"1\">\n"
+                + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+                + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"other\"/>"
+                + "</package>"
+                + "</ranking>";
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+                null);
+        parser.nextTag();
+        mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+        assertNotNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true));
+    }
+
+    @Test
+    public void testNoConversationId_flagOff() throws Exception {
+        Settings.Global.putString(
+                mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+
+        final String xml = "<ranking version=\"1\">\n"
+                + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+                + "<channel id=\"id\" name=\"hi\" importance=\"3\"/>"
+                + "</package>"
+                + "</ranking>";
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+                null);
+        parser.nextTag();
+        mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+        assertNotNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", 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/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/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 56ca8c7..c4bcc17 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3859,21 +3859,20 @@
     }
 
     /**
-     * Return if the current radio is LTE on CDMA. This is a tri-state return value as for a period
-     * of time the mode may be unknown.
+     * Return if the current radio has global mode enabled, meaning it supports
+     * both 3GPP and 3GPP2 radio technologies at the same time.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
-     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}.
      *
-     * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE}
-     * or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
-     *
+     * @return {@code true} if global mode is enabled
+     *         {@code false} if global mode is not enabled or unknown
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    @UnsupportedAppUsage
-    public int getLteOnCdmaMode() {
-        return getLteOnCdmaMode(getSubId());
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isGlobalModeEnabled() {
+        return getLteOnCdmaMode(getSubId()) == PhoneConstants.LTE_ON_CDMA_TRUE;
     }
 
     /**
@@ -3886,7 +3885,7 @@
      * or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @UnsupportedAppUsage
     public int getLteOnCdmaMode(int subId) {
         try {
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/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/media/java/android/media/tv/tuner/frontend/FrontendCallback.java b/wifi/java/android/net/wifi/IScoreChangeCallback.aidl
similarity index 72%
rename from media/java/android/media/tv/tuner/frontend/FrontendCallback.java
rename to wifi/java/android/net/wifi/IScoreChangeCallback.aidl
index 9c4f460..fd23610 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendCallback.java
+++ b/wifi/java/android/net/wifi/IScoreChangeCallback.aidl
@@ -14,17 +14,16 @@
  * limitations under the License.
  */
 
-package android.media.tv.tuner.frontend;
+package android.net.wifi;
 
 /**
- * Frontend Callback.
+ * Interface for Wi-Fi network score callback.
  *
  * @hide
  */
-public interface FrontendCallback {
+oneway interface IScoreChangeCallback
+{
+    void onStatusChange(int sessionId, boolean exiting);
 
-    /**
-     * Invoked when there is a frontend event.
-     */
-    void onEvent(int frontendEventType);
+    void onTriggerUpdateOfWifiUsabilityStats(int sessionId);
 }
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendCallback.java b/wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl
similarity index 68%
copy from media/java/android/media/tv/tuner/frontend/FrontendCallback.java
copy to wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl
index 9c4f460..d9a3b01 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendCallback.java
+++ b/wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl
@@ -14,17 +14,20 @@
  * limitations under the License.
  */
 
-package android.media.tv.tuner.frontend;
+package android.net.wifi;
+
+import android.net.wifi.IScoreChangeCallback;
 
 /**
- * Frontend Callback.
+ * Interface for Wi-Fi connected network scorer.
  *
  * @hide
  */
-public interface FrontendCallback {
+oneway interface IWifiConnectedNetworkScorer
+{
+    void start(int sessionId);
 
-    /**
-     * Invoked when there is a frontend event.
-     */
-    void onEvent(int frontendEventType);
+    void stop(int sessionId);
+
+    void setScoreChangeCallback(IScoreChangeCallback cbImpl);
 }
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 67f1663..5a98ac8 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -35,6 +35,7 @@
 import android.net.wifi.ISuggestionConnectionStatusListener;
 import android.net.wifi.ITrafficStateCallback;
 import android.net.wifi.ITxPacketCountListener;
+import android.net.wifi.IWifiConnectedNetworkScorer;
 import android.net.wifi.ScanResult;
 import android.net.wifi.SoftApConfiguration;
 import android.net.wifi.WifiConfiguration;
@@ -254,4 +255,8 @@
     int calculateSignalLevel(int rssi);
 
     List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(in List<ScanResult> scanResults);
+
+    boolean setWifiConnectedNetworkScorer(in IBinder binder, in IWifiConnectedNetworkScorer scorer);
+
+    void clearWifiConnectedNetworkScorer();
 }
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index ec3de43..64d4eaf 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -5806,4 +5806,186 @@
             return new SparseArray<>();
         }
     }
+
+    /**
+     * Callback interface for framework to receive network status changes and trigger of updating
+     * {@link WifiUsabilityStatsEntry}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface ScoreChangeCallback {
+        /**
+         * Called by applications to indicate network status.
+         *
+         * @param sessionId The ID to indicate current Wi-Fi network connection obtained from
+         *                  {@link WifiConnectedNetworkScorer#start(int)}.
+         * @param isUsable The bit to indicate whether current Wi-Fi network is usable or not.
+         *                 Populated by connected network scorer in applications.
+         */
+        void onStatusChange(int sessionId, boolean isUsable);
+
+        /**
+         * Called by applications to trigger an update of {@link WifiUsabilityStatsEntry}.
+         * To receive update applications need to add WifiUsabilityStatsEntry listener. See
+         * {@link addOnWifiUsabilityStatsListener(Executor, OnWifiUsabilityStatsListener)}.
+         *
+         * @param sessionId The ID to indicate current Wi-Fi network connection obtained from
+         *                  {@link WifiConnectedNetworkScorer#start(int)}.
+         */
+        void onTriggerUpdateOfWifiUsabilityStats(int sessionId);
+    }
+
+    /**
+     * Callback proxy for {@link ScoreChangeCallback} objects.
+     *
+     * @hide
+     */
+    private class ScoreChangeCallbackProxy implements ScoreChangeCallback {
+        private final IScoreChangeCallback mScoreChangeCallback;
+
+        private ScoreChangeCallbackProxy(IScoreChangeCallback callback) {
+            mScoreChangeCallback = callback;
+        }
+
+        @Override
+        public void onStatusChange(int sessionId, boolean isUsable) {
+            try {
+                mScoreChangeCallback.onStatusChange(sessionId, isUsable);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        @Override
+        public void onTriggerUpdateOfWifiUsabilityStats(int sessionId) {
+            try {
+                mScoreChangeCallback.onTriggerUpdateOfWifiUsabilityStats(sessionId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Interface for Wi-Fi connected network scorer. Should be implemented by applications and set
+     * when calling
+     * {@link WifiManager#setWifiConnectedNetworkScorer(Executor, WifiConnectedNetworkScorer)}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface WifiConnectedNetworkScorer {
+        /**
+         * Called by framework to indicate the start of a network connection.
+         * @param sessionId The ID to indicate current Wi-Fi network connection.
+         */
+        void start(int sessionId);
+
+        /**
+         * Called by framework to indicate the end of a network connection.
+         * @param sessionId The ID to indicate current Wi-Fi network connection obtained from
+         *                  {@link WifiConnectedNetworkScorer#start(int)}.
+         */
+        void stop(int sessionId);
+
+        /**
+         * Framework sets callback for score change events after application sets its scorer.
+         * @param cbImpl The instance for {@link WifiManager#ScoreChangeCallback}. Should be
+         * implemented and instantiated by framework.
+         */
+        void setScoreChangeCallback(@NonNull ScoreChangeCallback cbImpl);
+    }
+
+    /**
+     * Callback proxy for {@link WifiConnectedNetworkScorer} objects.
+     *
+     * @hide
+     */
+    private class WifiConnectedNetworkScorerProxy extends IWifiConnectedNetworkScorer.Stub {
+        private Executor mExecutor;
+        private WifiConnectedNetworkScorer mScorer;
+
+        WifiConnectedNetworkScorerProxy(Executor executor, WifiConnectedNetworkScorer scorer) {
+            mExecutor = executor;
+            mScorer = scorer;
+        }
+
+        @Override
+        public void start(int sessionId) {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "WifiConnectedNetworkScorer: " + "start: sessionId=" + sessionId);
+            }
+            Binder.clearCallingIdentity();
+            mExecutor.execute(() -> mScorer.start(sessionId));
+        }
+
+        @Override
+        public void stop(int sessionId) {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "WifiConnectedNetworkScorer: " + "stop: sessionId=" + sessionId);
+            }
+            Binder.clearCallingIdentity();
+            mExecutor.execute(() -> mScorer.stop(sessionId));
+        }
+
+        @Override
+        public void setScoreChangeCallback(IScoreChangeCallback cbImpl) {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "WifiConnectedNetworkScorer: "
+                        + "setScoreChangeCallback: cbImpl=" + cbImpl);
+            }
+            Binder.clearCallingIdentity();
+            mExecutor.execute(() -> mScorer.setScoreChangeCallback(
+                    new ScoreChangeCallbackProxy(cbImpl)));
+        }
+    }
+
+    /**
+     * Set a callback for Wi-Fi connected network scorer.  See {@link WifiConnectedNetworkScorer}.
+     * Only a single scorer can be set. Caller will be invoked periodically by framework to inform
+     * client about start and stop of Wi-Fi connection. Caller can clear a previously set scorer
+     * using {@link clearWifiConnectedNetworkScorer()}.
+     *
+     * @param executor The executor on which callback will be invoked.
+     * @param scorer Scorer for Wi-Fi network implemented by application.
+     * @return true Scorer is set successfully.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE)
+    public boolean setWifiConnectedNetworkScorer(@NonNull @CallbackExecutor Executor executor,
+            @NonNull WifiConnectedNetworkScorer scorer) {
+        if (executor == null) throw new IllegalArgumentException("executor cannot be null");
+        if (scorer == null) throw new IllegalArgumentException("scorer cannot be null");
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "setWifiConnectedNetworkScorer: scorer=" + scorer);
+        }
+        try {
+            return mService.setWifiConnectedNetworkScorer(new Binder(),
+                    new WifiConnectedNetworkScorerProxy(executor, scorer));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Allow caller to clear a previously set scorer. After calling this method,
+     * client will no longer receive information about start and stop of Wi-Fi connection.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE)
+    public void clearWifiConnectedNetworkScorer() {
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "clearWifiConnectedNetworkScorer");
+        }
+        try {
+            mService.clearWifiConnectedNetworkScorer();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/wifi/java/android/net/wifi/wificond/DeviceWiphyCapabilities.java b/wifi/java/android/net/wifi/wificond/DeviceWiphyCapabilities.java
new file mode 100644
index 0000000..de1c760
--- /dev/null
+++ b/wifi/java/android/net/wifi/wificond/DeviceWiphyCapabilities.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.wificond;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.net.wifi.ScanResult;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.util.Objects;
+
+/**
+ * DeviceWiphyCapabilities for wificond
+ *
+ * @hide
+ */
+@SystemApi
+public final class DeviceWiphyCapabilities implements Parcelable {
+    private static final String TAG = "DeviceWiphyCapabilities";
+
+    private boolean m80211nSupported;
+    private boolean m80211acSupported;
+    private boolean m80211axSupported;
+
+    /** public constructor */
+    public DeviceWiphyCapabilities() {
+        m80211nSupported = false;
+        m80211acSupported = false;
+        m80211axSupported = false;
+    }
+
+    /**
+     * Get the IEEE 802.11 standard support
+     *
+     * @param standard the IEEE 802.11 standard to check on its support.
+     *        valid values from {@link ScanResult}'s {@code WIFI_STANDARD_}
+     * @return {@code true} if supported, {@code false} otherwise.
+     */
+    public boolean isWifiStandardSupported(int standard) {
+        switch (standard) {
+            case ScanResult.WIFI_STANDARD_LEGACY:
+                return true;
+            case ScanResult.WIFI_STANDARD_11N:
+                return m80211nSupported;
+            case ScanResult.WIFI_STANDARD_11AC:
+                return m80211acSupported;
+            case ScanResult.WIFI_STANDARD_11AX:
+                return m80211axSupported;
+            default:
+                Log.e(TAG, "isWifiStandardSupported called with invalid standard: " + standard);
+                return false;
+        }
+    }
+
+    /**
+     * Set the IEEE 802.11 standard support
+     *
+     * @param standard the IEEE 802.11 standard to set its support.
+     *        valid values from {@link ScanResult}'s {@code WIFI_STANDARD_}
+     * @param support {@code true} if supported, {@code false} otherwise.
+     */
+    public void setWifiStandardSupport(int standard, boolean support) {
+        switch (standard) {
+            case ScanResult.WIFI_STANDARD_11N:
+                m80211nSupported = support;
+                break;
+            case ScanResult.WIFI_STANDARD_11AC:
+                m80211acSupported = support;
+                break;
+            case ScanResult.WIFI_STANDARD_11AX:
+                m80211axSupported = support;
+                break;
+            default:
+                Log.e(TAG, "setWifiStandardSupport called with invalid standard: " + standard);
+        }
+    }
+
+    /** override comparator */
+    @Override
+    public boolean equals(Object rhs) {
+        if (this == rhs) return true;
+        if (!(rhs instanceof DeviceWiphyCapabilities)) {
+            return false;
+        }
+        DeviceWiphyCapabilities capa = (DeviceWiphyCapabilities) rhs;
+
+        return m80211nSupported == capa.m80211nSupported
+                && m80211acSupported == capa.m80211acSupported
+                && m80211axSupported == capa.m80211axSupported;
+    }
+
+    /** override hash code */
+    @Override
+    public int hashCode() {
+        return Objects.hash(m80211nSupported, m80211acSupported, m80211axSupported);
+    }
+
+    /** implement Parcelable interface */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * implement Parcelable interface
+     * |flags| is ignored.
+     */
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeBoolean(m80211nSupported);
+        out.writeBoolean(m80211acSupported);
+        out.writeBoolean(m80211axSupported);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("m80211nSupported:").append(m80211nSupported ? "Yes" : "No");
+        sb.append("m80211acSupported:").append(m80211acSupported ? "Yes" : "No");
+        sb.append("m80211axSupported:").append(m80211axSupported ? "Yes" : "No");
+        return sb.toString();
+    }
+
+    /** implement Parcelable interface */
+    public static final @NonNull Parcelable.Creator<DeviceWiphyCapabilities> CREATOR =
+            new Parcelable.Creator<DeviceWiphyCapabilities>() {
+        /**
+         * Caller is responsible for providing a valid parcel.
+         */
+        @Override
+        public DeviceWiphyCapabilities createFromParcel(Parcel in) {
+            DeviceWiphyCapabilities capabilities = new DeviceWiphyCapabilities();
+            capabilities.m80211nSupported = in.readBoolean();
+            capabilities.m80211acSupported = in.readBoolean();
+            capabilities.m80211axSupported = in.readBoolean();
+            return capabilities;
+        }
+
+        @Override
+        public DeviceWiphyCapabilities[] newArray(int size) {
+            return new DeviceWiphyCapabilities[size];
+        }
+    };
+}
diff --git a/wifi/java/android/net/wifi/wificond/WifiCondManager.java b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
index f70bdac..4847640 100644
--- a/wifi/java/android/net/wifi/wificond/WifiCondManager.java
+++ b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
@@ -1052,6 +1052,22 @@
     }
 
     /**
+     * Get the device phy capabilities for a given interface
+     */
+    @Nullable public DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String ifaceName) {
+        if (mWificond == null) {
+            Log.e(TAG, "Can not query for device wiphy capabilities at this time");
+            return null;
+        }
+
+        try {
+            return mWificond.getDeviceWiphyCapabilities(ifaceName);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
      * Register the provided callback handler for SoftAp events. Note that the Soft AP itself is
      * configured using {@link #setupInterfaceForSoftApMode(String)}.
      *
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index 1cf3825..08822e2 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -32,6 +32,7 @@
 import android.net.wifi.ISuggestionConnectionStatusListener;
 import android.net.wifi.ITrafficStateCallback;
 import android.net.wifi.ITxPacketCountListener;
+import android.net.wifi.IWifiConnectedNetworkScorer;
 import android.net.wifi.IWifiManager;
 import android.net.wifi.ScanResult;
 import android.net.wifi.SoftApConfiguration;
@@ -600,4 +601,15 @@
             List<ScanResult> scanResults) {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public boolean setWifiConnectedNetworkScorer(IBinder binder,
+            IWifiConnectedNetworkScorer scorer) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void clearWifiConnectedNetworkScorer() {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 983ac82..1ee5374 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -82,6 +82,7 @@
 import android.net.wifi.WifiManager.SoftApCallback;
 import android.net.wifi.WifiManager.SuggestionConnectionStatusListener;
 import android.net.wifi.WifiManager.TrafficStateCallback;
+import android.net.wifi.WifiManager.WifiConnectedNetworkScorer;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
@@ -138,6 +139,7 @@
     @Mock Executor mExecutor;
     @Mock Executor mAnotherExecutor;
     @Mock ActivityManager mActivityManager;
+    @Mock WifiConnectedNetworkScorer mWifiConnectedNetworkScorer;
 
     private Handler mHandler;
     private TestLooper mLooper;
@@ -2230,4 +2232,63 @@
         assertEquals(testResults, mWifiManager
                 .getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(new ArrayList<>()));
     }
+
+    /**
+     * Verify the call to setWifiConnectedNetworkScorer goes to WifiServiceImpl.
+     */
+    @Test
+    public void setWifiConnectedNetworkScorerGoesToWifiServiceImpl() throws Exception {
+        mExecutor = new SynchronousExecutor();
+        mWifiManager.setWifiConnectedNetworkScorer(mExecutor, mWifiConnectedNetworkScorer);
+        verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class),
+                any(IWifiConnectedNetworkScorer.Stub.class));
+    }
+
+    /**
+     * Verify the call to clearWifiConnectedNetworkScorer goes to WifiServiceImpl.
+     */
+    @Test
+    public void clearWifiConnectedNetworkScorerGoesToWifiServiceImpl() throws Exception {
+        mExecutor = new SynchronousExecutor();
+        mWifiManager.setWifiConnectedNetworkScorer(mExecutor, mWifiConnectedNetworkScorer);
+        verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class),
+                any(IWifiConnectedNetworkScorer.Stub.class));
+
+        mWifiManager.clearWifiConnectedNetworkScorer();
+        verify(mWifiService).clearWifiConnectedNetworkScorer();
+    }
+
+    /**
+     * Verify that Wi-Fi connected scorer receives score change callback after registeration.
+     */
+    @Test
+    public void verifyScorerReceiveScoreChangeCallbackAfterRegistration() throws Exception {
+        mExecutor = new SynchronousExecutor();
+        mWifiManager.setWifiConnectedNetworkScorer(mExecutor, mWifiConnectedNetworkScorer);
+        ArgumentCaptor<IWifiConnectedNetworkScorer.Stub> scorerCaptor =
+                ArgumentCaptor.forClass(IWifiConnectedNetworkScorer.Stub.class);
+        verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class),
+                scorerCaptor.capture());
+        scorerCaptor.getValue().setScoreChangeCallback(any());
+        mLooper.dispatchAll();
+        verify(mWifiConnectedNetworkScorer).setScoreChangeCallback(any());
+    }
+
+    /**
+     * Verify that Wi-Fi connected scorer receives session ID when start/stop methods are called.
+     */
+    @Test
+    public void verifyScorerReceiveSessionIdWhenStartStopIsCalled() throws Exception {
+        mExecutor = new SynchronousExecutor();
+        mWifiManager.setWifiConnectedNetworkScorer(mExecutor, mWifiConnectedNetworkScorer);
+        ArgumentCaptor<IWifiConnectedNetworkScorer.Stub> callbackCaptor =
+                ArgumentCaptor.forClass(IWifiConnectedNetworkScorer.Stub.class);
+        verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class),
+                callbackCaptor.capture());
+        callbackCaptor.getValue().start(0);
+        callbackCaptor.getValue().stop(10);
+        mLooper.dispatchAll();
+        verify(mWifiConnectedNetworkScorer).start(0);
+        verify(mWifiConnectedNetworkScorer).stop(10);
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/wificond/DeviceWiphyCapabilitiesTest.java b/wifi/tests/src/android/net/wifi/wificond/DeviceWiphyCapabilitiesTest.java
new file mode 100644
index 0000000..1479acf
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/wificond/DeviceWiphyCapabilitiesTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.wificond;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.wifi.ScanResult;
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.wificond.DeviceWiphyCapabilities}.
+ */
+@SmallTest
+public class DeviceWiphyCapabilitiesTest {
+    @Before
+    public void setUp() {}
+
+    /**
+     *  DeviceWiphyCapabilities object can be serialized and deserialized, while keeping the
+     *  values unchanged.
+     */
+    @Test
+    public void canSerializeAndDeserialize() {
+        DeviceWiphyCapabilities capa = new DeviceWiphyCapabilities();
+
+        capa.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11N, true);
+        capa.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11AC, true);
+        capa.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11AX, false);
+
+        Parcel parcel = Parcel.obtain();
+        capa.writeToParcel(parcel, 0);
+        // Rewind the pointer to the head of the parcel.
+        parcel.setDataPosition(0);
+        DeviceWiphyCapabilities capaDeserialized =
+                DeviceWiphyCapabilities.CREATOR.createFromParcel(parcel);
+
+        assertEquals(capa, capaDeserialized);
+        assertEquals(capa.hashCode(), capaDeserialized.hashCode());
+    }
+}
diff --git a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
index f3867c1..619c95e 100644
--- a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
@@ -38,6 +38,7 @@
 import android.app.AlarmManager;
 import android.app.test.TestAlarmManager;
 import android.content.Context;
+import android.net.wifi.ScanResult;
 import android.net.wifi.SoftApInfo;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiScanner;
@@ -397,7 +398,6 @@
         verify(mWifiScannerImpl).unsubscribeScanEvents();
     }
 
-
     /**
      * Verifies that tearDownInterfaces() returns false when wificond is not started.
      */
@@ -1036,6 +1036,25 @@
         verify(mSendMgmtFrameCallback).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_TIMEOUT);
     }
 
+    /**
+     * Tests getDeviceWiphyCapabililties
+     */
+    @Test
+    public void testGetDeviceWiphyCapabilities() throws Exception {
+        DeviceWiphyCapabilities capaExpected = new DeviceWiphyCapabilities();
+
+        capaExpected.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11N, true);
+        capaExpected.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11AC, true);
+        capaExpected.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11AX, false);
+
+        when(mWificond.getDeviceWiphyCapabilities(TEST_INTERFACE_NAME))
+                .thenReturn(capaExpected);
+
+        DeviceWiphyCapabilities capaActual =
+                mWificondControl.getDeviceWiphyCapabilities(TEST_INTERFACE_NAME);
+        assertEquals(capaExpected, capaActual);
+    }
+
     // Create a ArgumentMatcher which captures a SingleScanSettings parameter and checks if it
     // matches the provided frequency set and ssid set.
     private class ScanMatcher implements ArgumentMatcher<SingleScanSettings> {