Merge "Prepare switching font loading to system server."
diff --git a/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java
index 303c667..2b7af2f 100644
--- a/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java
+++ b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java
@@ -23,11 +23,14 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static org.junit.Assert.assertTrue;
+
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
 import android.inputmethodservice.InputMethodService;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
 import android.os.SystemClock;
 import android.perftests.utils.ManualBenchmarkState;
 import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest;
@@ -88,7 +91,7 @@
             "ISC.onPostLayout"
     };
 
-     /** IMF show methods to log in trace. */
+    /** IMF show methods to log in trace. */
     private String[] mShowMethods = {
             "IC.showRequestFromIme",
             "IC.showRequestFromApi",
@@ -99,7 +102,23 @@
             "IMMS.showSoftInput",
             "IMS.showSoftInput",
             "IMS.startInput",
-            "WMS.showImePostLayout"
+            "WMS.showImePostLayout",
+            "IMS.updateFullscreenMode",
+            "IMS.onComputeInsets",
+            "IMS.showWindow"
+    };
+
+    /** IMF show methods to log in trace. */
+    private String[] mShowMethodsCold = {
+            "IMS.bindInput",
+            "IMS.initializeInternal",
+            "IMS.restartInput",
+            "IMS.onCreate",
+            "IMS.initSoftInputWindow",
+            "IMS.resetStateForNewConfiguration",
+            "IMMS.onServiceConnected",
+            "IMMS.sessionCreated",
+            "IMMS.startInputOrWindowGainedFocus"
     };
 
     /** IMF hide lifecycle methods to log in trace. */
@@ -163,6 +182,7 @@
     public static class BaselineIme extends InputMethodService {
 
         public static final int HEIGHT_DP = 100;
+        private static int sPid;
 
         @Override
         public View onCreateInputView() {
@@ -173,9 +193,14 @@
             view.setPadding(0, 0, 0, 0);
             view.addView(inner, new FrameLayout.LayoutParams(MATCH_PARENT, height));
             inner.setBackgroundColor(0xff01fe10); // green
+            sPid = Process.myPid();
             return view;
         }
 
+        static int getPid() {
+            return sPid;
+        }
+
         static ComponentName getName(Context context) {
             return new ComponentName(context, BaselineIme.class);
         }
@@ -188,8 +213,8 @@
                     flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN
                             | StatsReport.FLAG_MIN | StatsReport.FLAG_MAX
                             | StatsReport.FLAG_COEFFICIENT_VAR))
-    public void testShowIme() throws Throwable {
-        testShowOrHideIme(true /* show */);
+    public void testShowImeWarm() throws Throwable {
+        testShowOrHideImeWarm(true /* show */);
     }
 
     @Test
@@ -200,10 +225,65 @@
                             | StatsReport.FLAG_MIN | StatsReport.FLAG_MAX
                             | StatsReport.FLAG_COEFFICIENT_VAR))
     public void testHideIme() throws Throwable {
-        testShowOrHideIme(false /* show */);
+        testShowOrHideImeWarm(false /* show */);
     }
 
-    private void testShowOrHideIme(final boolean show) throws Throwable {
+    @Test
+    @ManualBenchmarkTest(
+            targetTestDurationNs = 10 * TIME_1_S_IN_NS,
+            statsReport = @StatsReport(
+                    flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN
+                            | StatsReport.FLAG_MIN | StatsReport.FLAG_MAX
+                            | StatsReport.FLAG_COEFFICIENT_VAR))
+    public void testShowImeCold() throws Throwable {
+        mTraceMethods = new TraceMarkParser(
+                buildArray(mCommonMethods, mShowMethods, mShowMethodsCold));
+
+        final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        state.setCustomizedIterations(getProfilingIterations(), this);
+        if (state.isWarmingUp()) {
+            // we don't need to warmup for cold start.
+            return;
+        }
+
+        long measuredTimeNs = 0;
+        while (state.keepRunning(measuredTimeNs)) {
+            killBaselineIme();
+            try (ImeSession imeSession = new ImeSession(BaselineIme.getName(
+                    getInstrumentation().getContext()))) {
+                final AtomicReference<CountDownLatch> latchStart = new AtomicReference<>();
+                final Activity activity = getActivityWithFocus();
+
+                setImeListener(activity, latchStart, null /* latchEnd */);
+                latchStart.set(new CountDownLatch(1));
+
+                if (!mIsTraceStarted) {
+                    startAsyncAtrace();
+                }
+
+                final WindowInsetsController controller =
+                        activity.getWindow().getDecorView().getWindowInsetsController();
+                AtomicLong startTime = new AtomicLong();
+                activity.runOnUiThread(() -> {
+                    startTime.set(SystemClock.elapsedRealtimeNanos());
+                    controller.show(WindowInsets.Type.ime());
+                });
+
+                measuredTimeNs = waitForAnimationStart(latchStart, startTime);
+                mActivityRule.finishActivity();
+            }
+        }
+        stopAsyncAtrace();
+        addResultToState(state);
+    }
+
+    private void killBaselineIme() {
+        assertTrue("PID of test and IME can't be same",
+                Process.myPid() != BaselineIme.getPid());
+        Process.killProcess(BaselineIme.getPid());
+    }
+
+    private void testShowOrHideImeWarm(final boolean show) throws Throwable {
         mTraceMethods = new TraceMarkParser(buildArray(
                 mCommonMethods, show ? mShowMethods : mHideMethods));
         final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index e57359f..5fd45ea 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -141,6 +141,25 @@
     }
 
     /**
+     * Creates a new {@link GlobalSearchSession}.
+     *
+     * <p>This process requires an AppSearch native indexing file system for each user. If it's not
+     * created for this user, the initialization process will create one under user's directory.
+     *
+     * @param executor      Executor on which to invoke the callback.
+     * @param callback      The {@link AppSearchResult}&lt;{@link GlobalSearchSession}&gt; of
+     *                      performing this operation. Or a {@link AppSearchResult} with failure
+     *                      reason code and error information.
+     */
+    public void createGlobalSearchSession(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<AppSearchResult<GlobalSearchSession>> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+        GlobalSearchSession.createGlobalSearchSession(mService, executor, callback);
+    }
+
+    /**
      * Sets the schema being used by documents provided to the {@link #putDocuments} method.
      *
      * <p>The schema provided here is compared to the stored copy of the schema previously supplied
@@ -383,22 +402,26 @@
             @NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
         // TODO(b/146386470): Transmit the result documents as a RemoteStream instead of sending
         //     them in one big list.
-        AndroidFuture<AppSearchResult> searchResultsFuture = new AndroidFuture<>();
+        AndroidFuture<AppSearchResult> future = new AndroidFuture<>();
         try {
-            mService.query(DEFAULT_DATABASE_NAME, queryExpression,
-                    searchSpec.getBundle(), searchResultsFuture);
+            mService.query(DEFAULT_DATABASE_NAME, queryExpression, searchSpec.getBundle(),
+                    new IAppSearchResultCallback.Stub() {
+                        public void onResult(AppSearchResult result) {
+                            future.complete(result);
+                        }
+                    });
+            AppSearchResult<Bundle> bundleResult = getFutureOrThrow(future);
+            if (!bundleResult.isSuccess()) {
+                return AppSearchResult.newFailedResult(bundleResult.getResultCode(),
+                        bundleResult.getErrorMessage());
+            }
+            SearchResultPage searchResultPage = new SearchResultPage(bundleResult.getResultValue());
+            return AppSearchResult.newSuccessfulResult(searchResultPage.getResults());
         } catch (RemoteException e) {
-            searchResultsFuture.completeExceptionally(e);
+            throw e.rethrowFromSystemServer();
+        } catch (Throwable t) {
+            return AppSearchResult.throwableToFailedResult(t);
         }
-
-        // Translate the Bundle into a searchResultPage.
-        AppSearchResult<Bundle> bundleResult = getFutureOrThrow(searchResultsFuture);
-        if (!bundleResult.isSuccess()) {
-            return AppSearchResult.newFailedResult(bundleResult.getResultCode(),
-                    bundleResult.getErrorMessage());
-        }
-        SearchResultPage searchResultPage = new SearchResultPage(bundleResult.getResultValue());
-        return AppSearchResult.newSuccessfulResult(searchResultPage.getResults());
     }
 
     /**
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl
index f0b2996..37ce990 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl
@@ -16,4 +16,4 @@
 package android.app.appsearch;
 
 /** {@hide} */
-parcelable AppSearchResult;
\ No newline at end of file
+parcelable AppSearchResult<ValueType>;
\ No newline at end of file
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index 531436e..9c7ccea 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -270,6 +270,61 @@
     }
 
     /**
+     * Searches a document based on a given query string.
+     *
+     * <p>Currently we support following features in the raw query format:
+     * <ul>
+     *     <li>AND
+     *     <p>AND joins (e.g. “match documents that have both the terms ‘dog’ and
+     *     ‘cat’”).
+     *     Example: hello world matches documents that have both ‘hello’ and ‘world’
+     *     <li>OR
+     *     <p>OR joins (e.g. “match documents that have either the term ‘dog’ or
+     *     ‘cat’”).
+     *     Example: dog OR puppy
+     *     <li>Exclusion
+     *     <p>Exclude a term (e.g. “match documents that do
+     *     not have the term ‘dog’”).
+     *     Example: -dog excludes the term ‘dog’
+     *     <li>Grouping terms
+     *     <p>Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g.
+     *     “match documents that have either ‘dog’ or ‘puppy’, and either ‘cat’ or ‘kitten’”).
+     *     Example: (dog puppy) (cat kitten) two one group containing two terms.
+     *     <li>Property restricts
+     *     <p> Specifies which properties of a document to specifically match terms in (e.g.
+     *     “match documents where the ‘subject’ property contains ‘important’”).
+     *     Example: subject:important matches documents with the term ‘important’ in the
+     *     ‘subject’ property
+     *     <li>Schema type restricts
+     *     <p>This is similar to property restricts, but allows for restricts on top-level document
+     *     fields, such as schema_type. Clients should be able to limit their query to documents of
+     *     a certain schema_type (e.g. “match documents that are of the ‘Email’ schema_type”).
+     *     Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will match documents
+     *     that contain the query term ‘dog’ and are of either the ‘Email’ schema type or the
+     *     ‘Video’ schema type.
+     * </ul>
+     *
+     * <p> This method is lightweight. The heavy work will be done in
+     * {@link SearchResults#getNextPage}.
+     *
+     * @param queryExpression Query String to search.
+     * @param searchSpec      Spec for setting filters, raw query etc.
+     * @param executor        Executor on which to invoke the callback of the following request
+     *                        {@link SearchResults#getNextPage}.
+     * @return The search result of performing this operation.
+     */
+    @NonNull
+    public SearchResults query(
+            @NonNull String queryExpression,
+            @NonNull SearchSpec searchSpec,
+            @NonNull @CallbackExecutor Executor executor) {
+        Objects.requireNonNull(queryExpression);
+        Objects.requireNonNull(searchSpec);
+        Objects.requireNonNull(executor);
+        return new SearchResults(mService, mDatabaseName, queryExpression, searchSpec, executor);
+    }
+
+    /**
      * Removes {@link GenericDocument}s from the index by URI.
      *
      * @param request Request containing URIs to be removed.
@@ -307,6 +362,39 @@
         }
     }
 
-    // TODO(b/162450968) port query() and SearchResults.java to platform.
-    // TODO(b/162450968) port removeByQuery() to platform.
+    /**
+     * Removes {@link GenericDocument}s from the index by Query. Documents will be removed if they
+     * match the {@code queryExpression} in given namespaces and schemaTypes which is set via
+     * {@link SearchSpec.Builder#addNamespace} and {@link SearchSpec.Builder#addSchema}.
+     *
+     * <p> An empty {@code queryExpression} matches all documents.
+     *
+     * <p> An empty set of namespaces or schemaTypes matches all namespaces or schemaTypes in
+     * the current database.
+     *
+     * @param queryExpression Query String to search.
+     * @param searchSpec Defines what and how to remove
+     * @param executor Executor on which to invoke the callback.
+     * @param callback Callback to receive errors resulting from removing the documents. If the
+     *                 operation succeeds, the callback will be invoked with {@code null}.
+     */
+    public void removeByQuery(@NonNull String queryExpression,
+            @NonNull SearchSpec searchSpec,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<AppSearchResult<Void>> callback) {
+        Objects.requireNonNull(queryExpression);
+        Objects.requireNonNull(searchSpec);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+        try {
+            mService.removeByQuery(mDatabaseName, queryExpression, searchSpec.getBundle(),
+                    new IAppSearchResultCallback.Stub() {
+                        public void onResult(AppSearchResult result) {
+                            executor.execute(() -> callback.accept(result));
+                        }
+                    });
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
new file mode 100644
index 0000000..d2aa8ea
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch;
+
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.os.RemoteException;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * This class provides global access to the centralized AppSearch index maintained by the system.
+ *
+ * <p>Apps can retrieve indexed documents through the query API.
+ * @hide
+ */
+public class GlobalSearchSession {
+
+    private final IAppSearchManager mService;
+
+    static void createGlobalSearchSession(
+            @NonNull IAppSearchManager service,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<AppSearchResult<GlobalSearchSession>> callback) {
+        GlobalSearchSession globalSearchSession = new GlobalSearchSession(service);
+        globalSearchSession.initialize(executor, callback);
+    }
+
+    // NOTE: No instance of this class should be created or returned except via initialize().
+    // Once the callback.accept has been called here, the class is ready to use.
+    private void initialize(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<AppSearchResult<GlobalSearchSession>> callback) {
+        try {
+            mService.initialize(new IAppSearchResultCallback.Stub() {
+                public void onResult(AppSearchResult result) {
+                    executor.execute(() -> {
+                        if (result.isSuccess()) {
+                            callback.accept(
+                                    AppSearchResult.newSuccessfulResult(GlobalSearchSession.this));
+                        } else {
+                            callback.accept(result);
+                        }
+                    });
+                }
+            });
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private GlobalSearchSession(@NonNull IAppSearchManager service) {
+        mService = service;
+    }
+
+    /**
+     * Searches across all documents in the storage based on a given query string.
+     *
+     * <p>Currently we support following features in the raw query format:
+     * <ul>
+     *     <li>AND
+     *     <p>AND joins (e.g. “match documents that have both the terms ‘dog’ and
+     *     ‘cat’”).
+     *     Example: hello world matches documents that have both ‘hello’ and ‘world’
+     *     <li>OR
+     *     <p>OR joins (e.g. “match documents that have either the term ‘dog’ or
+     *     ‘cat’”).
+     *     Example: dog OR puppy
+     *     <li>Exclusion
+     *     <p>Exclude a term (e.g. “match documents that do
+     *     not have the term ‘dog’”).
+     *     Example: -dog excludes the term ‘dog’
+     *     <li>Grouping terms
+     *     <p>Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g.
+     *     “match documents that have either ‘dog’ or ‘puppy’, and either ‘cat’ or ‘kitten’”).
+     *     Example: (dog puppy) (cat kitten) two one group containing two terms.
+     *     <li>Property restricts
+     *     <p> Specifies which properties of a document to specifically match terms in (e.g.
+     *     “match documents where the ‘subject’ property contains ‘important’”).
+     *     Example: subject:important matches documents with the term ‘important’ in the
+     *     ‘subject’ property
+     *     <li>Schema type restricts
+     *     <p>This is similar to property restricts, but allows for restricts on top-level document
+     *     fields, such as schema_type. Clients should be able to limit their query to documents of
+     *     a certain schema_type (e.g. “match documents that are of the ‘Email’ schema_type”).
+     *     Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will match documents
+     *     that contain the query term ‘dog’ and are of either the ‘Email’ schema type or the
+     *     ‘Video’ schema type.
+     * </ul>
+     *
+     * <p> This method is lightweight. The heavy work will be done in
+     * {@link SearchResults#getNextPage}.
+     *
+     * @param queryExpression Query String to search.
+     * @param searchSpec      Spec for setting filters, raw query etc.
+     * @param executor        Executor on which to invoke the callback of the following request
+     *                        {@link SearchResults#getNextPage}.
+     * @return The search result of performing this operation.
+     */
+    @NonNull
+    public SearchResults globalQuery(
+            @NonNull String queryExpression,
+            @NonNull SearchSpec searchSpec,
+            @NonNull @CallbackExecutor Executor executor) {
+        Objects.requireNonNull(queryExpression);
+        Objects.requireNonNull(searchSpec);
+        Objects.requireNonNull(executor);
+        return new SearchResults(mService, /*databaseName=*/null, queryExpression,
+                searchSpec, executor);
+    }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index 4a98102..22e00f2 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -85,13 +85,46 @@
      * @param databaseName The databaseName this query for.
      * @param queryExpression String to search for
      * @param searchSpecBundle SearchSpec bundle
-     * @param callback {@link AndroidFuture}&lt;{@link AppSearchResult}&lt;{@link SearchResults}&gt;&gt;
+     * @param callback {@link AppSearchResult}&lt;{@link Bundle}&gt; of performing this
+     *         operation.
      */
     void query(
         in String databaseName,
         in String queryExpression,
         in Bundle searchSpecBundle,
-        in AndroidFuture<AppSearchResult> callback);
+        in IAppSearchResultCallback callback);
+
+    /**
+     * Executes a global query, i.e. over all permitted databases, against the AppSearch index and
+     * returns results.
+     *
+     * @param queryExpression String to search for
+     * @param searchSpecBundle SearchSpec bundle
+     * @param callback {@link AppSearchResult}&lt;{@link Bundle}&gt; of performing this
+     *         operation.
+     */
+    void globalQuery(
+        in String queryExpression,
+        in Bundle searchSpecBundle,
+        in IAppSearchResultCallback callback);
+
+    /**
+     * Fetches the next page of results of a previously executed query. Results can be empty if
+     * next-page token is invalid or all pages have been returned.
+     *
+     * @param nextPageToken The token of pre-loaded results of previously executed query.
+     * @param callback {@link AppSearchResult}&lt;{@link Bundle}&gt; of performing this
+     *                  operation.
+     */
+    void getNextPage(in long nextPageToken, in IAppSearchResultCallback callback);
+
+    /**
+     * Invalidates the next-page token so that no more results of the related query can be returned.
+     *
+     * @param nextPageToken The token of pre-loaded results of previously executed query to be
+     *                      Invalidated.
+     */
+    void invalidateNextPageToken(in long nextPageToken);
 
     /**
      * Removes documents by URI.
@@ -119,13 +152,14 @@
      * @param databaseName The databaseName this query for.
      * @param queryExpression String to search for
      * @param searchSpecBundle SearchSpec bundle
-     * @param callback {@link AndroidFuture}&lt;{@link AppSearchResult}&lt;{@link SearchResults}&gt;&gt;
+     * @param callback {@link IAppSearchResultCallback#onResult} will be called with an
+     *     {@link AppSearchResult}&lt;{@link Void}&gt;.
      */
     void removeByQuery(
         in String databaseName,
         in String queryExpression,
         in Bundle searchSpecBundle,
-        in AndroidFuture<AppSearchResult> callback);
+        in IAppSearchResultCallback callback);
 
     /**
      * Creates and initializes AppSearchImpl for the calling app.
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
index 9f37625..8548d20 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 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.
@@ -16,63 +16,123 @@
 
 package android.app.appsearch;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
 
-import java.util.ArrayList;
+import com.android.internal.util.Preconditions;
+
+import java.io.Closeable;
 import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
- * Structure for transmitting a page of search results across binder.
+ * SearchResults are a returned object from a query API.
+ *
+ * <p>Each {@link SearchResult} contains a document and may contain other fields like snippets
+ * based on request.
+ *
+ * <p>Should close this object after finish fetching results.
+ *
+ * <p>This class is not thread safe.
  * @hide
  */
-public final class SearchResults implements Parcelable {
-    final List<SearchResult> mResults;
-    final long mNextPageToken;
+public class SearchResults implements Closeable {
+    private static final String TAG = "SearchResults";
 
-    public SearchResults(@NonNull List<SearchResult> results, long nextPageToken) {
-        mResults = results;
-        mNextPageToken = nextPageToken;
+    private final IAppSearchManager mService;
+
+    @Nullable
+    private final String mDatabaseName;
+
+    private final String mQueryExpression;
+
+    private final SearchSpec mSearchSpec;
+
+    private final Executor mExecutor;
+
+    private long mNextPageToken;
+
+    private boolean mIsFirstLoad = true;
+
+    SearchResults(@NonNull IAppSearchManager service,
+            @Nullable String databaseName,
+            @NonNull String queryExpression,
+            @NonNull SearchSpec searchSpec,
+            @NonNull @CallbackExecutor Executor executor) {
+        mService = Preconditions.checkNotNull(service);
+        mExecutor = Preconditions.checkNotNull(executor);
+        mDatabaseName = databaseName;
+        mQueryExpression = Preconditions.checkNotNull(queryExpression);
+        mSearchSpec = Preconditions.checkNotNull(searchSpec);
     }
 
-    private SearchResults(@NonNull Parcel in) {
-        List<Bundle> resultBundles = in.readArrayList(/*loader=*/ null);
-        mResults = new ArrayList<>(resultBundles.size());
-        for (int i = 0; i < resultBundles.size(); i++) {
-            SearchResult searchResult = new SearchResult(resultBundles.get(i));
-            mResults.add(searchResult);
+    /**
+     * Gets a whole page of {@link SearchResult}s.
+     *
+     * <p>Re-call this method to get next page of {@link SearchResult}, until it returns an
+     * empty list.
+     *
+     * <p>The page size is set by {@link SearchSpec.Builder#setNumPerPage}.
+     *
+     * @param callback Callback to receive the pending result of performing this operation.
+     */
+    public void getNextPage(@NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) {
+        try {
+            if (mIsFirstLoad) {
+                mIsFirstLoad = false;
+                if (mDatabaseName == null) {
+                    mService.globalQuery(mQueryExpression, mSearchSpec.getBundle(),
+                            wrapCallback(callback));
+                } else {
+                    mService.query(mDatabaseName, mQueryExpression, mSearchSpec.getBundle(),
+                            wrapCallback(callback));
+                }
+            } else {
+                mService.getNextPage(mNextPageToken, wrapCallback(callback));
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
-        mNextPageToken = in.readLong();
     }
 
+    private void invokeCallback(AppSearchResult result,
+            @NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) {
+        if (result.isSuccess()) {
+            try {
+                SearchResultPage searchResultPage =
+                        new SearchResultPage((Bundle) result.getResultValue());
+                mNextPageToken = searchResultPage.getNextPageToken();
+                callback.accept(AppSearchResult.newSuccessfulResult(
+                        searchResultPage.getResults()));
+            } catch (Throwable t) {
+                callback.accept(AppSearchResult.throwableToFailedResult(t));
+            }
+        } else {
+            callback.accept(result);
+        }
+    }
     @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        List<Bundle> resultBundles = new ArrayList<>(mResults.size());
-        for (int i = 0; i < mResults.size(); i++) {
-            resultBundles.add(mResults.get(i).getBundle());
-        }
-        dest.writeList(resultBundles);
-        dest.writeLong(mNextPageToken);
+    public void close() {
+        mExecutor.execute(() -> {
+            try {
+                mService.invalidateNextPageToken(mNextPageToken);
+            } catch (RemoteException e) {
+                Log.d(TAG, "Unable to close the SearchResults", e);
+            }
+        });
     }
 
-    @Override
-    public int describeContents() {
-        return 0;
+    private IAppSearchResultCallback wrapCallback(
+            @NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) {
+        return new IAppSearchResultCallback.Stub() {
+            public void onResult(AppSearchResult result) {
+                mExecutor.execute(() -> invokeCallback(result, callback));
+            }
+        };
     }
-
-    public static final Creator<SearchResults> CREATOR = new Creator<SearchResults>() {
-        @NonNull
-        @Override
-        public SearchResults createFromParcel(@NonNull Parcel in) {
-            return new SearchResults(in);
-        }
-
-        @NonNull
-        @Override
-        public SearchResults[] newArray(int size) {
-            return new SearchResults[size];
-        }
-    };
 }
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 8269799..d5146dd 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -36,7 +36,6 @@
 import android.util.ArraySet;
 import android.util.Log;
 
-import com.android.internal.infra.AndroidFuture;
 import com.android.internal.util.Preconditions;
 import com.android.server.SystemService;
 import com.android.server.appsearch.external.localstorage.AppSearchImpl;
@@ -69,6 +68,7 @@
                 @NonNull IAppSearchResultCallback callback) {
             Preconditions.checkNotNull(databaseName);
             Preconditions.checkNotNull(schemaBundles);
+            Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUidOrThrow();
             int callingUserId = UserHandle.getUserId(callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
@@ -158,13 +158,12 @@
         }
 
         // TODO(sidchhabra): Do this in a threadpool.
-        // TODO(b/162450968) handle pagination after getNextPage and SearchResults is ready.
         @Override
         public void query(
                 @NonNull String databaseName,
                 @NonNull String queryExpression,
                 @NonNull Bundle searchSpecBundle,
-                @NonNull AndroidFuture<AppSearchResult> callback) {
+                @NonNull IAppSearchResultCallback callback) {
             Preconditions.checkNotNull(databaseName);
             Preconditions.checkNotNull(queryExpression);
             Preconditions.checkNotNull(searchSpecBundle);
@@ -179,10 +178,70 @@
                         databaseName,
                         queryExpression,
                         new SearchSpec(searchSpecBundle));
-                callback.complete(
+                invokeCallbackOnResult(callback,
                         AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
             } catch (Throwable t) {
-                callback.complete(throwableToFailedResult(t));
+                invokeCallbackOnError(callback, t);
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
+            }
+        }
+
+        public void globalQuery(
+                @NonNull String queryExpression,
+                @NonNull Bundle searchSpecBundle,
+                @NonNull IAppSearchResultCallback callback) {
+            Preconditions.checkNotNull(queryExpression);
+            Preconditions.checkNotNull(searchSpecBundle);
+            Preconditions.checkNotNull(callback);
+            int callingUid = Binder.getCallingUidOrThrow();
+            int callingUserId = UserHandle.getUserId(callingUid);
+            final long callingIdentity = Binder.clearCallingIdentity();
+            try {
+                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
+                SearchResultPage searchResultPage = impl.globalQuery(
+                        queryExpression,
+                        new SearchSpec(searchSpecBundle));
+                invokeCallbackOnResult(callback,
+                        AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
+            } catch (Throwable t) {
+                invokeCallbackOnError(callback, t);
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
+            }
+        }
+
+        @Override
+        public void getNextPage(long nextPageToken,
+                @NonNull IAppSearchResultCallback callback) {
+            Preconditions.checkNotNull(callback);
+            int callingUid = Binder.getCallingUidOrThrow();
+            int callingUserId = UserHandle.getUserId(callingUid);
+            final long callingIdentity = Binder.clearCallingIdentity();
+            // TODO(b/162450968) check nextPageToken is being advanced by the same uid as originally
+            // opened it
+            try {
+                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
+                SearchResultPage searchResultPage = impl.getNextPage(nextPageToken);
+                invokeCallbackOnResult(callback,
+                        AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
+            } catch (Throwable t) {
+                invokeCallbackOnError(callback, t);
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
+            }
+        }
+
+        @Override
+        public void invalidateNextPageToken(long nextPageToken) {
+            int callingUid = Binder.getCallingUidOrThrow();
+            int callingUserId = UserHandle.getUserId(callingUid);
+            final long callingIdentity = Binder.clearCallingIdentity();
+            try {
+                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
+                impl.invalidateNextPageToken(nextPageToken);
+            } catch (Throwable t) {
+                Log.d(TAG, "Unable to invalidate the query page token", t);
             } finally {
                 Binder.restoreCallingIdentity(callingIdentity);
             }
@@ -225,7 +284,7 @@
                 @NonNull String databaseName,
                 @NonNull String queryExpression,
                 @NonNull Bundle searchSpecBundle,
-                @NonNull AndroidFuture<AppSearchResult> callback) {
+                @NonNull IAppSearchResultCallback callback) {
             Preconditions.checkNotNull(databaseName);
             Preconditions.checkNotNull(queryExpression);
             Preconditions.checkNotNull(searchSpecBundle);
@@ -236,10 +295,11 @@
             try {
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
                 databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
-                impl.removeByQuery(databaseName, queryExpression, new SearchSpec(searchSpecBundle));
-                callback.complete(AppSearchResult.newSuccessfulResult(/*result= */null));
+                impl.removeByQuery(databaseName, queryExpression,
+                        new SearchSpec(searchSpecBundle));
+                invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
             } catch (Throwable t) {
-                callback.complete(throwableToFailedResult(t));
+                invokeCallbackOnError(callback, t);
             } finally {
                 Binder.restoreCallingIdentity(callingIdentity);
             }
@@ -247,6 +307,7 @@
 
         @Override
         public void initialize(@NonNull IAppSearchResultCallback callback) {
+            Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUidOrThrow();
             int callingUserId = UserHandle.getUserId(callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 2c8a558..3597c27 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -462,11 +462,11 @@
     public @NetworkType int getNetworkType() {
         if (networkRequest == null) {
             return NETWORK_TYPE_NONE;
-        } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
+        } else if (networkRequest.hasCapability(NET_CAPABILITY_NOT_METERED)) {
             return NETWORK_TYPE_UNMETERED;
-        } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING)) {
+        } else if (networkRequest.hasCapability(NET_CAPABILITY_NOT_ROAMING)) {
             return NETWORK_TYPE_NOT_ROAMING;
-        } else if (networkRequest.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+        } else if (networkRequest.hasTransport(TRANSPORT_CELLULAR)) {
             return NETWORK_TYPE_CELLULAR;
         } else {
             return NETWORK_TYPE_ANY;
@@ -1558,7 +1558,7 @@
         if (isPersisted) {
             // We can't serialize network specifiers
             if (networkRequest != null
-                    && networkRequest.networkCapabilities.getNetworkSpecifier() != null) {
+                    && networkRequest.getNetworkSpecifier() != null) {
                 throw new IllegalArgumentException(
                         "Network specifiers aren't supported for persistent jobs");
             }
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index d4ea7af..20c77da 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -417,6 +417,9 @@
         @VisibleForTesting
         static final String KEY_LAZY_BATCHING = "lazy_batching";
 
+        private static final String KEY_TIME_TICK_ALLOWED_WHILE_IDLE =
+                "time_tick_allowed_while_idle";
+
         private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
         private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
         private static final long DEFAULT_MAX_INTERVAL = 365 * DateUtils.DAY_IN_MILLIS;
@@ -440,6 +443,7 @@
         private static final long DEFAULT_APP_STANDBY_RESTRICTED_WINDOW = MILLIS_IN_DAY;
 
         private static final boolean DEFAULT_LAZY_BATCHING = true;
+        private static final boolean DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE = true;
 
         // Minimum futurity of a new alarm
         public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
@@ -470,6 +474,7 @@
         public long APP_STANDBY_RESTRICTED_WINDOW = DEFAULT_APP_STANDBY_RESTRICTED_WINDOW;
 
         public boolean LAZY_BATCHING = DEFAULT_LAZY_BATCHING;
+        public boolean TIME_TICK_ALLOWED_WHILE_IDLE = DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE;
 
         private long mLastAllowWhileIdleWhitelistDuration = -1;
 
@@ -557,6 +562,11 @@
                                 migrateAlarmsToNewStoreLocked();
                             }
                             break;
+                        case KEY_TIME_TICK_ALLOWED_WHILE_IDLE:
+                            TIME_TICK_ALLOWED_WHILE_IDLE = properties.getBoolean(
+                                    KEY_TIME_TICK_ALLOWED_WHILE_IDLE,
+                                    DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE);
+                            break;
                         default:
                             if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) {
                                 // The quotas need to be updated in order, so we can't just rely
@@ -690,6 +700,9 @@
             pw.print(KEY_LAZY_BATCHING, LAZY_BATCHING);
             pw.println();
 
+            pw.print(KEY_TIME_TICK_ALLOWED_WHILE_IDLE, TIME_TICK_ALLOWED_WHILE_IDLE);
+            pw.println();
+
             pw.decreaseIndent();
         }
 
@@ -3756,9 +3769,14 @@
             final long tickEventDelay = nextTime - currentTime;
 
             final WorkSource workSource = null; // Let system take blame for time tick events.
+
+            int flags = AlarmManager.FLAG_STANDALONE;
+            flags |= mConstants.TIME_TICK_ALLOWED_WHILE_IDLE ? FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED
+                    : 0;
+
             setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0,
-                    0, null, mTimeTickTrigger, TIME_TICK_TAG, AlarmManager.FLAG_STANDALONE,
-                    workSource, null, Process.myUid(), "android");
+                    0, null, mTimeTickTrigger, TIME_TICK_TAG, flags, workSource, null,
+                    Process.myUid(), "android");
 
             // Finally, remember when we set the tick alarm
             synchronized (mLock) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
index 56b97f2..2b5aab8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
@@ -282,12 +282,13 @@
             String nextDelayPackageName = null;
             boolean ready = false;
             Iterator<JobStatus> it = mTrackedJobs.iterator();
+            final long nowElapsedMillis = sElapsedRealtimeClock.millis();
             while (it.hasNext()) {
                 final JobStatus job = it.next();
                 if (!job.hasTimingDelayConstraint()) {
                     continue;
                 }
-                if (evaluateTimingDelayConstraint(job, sElapsedRealtimeClock.millis())) {
+                if (evaluateTimingDelayConstraint(job, nowElapsedMillis)) {
                     if (canStopTrackingJobLocked(job)) {
                         it.remove();
                     }
diff --git a/cmds/statsd/OWNERS b/cmds/statsd/OWNERS
index a61babf..4e4e119 100644
--- a/cmds/statsd/OWNERS
+++ b/cmds/statsd/OWNERS
@@ -1,9 +1 @@
-jeffreyhuang@google.com
-joeo@google.com
-jtnguyen@google.com
-muhammadq@google.com
-ruchirr@google.com
-singhtejinder@google.com
-tsaichristine@google.com
-yaochen@google.com
-yro@google.com
+baligh@google.com
diff --git a/core/api/current.txt b/core/api/current.txt
index ef397b7..9cec7a6 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -6968,6 +6968,7 @@
     method public boolean grantKeyPairToApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String);
     method public boolean hasCaCertInstalled(@Nullable android.content.ComponentName, byte[]);
     method public boolean hasGrantedPolicy(@NonNull android.content.ComponentName, int);
+    method public boolean hasKeyPair(@NonNull String);
     method public boolean hasLockdownAdminConfiguredNetworks(@NonNull android.content.ComponentName);
     method public boolean installCaCert(@Nullable android.content.ComponentName, byte[]);
     method public boolean installExistingPackage(@NonNull android.content.ComponentName, String);
@@ -7343,6 +7344,7 @@
     field public static final int TAG_MEDIA_UNMOUNT = 210014; // 0x3345e
     field public static final int TAG_OS_SHUTDOWN = 210010; // 0x3345a
     field public static final int TAG_OS_STARTUP = 210009; // 0x33459
+    field public static final int TAG_PASSWORD_COMPLEXITY_REQUIRED = 210035; // 0x33473
     field public static final int TAG_PASSWORD_COMPLEXITY_SET = 210017; // 0x33461
     field public static final int TAG_PASSWORD_EXPIRATION_SET = 210016; // 0x33460
     field public static final int TAG_PASSWORD_HISTORY_LENGTH_SET = 210018; // 0x33462
@@ -45126,6 +45128,7 @@
     field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
     field public static final int DATA_CYCLE_USE_PLATFORM_DEFAULT = -1; // 0xffffffff
     field public static final String ENABLE_EAP_METHOD_PREFIX_BOOL = "enable_eap_method_prefix_bool";
+    field public static final String EXTRA_REBROADCAST_ON_UNLOCK = "android.telephony.extra.REBROADCAST_ON_UNLOCK";
     field public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
     field public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
     field public static final String IMSI_KEY_AVAILABILITY_INT = "imsi_key_availability_int";
@@ -45400,6 +45403,7 @@
   }
 
   public static final class CarrierConfigManager.Ims {
+    field public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = "ims.enable_presence_publish_bool";
     field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool";
     field public static final String KEY_PREFIX = "ims.";
     field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 99179ec..d9a9c26 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -248,7 +248,9 @@
     field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
     field public static final String WHITELIST_AUTO_REVOKE_PERMISSIONS = "android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS";
     field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS";
+    field public static final String WIFI_ACCESS_COEX_UNSAFE_CHANNELS = "android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS";
     field public static final String WIFI_SET_DEVICE_MOBILITY_STATE = "android.permission.WIFI_SET_DEVICE_MOBILITY_STATE";
+    field public static final String WIFI_UPDATE_COEX_UNSAFE_CHANNELS = "android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS";
     field public static final String WIFI_UPDATE_USABILITY_STATS_SCORE = "android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE";
     field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
     field public static final String WRITE_DREAM_STATE = "android.permission.WRITE_DREAM_STATE";
@@ -8420,6 +8422,8 @@
     field public static final String NAMESPACE_RUNTIME_NATIVE = "runtime_native";
     field public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot";
     field public static final String NAMESPACE_SCHEDULER = "scheduler";
+    field public static final String NAMESPACE_STATSD_NATIVE = "statsd_native";
+    field public static final String NAMESPACE_STATSD_NATIVE_BOOT = "statsd_native_boot";
     field @Deprecated public static final String NAMESPACE_STORAGE = "storage";
     field public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
     field public static final String NAMESPACE_SYSTEMUI = "systemui";
@@ -10827,6 +10831,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
     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 isLteCdmaEvdoGsmWcdmaEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
     method public boolean isNrDualConnectivityEnabled();
@@ -11432,6 +11437,61 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.AudioCodecAttributes> CREATOR;
   }
 
+  public interface DelegateMessageCallback {
+    method public void onMessageReceived(@NonNull android.telephony.ims.SipMessage);
+    method public void onMessageSendFailure(@NonNull String, int);
+    method public void onMessageSent(@NonNull String);
+  }
+
+  public final class DelegateRegistrationState implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public java.util.Set<android.telephony.ims.FeatureTagState> getDeregisteredFeatureTags();
+    method @NonNull public java.util.Set<android.telephony.ims.FeatureTagState> getDeregisteringFeatureTags();
+    method @NonNull public java.util.Set<java.lang.String> getRegisteredFeatureTags();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.DelegateRegistrationState> CREATOR;
+    field public static final int DEREGISTERED_REASON_NOT_PROVISIONED = 1; // 0x1
+    field public static final int DEREGISTERED_REASON_NOT_REGISTERED = 2; // 0x2
+    field public static final int DEREGISTERED_REASON_UNKNOWN = 0; // 0x0
+    field public static final int DEREGISTERING_REASON_DESTROY_PENDING = 6; // 0x6
+    field public static final int DEREGISTERING_REASON_FEATURE_TAGS_CHANGING = 5; // 0x5
+    field public static final int DEREGISTERING_REASON_PDN_CHANGE = 3; // 0x3
+    field public static final int DEREGISTERING_REASON_PROVISIONING_CHANGE = 4; // 0x4
+  }
+
+  public static final class DelegateRegistrationState.Builder {
+    ctor public DelegateRegistrationState.Builder();
+    method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addDeregisteredFeatureTag(@NonNull String, int);
+    method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addDeregisteringFeatureTag(@NonNull String, int);
+    method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addRegisteredFeatureTag(@NonNull String);
+    method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addRegisteredFeatureTags(@NonNull java.util.Set<java.lang.String>);
+    method @NonNull public android.telephony.ims.DelegateRegistrationState build();
+  }
+
+  public final class DelegateRequest implements android.os.Parcelable {
+    ctor public DelegateRequest(@NonNull java.util.Set<java.lang.String>);
+    method public int describeContents();
+    method @NonNull public java.util.Set<java.lang.String> getFeatureTags();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.DelegateRequest> CREATOR;
+  }
+
+  public interface DelegateStateCallback {
+    method public void onCreated(@NonNull android.telephony.ims.stub.SipDelegate, @Nullable java.util.Set<android.telephony.ims.FeatureTagState>);
+    method public void onDestroyed(int);
+    method public void onFeatureTagRegistrationChanged(@NonNull android.telephony.ims.DelegateRegistrationState);
+    method public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
+  }
+
+  public final class FeatureTagState implements android.os.Parcelable {
+    ctor public FeatureTagState(@NonNull String, int);
+    method public int describeContents();
+    method @NonNull public String getFeatureTag();
+    method public int getState();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.FeatureTagState> CREATOR;
+  }
+
   public final class ImsCallForwardInfo implements android.os.Parcelable {
     ctor public ImsCallForwardInfo(int, int, int, int, @NonNull String, int);
     method public int describeContents();
@@ -11468,6 +11528,7 @@
     method public boolean getCallExtraBoolean(String, boolean);
     method public int getCallExtraInt(String);
     method public int getCallExtraInt(String, int);
+    method @Nullable public <T extends android.os.Parcelable> T getCallExtraParcelable(@Nullable String);
     method public android.os.Bundle getCallExtras();
     method public int getCallType();
     method public static int getCallTypeFromVideoState(int);
@@ -11490,6 +11551,7 @@
     method public void setCallExtra(String, String);
     method public void setCallExtraBoolean(String, boolean);
     method public void setCallExtraInt(String, int);
+    method public void setCallExtraParcelable(@NonNull String, @NonNull android.os.Parcelable);
     method public void setCallRestrictCause(int);
     method public void setCallerNumberVerificationStatus(int);
     method public void setEmergencyCallRouting(int);
@@ -11524,6 +11586,7 @@
     field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telephony.ims.extra.CALL_DISCONNECT_CAUSE";
     field public static final String EXTRA_CALL_NETWORK_TYPE = "android.telephony.ims.extra.CALL_NETWORK_TYPE";
     field @Deprecated public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
+    field public static final String EXTRA_CALL_SUBJECT = "android.telephony.ims.extra.CALL_SUBJECT";
     field public static final String EXTRA_CHILD_NUMBER = "ChildNum";
     field public static final String EXTRA_CNA = "cna";
     field public static final String EXTRA_CNAP = "cnap";
@@ -11533,8 +11596,11 @@
     field public static final String EXTRA_EMERGENCY_CALL = "e_call";
     field public static final String EXTRA_FORWARDED_NUMBER = "android.telephony.ims.extra.FORWARDED_NUMBER";
     field public static final String EXTRA_IS_CALL_PULL = "CallPull";
+    field public static final String EXTRA_LOCATION = "android.telephony.ims.extra.LOCATION";
     field public static final String EXTRA_OI = "oi";
     field public static final String EXTRA_OIR = "oir";
+    field public static final String EXTRA_PICTURE_URL = "android.telephony.ims.extra.PICTURE_URL";
+    field public static final String EXTRA_PRIORITY = "android.telephony.ims.extra.PRIORITY";
     field public static final String EXTRA_REMOTE_URI = "remote_uri";
     field public static final String EXTRA_USSD = "ussd";
     field public static final int OIR_DEFAULT = 0; // 0x0
@@ -11542,6 +11608,8 @@
     field public static final int OIR_PRESENTATION_PAYPHONE = 4; // 0x4
     field public static final int OIR_PRESENTATION_RESTRICTED = 1; // 0x1
     field public static final int OIR_PRESENTATION_UNKNOWN = 3; // 0x3
+    field public static final int PRIORITY_NORMAL = 0; // 0x0
+    field public static final int PRIORITY_URGENT = 1; // 0x1
     field public static final int SERVICE_TYPE_EMERGENCY = 2; // 0x2
     field public static final int SERVICE_TYPE_NONE = 0; // 0x0
     field public static final int SERVICE_TYPE_NORMAL = 1; // 0x1
@@ -11952,8 +12020,107 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RtpHeaderExtensionType> CREATOR;
   }
 
+  public interface SipDelegateConnection {
+    method public void notifyMessageReceiveError(@NonNull String, int);
+    method public void notifyMessageReceived(@NonNull String);
+    method public void sendMessage(@NonNull android.telephony.ims.SipMessage, long);
+  }
+
+  public final class SipDelegateImsConfiguration implements android.os.Parcelable {
+    method public boolean containsKey(@NonNull String);
+    method @NonNull public android.os.PersistableBundle copyBundle();
+    method public int describeContents();
+    method public boolean getBoolean(@NonNull String, boolean);
+    method public int getInt(@NonNull String, int);
+    method @Nullable public String getString(@NonNull String);
+    method public long getVersion();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR;
+    field public static final String IPTYPE_IPV4 = "IPV4";
+    field public static final String IPTYPE_IPV6 = "IPV6";
+    field public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string";
+    field public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string";
+    field public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
+    field public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string";
+    field public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string";
+    field public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool";
+    field public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool";
+    field public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool";
+    field public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool";
+    field public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool";
+    field public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int";
+    field public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string";
+    field public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string";
+    field public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string";
+    field public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string";
+    field public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string";
+    field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string";
+    field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int";
+    field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int";
+    field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int";
+    field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int";
+    field public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string";
+    field public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string";
+    field public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string";
+    field public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int";
+    field public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int";
+    field public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int";
+    field public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int";
+    field public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string";
+    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string";
+    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string";
+    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int";
+    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string";
+    field public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string";
+    field public static final String SIP_TRANSPORT_TCP = "TCP";
+    field public static final String SIP_TRANSPORT_UDP = "UDP";
+  }
+
+  public static final class SipDelegateImsConfiguration.Builder {
+    ctor public SipDelegateImsConfiguration.Builder(int);
+    ctor public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
+    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean);
+    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int);
+    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String);
+    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration build();
+  }
+
   public class SipDelegateManager {
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void createSipDelegate(@NonNull android.telephony.ims.DelegateRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.DelegateConnectionStateCallback, @NonNull android.telephony.ims.stub.DelegateConnectionMessageCallback) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void destroySipDelegate(@NonNull android.telephony.ims.SipDelegateConnection, int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws android.telephony.ims.ImsException;
+    field public static final int DENIED_REASON_INVALID = 4; // 0x4
+    field public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1; // 0x1
+    field public static final int DENIED_REASON_NOT_ALLOWED = 2; // 0x2
+    field public static final int DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED = 3; // 0x3
+    field public static final int DENIED_REASON_UNKNOWN = 0; // 0x0
+    field public static final int MESSAGE_FAILURE_REASON_DELEGATE_CLOSED = 2; // 0x2
+    field public static final int MESSAGE_FAILURE_REASON_DELEGATE_DEAD = 1; // 0x1
+    field public static final int MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION = 11; // 0xb
+    field public static final int MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT = 5; // 0x5
+    field public static final int MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG = 6; // 0x6
+    field public static final int MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS = 4; // 0x4
+    field public static final int MESSAGE_FAILURE_REASON_INVALID_START_LINE = 3; // 0x3
+    field public static final int MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE = 8; // 0x8
+    field public static final int MESSAGE_FAILURE_REASON_NOT_REGISTERED = 9; // 0x9
+    field public static final int MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION = 10; // 0xa
+    field public static final int MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE = 7; // 0x7
+    field public static final int MESSAGE_FAILURE_REASON_UNKNOWN = 0; // 0x0
+    field public static final int SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP = 2; // 0x2
+    field public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD = 1; // 0x1
+    field public static final int SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN = 4; // 0x4
+    field public static final int SIP_DELEGATE_DESTROY_REASON_UNKNOWN = 0; // 0x0
+    field public static final int SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS = 3; // 0x3
+  }
+
+  public final class SipMessage implements android.os.Parcelable {
+    ctor public SipMessage(@NonNull String, @NonNull String, @NonNull byte[]);
+    method public int describeContents();
+    method @NonNull public byte[] getContent();
+    method @NonNull public String getHeaderSection();
+    method @NonNull public String getStartLine();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipMessage> CREATOR;
   }
 
 }
@@ -12047,6 +12214,19 @@
 
 package android.telephony.ims.stub {
 
+  public interface DelegateConnectionMessageCallback {
+    method public void onMessageReceived(@NonNull android.telephony.ims.SipMessage);
+    method public void onMessageSendFailure(@NonNull String, int);
+    method public void onMessageSent(@NonNull String);
+  }
+
+  public interface DelegateConnectionStateCallback {
+    method public void onCreated(@NonNull android.telephony.ims.SipDelegateConnection);
+    method public void onDestroyed(int);
+    method public void onFeatureTagStatusChanged(@NonNull android.telephony.ims.DelegateRegistrationState, @NonNull java.util.Set<android.telephony.ims.FeatureTagState>);
+    method public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
+  }
+
   public class ImsCallSessionImplBase implements java.lang.AutoCloseable {
     ctor public ImsCallSessionImplBase();
     method public void accept(int, android.telephony.ims.ImsStreamMediaProfile);
@@ -12207,8 +12387,17 @@
     method public int updateColr(int);
   }
 
+  public interface SipDelegate {
+    method public void closeDialog(@NonNull String);
+    method public void notifyMessageReceiveError(@NonNull String, int);
+    method public void notifyMessageReceived(@NonNull String);
+    method public void sendMessage(@NonNull android.telephony.ims.SipMessage, long);
+  }
+
   public class SipTransportImplBase {
     ctor public SipTransportImplBase(@NonNull java.util.concurrent.Executor);
+    method public void createSipDelegate(int, @NonNull android.telephony.ims.DelegateRequest, @NonNull android.telephony.ims.DelegateStateCallback, @NonNull android.telephony.ims.DelegateMessageCallback);
+    method public void destroySipDelegate(@NonNull android.telephony.ims.stub.SipDelegate, int);
   }
 
 }
diff --git a/core/java/android/annotation/RequiresFeature.java b/core/java/android/annotation/RequiresFeature.java
index fc93f03..08861d4 100644
--- a/core/java/android/annotation/RequiresFeature.java
+++ b/core/java/android/annotation/RequiresFeature.java
@@ -30,7 +30,6 @@
  * Denotes that the annotated element requires one or more device features. This
  * is used to auto-generate documentation.
  *
- * @see PackageManager#hasSystemFeature(String)
  * @hide
  */
 @Retention(SOURCE)
@@ -38,8 +37,16 @@
 public @interface RequiresFeature {
     /**
      * The name of the device feature that is required.
-     *
-     * @see PackageManager#hasSystemFeature(String)
      */
     String value();
+
+    /**
+     * Defines the name of the method that should be called to check whether the feature is
+     * available, using the same signature format as javadoc. The feature checking method can have
+     * multiple parameters, but the feature name parameter must be of type String and must also be
+     * the first String-type parameter.
+     * <p>
+     * By default, the enforcement is {@link PackageManager#hasSystemFeature(String)}.
+     */
+    String enforcement() default("android.content.pm.PackageManager#hasSystemFeature");
 }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 1c2f682..5ee5597 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2590,7 +2590,7 @@
     protected void onStop() {
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this);
         if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
-        mActivityTransitionState.onStop();
+        mActivityTransitionState.onStop(this);
         dispatchActivityStopped();
         mTranslucentCallback = null;
         mCalled = true;
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index ef146b1..0b0781e 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -27,6 +27,8 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.TestApi;
+import android.app.ExitTransitionCoordinator.ActivityExitTransitionCallbacks;
+import android.app.ExitTransitionCoordinator.ExitTransitionCallbacks;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -44,8 +46,6 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
-import android.transition.Transition;
-import android.transition.TransitionListenerAdapter;
 import android.transition.TransitionManager;
 import android.util.Pair;
 import android.util.Slog;
@@ -806,8 +806,11 @@
     public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
             Pair<View, String>... sharedElements) {
         ActivityOptions opts = new ActivityOptions();
-        makeSceneTransitionAnimation(activity, activity.getWindow(), opts,
-                activity.mExitTransitionListener, sharedElements);
+        ExitTransitionCoordinator exit = makeSceneTransitionAnimation(
+                new ActivityExitTransitionCallbacks(activity), activity.mExitTransitionListener,
+                activity.getWindow(), opts, sharedElements);
+        opts.mExitCoordinatorIndex =
+                activity.mActivityTransitionState.addExitTransitionCoordinator(exit);
         return opts;
     }
 
@@ -823,25 +826,19 @@
      * @hide
      */
     @SafeVarargs
-    public static ActivityOptions startSharedElementAnimation(Window window,
+    public static Pair<ActivityOptions, ExitTransitionCoordinator> startSharedElementAnimation(
+            Window window, ExitTransitionCallbacks exitCallbacks, SharedElementCallback callback,
             Pair<View, String>... sharedElements) {
         ActivityOptions opts = new ActivityOptions();
-        final View decorView = window.getDecorView();
-        if (decorView == null) {
-            return opts;
-        }
-        final ExitTransitionCoordinator exit =
-                makeSceneTransitionAnimation(null, window, opts, null, sharedElements);
-        if (exit != null) {
-            HideWindowListener listener = new HideWindowListener(window, exit);
-            exit.setHideSharedElementsCallback(listener);
-            exit.startExit();
-        }
-        return opts;
+        ExitTransitionCoordinator exit = makeSceneTransitionAnimation(
+                exitCallbacks, callback, window, opts, sharedElements);
+        opts.mExitCoordinatorIndex = -1;
+        return Pair.create(opts, exit);
     }
 
     /**
-     * This method should be called when the {@link #startSharedElementAnimation(Window, Pair[])}
+     * This method should be called when the
+     * {@link #startSharedElementAnimation(Window, ExitTransitionCallbacks, Pair[])}
      * animation must be stopped and the Views reset. This can happen if there was an error
      * from startActivity or a springboard activity and the animation should stop and reset.
      *
@@ -864,9 +861,9 @@
         }
     }
 
-    static ExitTransitionCoordinator makeSceneTransitionAnimation(Activity activity, Window window,
-            ActivityOptions opts, SharedElementCallback callback,
-            Pair<View, String>[] sharedElements) {
+    static ExitTransitionCoordinator makeSceneTransitionAnimation(
+            ExitTransitionCallbacks exitCallbacks, SharedElementCallback callback, Window window,
+            ActivityOptions opts, Pair<View, String>[] sharedElements) {
         if (!window.hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
             opts.mAnimationType = ANIM_DEFAULT;
             return null;
@@ -892,17 +889,11 @@
             }
         }
 
-        ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, window,
+        ExitTransitionCoordinator exit = new ExitTransitionCoordinator(exitCallbacks, window,
                 callback, names, names, views, false);
         opts.mTransitionReceiver = exit;
         opts.mSharedElementNames = names;
-        opts.mIsReturning = (activity == null);
-        if (activity == null) {
-            opts.mExitCoordinatorIndex = -1;
-        } else {
-            opts.mExitCoordinatorIndex =
-                    activity.mActivityTransitionState.addExitTransitionCoordinator(exit);
-        }
+        opts.mIsReturning = false;
         return exit;
     }
 
@@ -928,8 +919,12 @@
         opts.mIsReturning = true;
         opts.mResultCode = resultCode;
         opts.mResultData = resultData;
-        opts.mExitCoordinatorIndex =
-                activity.mActivityTransitionState.addExitTransitionCoordinator(exitCoordinator);
+        if (activity == null) {
+            opts.mExitCoordinatorIndex = -1;
+        } else {
+            opts.mExitCoordinatorIndex =
+                    activity.mActivityTransitionState.addExitTransitionCoordinator(exitCoordinator);
+        }
         return opts;
     }
 
@@ -1868,67 +1863,6 @@
                 + mStartY + ", mWidth=" + mWidth + ", mHeight=" + mHeight;
     }
 
-    private static class HideWindowListener extends TransitionListenerAdapter
-        implements ExitTransitionCoordinator.HideSharedElementsCallback {
-        private final Window mWindow;
-        private final ExitTransitionCoordinator mExit;
-        private final boolean mWaitingForTransition;
-        private boolean mTransitionEnded;
-        private boolean mSharedElementHidden;
-        private ArrayList<View> mSharedElements;
-
-        public HideWindowListener(Window window, ExitTransitionCoordinator exit) {
-            mWindow = window;
-            mExit = exit;
-            mSharedElements = new ArrayList<>(exit.mSharedElements);
-            Transition transition = mWindow.getExitTransition();
-            if (transition != null) {
-                transition.addListener(this);
-                mWaitingForTransition = true;
-            } else {
-                mWaitingForTransition = false;
-            }
-            View decorView = mWindow.getDecorView();
-            if (decorView != null) {
-                if (decorView.getTag(com.android.internal.R.id.cross_task_transition) != null) {
-                    throw new IllegalStateException(
-                            "Cannot start a transition while one is running");
-                }
-                decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, exit);
-            }
-        }
-
-        @Override
-        public void onTransitionEnd(Transition transition) {
-            mTransitionEnded = true;
-            hideWhenDone();
-            transition.removeListener(this);
-        }
-
-        @Override
-        public void hideSharedElements() {
-            mSharedElementHidden = true;
-            hideWhenDone();
-        }
-
-        private void hideWhenDone() {
-            if (mSharedElementHidden && (!mWaitingForTransition || mTransitionEnded)) {
-                mExit.resetViews();
-                int numSharedElements = mSharedElements.size();
-                for (int i = 0; i < numSharedElements; i++) {
-                    View view = mSharedElements.get(i);
-                    view.requestLayout();
-                }
-                View decorView = mWindow.getDecorView();
-                if (decorView != null) {
-                    decorView.setTagInternal(
-                            com.android.internal.R.id.cross_task_transition, null);
-                    decorView.setVisibility(View.GONE);
-                }
-            }
-        }
-    }
-
     /**
      * The information about the source of activity launch. E.g. describe an activity is launched
      * from launcher by receiving a motion event with a timestamp.
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 5c4125e..6261950 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -247,14 +247,14 @@
         mEnterActivityOptions = null;
     }
 
-    public void onStop() {
+    public void onStop(Activity activity) {
         restoreExitedViews();
         if (mEnterTransitionCoordinator != null) {
             mEnterTransitionCoordinator.stop();
             mEnterTransitionCoordinator = null;
         }
         if (mReturnExitCoordinator != null) {
-            mReturnExitCoordinator.stop();
+            mReturnExitCoordinator.stop(activity);
             mReturnExitCoordinator = null;
         }
     }
@@ -331,7 +331,8 @@
                     }
                 }
 
-                mReturnExitCoordinator = new ExitTransitionCoordinator(activity,
+                mReturnExitCoordinator = new ExitTransitionCoordinator(
+                        new ExitTransitionCoordinator.ActivityExitTransitionCallbacks(activity),
                         activity.getWindow(), activity.mEnterTransitionListener, pendingExitNames,
                         null, null, true);
                 if (enterViewsTransition != null && decor != null) {
@@ -341,12 +342,11 @@
                     final ViewGroup finalDecor = decor;
                     OneShotPreDrawListener.add(decor, () -> {
                         if (mReturnExitCoordinator != null) {
-                            mReturnExitCoordinator.startExit(activity.mResultCode,
-                                    activity.mResultData);
+                            mReturnExitCoordinator.startExit(activity);
                         }
                     });
                 } else {
-                    mReturnExitCoordinator.startExit(activity.mResultCode, activity.mResultData);
+                    mReturnExitCoordinator.startExit(activity);
                 }
             }
             return true;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index cdfe41e..4dd6a7e 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -358,7 +358,7 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true, prefix = { "MODE_" }, value = {
+    @IntDef(prefix = { "MODE_" }, value = {
             MODE_ALLOWED,
             MODE_IGNORED,
             MODE_ERRORED,
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 68824cd..9fdff59 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -18,6 +18,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
+import android.annotation.NonNull;
 import android.app.SharedElementCallback.OnSharedElementsReadyListener;
 import android.content.Intent;
 import android.graphics.Color;
@@ -45,15 +46,17 @@
  * This ActivityTransitionCoordinator is created in ActivityOptions#makeSceneTransitionAnimation
  * to govern the exit of the Scene and the shared elements when calling an Activity as well as
  * the reentry of the Scene when coming back from the called Activity.
+ *
+ * @hide
  */
-class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
+public class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
     private static final String TAG = "ExitTransitionCoordinator";
     static long sMaxWaitMillis = 1000;
 
     private Bundle mSharedElementBundle;
     private boolean mExitNotified;
     private boolean mSharedElementNotified;
-    private Activity mActivity;
+    private ExitTransitionCallbacks mExitCallbacks;
     private boolean mIsBackgroundReady;
     private boolean mIsCanceled;
     private Handler mHandler;
@@ -62,20 +65,15 @@
     private Bundle mExitSharedElementBundle;
     private boolean mIsExitStarted;
     private boolean mSharedElementsHidden;
-    private HideSharedElementsCallback mHideSharedElementsCallback;
 
-    public ExitTransitionCoordinator(Activity activity, Window window,
-            SharedElementCallback listener, ArrayList<String> names,
+    public ExitTransitionCoordinator(ExitTransitionCallbacks exitCallbacks,
+            Window window, SharedElementCallback listener, ArrayList<String> names,
             ArrayList<String> accepted, ArrayList<View> mapped, boolean isReturning) {
         super(window, names, listener, isReturning);
         viewsReady(mapSharedElements(accepted, mapped));
         stripOffscreenViews();
         mIsBackgroundReady = !isReturning;
-        mActivity = activity;
-    }
-
-    void setHideSharedElementsCallback(HideSharedElementsCallback callback) {
-        mHideSharedElementsCallback = callback;
+        mExitCallbacks = exitCallbacks;
     }
 
     @Override
@@ -190,8 +188,8 @@
 
     private void hideSharedElements() {
         moveSharedElementsFromOverlay();
-        if (mHideSharedElementsCallback != null) {
-            mHideSharedElementsCallback.hideSharedElements();
+        if (mExitCallbacks != null) {
+            mExitCallbacks.hideSharedElements();
         }
         if (!mIsHidden) {
             hideViews(mSharedElements);
@@ -210,20 +208,16 @@
                 decorView.suppressLayout(true);
             }
             moveSharedElementsToOverlay();
-            startTransition(new Runnable() {
-                @Override
-                public void run() {
-                    if (mActivity != null) {
-                        beginTransitions();
-                    } else {
-                        startExitTransition();
-                    }
-                }
-            });
+            startTransition(this::beginTransitions);
         }
     }
 
-    public void startExit(int resultCode, Intent data) {
+    /**
+     * Starts the exit animation and sends back the activity result
+     */
+    public void startExit(Activity activity) {
+        int resultCode = activity.mResultCode;
+        Intent data = activity.mResultData;
         if (!mIsExitStarted) {
             mIsExitStarted = true;
             pauseInput();
@@ -247,9 +241,9 @@
                     .getApplicationInfo().targetSdkVersion >= VERSION_CODES.M;
             ArrayList<String> sharedElementNames = targetsM ? mSharedElementNames :
                     mAllSharedElementNames;
-            ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(mActivity, this,
+            ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(activity, this,
                     sharedElementNames, resultCode, data);
-            mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() {
+            activity.convertToTranslucent(new Activity.TranslucentConversionListener() {
                 @Override
                 public void onTranslucentConversionComplete(boolean drawComplete) {
                     if (!mIsCanceled) {
@@ -257,21 +251,19 @@
                     }
                 }
             }, options);
-            startTransition(new Runnable() {
-                @Override
-                public void run() {
-                    startExitTransition();
-                }
-            });
+            startTransition(this::startExitTransition);
         }
     }
 
-    public void stop() {
-        if (mIsReturning && mActivity != null) {
+    /**
+     * Called from {@link Activity#onStop()}
+     */
+    public void stop(Activity activity) {
+        if (mIsReturning && mExitCallbacks != null) {
             // Override the previous ActivityOptions. We don't want the
             // activity to have options since we're essentially canceling the
             // transition and finishing right now.
-            mActivity.convertToTranslucent(null, null);
+            activity.convertToTranslucent(null, null);
             finish();
         }
     }
@@ -434,7 +426,7 @@
                 mSharedElementNotified = true;
                 delayCancel();
 
-                if (!mActivity.isTopOfTask()) {
+                if (mExitCallbacks.isReturnTransitionAllowed()) {
                     mResultReceiver.send(MSG_ALLOW_RETURN_TRANSITION, null);
                 }
 
@@ -474,22 +466,20 @@
     }
 
     private void finishIfNecessary() {
-        if (mIsReturning && mExitNotified && mActivity != null && (mSharedElements.isEmpty() ||
-                mSharedElementsHidden)) {
+        if (mIsReturning && mExitNotified && mExitCallbacks != null && (mSharedElements.isEmpty()
+                || mSharedElementsHidden)) {
             finish();
         }
         if (!mIsReturning && mExitNotified) {
-            mActivity = null; // don't need it anymore
+            mExitCallbacks = null; // don't need it anymore
         }
     }
 
     private void finish() {
         stopCancel();
-        if (mActivity != null) {
-            mActivity.mActivityTransitionState.clear();
-            mActivity.finish();
-            mActivity.overridePendingTransition(0, 0);
-            mActivity = null;
+        if (mExitCallbacks != null) {
+            mExitCallbacks.onFinish();
+            mExitCallbacks = null;
         }
         // Clear the state so that we can't hold any references accidentally and leak memory.
         clearState();
@@ -529,7 +519,49 @@
         }
     }
 
-    interface HideSharedElementsCallback {
-        void hideSharedElements();
+    /**
+     * @hide
+     */
+    public interface ExitTransitionCallbacks {
+
+        /**
+         * Returns true if reverse exit animation is supported
+         */
+        boolean isReturnTransitionAllowed();
+
+        /**
+         * Called then the transition finishes
+         */
+        void onFinish();
+
+        /**
+         * Optional callback when the transition is hiding elements in the source surface
+         */
+        default void hideSharedElements() { };
+    }
+
+    /**
+     * @hide
+     */
+    public static class ActivityExitTransitionCallbacks implements ExitTransitionCallbacks {
+
+        @NonNull
+        final Activity mActivity;
+
+        ActivityExitTransitionCallbacks(@NonNull Activity activity) {
+            mActivity = activity;
+        }
+
+        @Override
+        public boolean isReturnTransitionAllowed() {
+            return !mActivity.isTopOfTask();
+        }
+
+        @Override
+        public void onFinish() {
+            mActivity.mActivityTransitionState.clear();
+            mActivity.finish();
+            mActivity.overridePendingTransition(0, 0);
+        }
     }
 }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d5572bf..d5977e7 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4874,6 +4874,7 @@
             // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
             // re-using the drawable when the notification is updated.
             contentView.setBoolean(R.id.expand_button, "setExpanded", false);
+            contentView.setViewVisibility(R.id.app_name_text, View.GONE);
             contentView.setTextViewText(R.id.app_name_text, null);
             contentView.setViewVisibility(R.id.chronometer, View.GONE);
             contentView.setViewVisibility(R.id.header_text, View.GONE);
@@ -5150,9 +5151,14 @@
 
         private void bindNotificationHeader(RemoteViews contentView, StandardTemplateParams p) {
             bindSmallIcon(contentView, p);
-            boolean hasTextToLeft = bindHeaderAppName(contentView, p);
+            // Populate text left-to-right so that separators are only shown between strings
+            boolean hasTextToLeft = bindHeaderAppName(contentView, p, false /* force */);
             hasTextToLeft |= bindHeaderTextSecondary(contentView, p, hasTextToLeft);
             hasTextToLeft |= bindHeaderText(contentView, p, hasTextToLeft);
+            if (!hasTextToLeft) {
+                // If there's still no text, force add the app name so there is some text.
+                hasTextToLeft |= bindHeaderAppName(contentView, p, true /* force */);
+            }
             bindHeaderChronometerAndTime(contentView, p, hasTextToLeft);
             bindProfileBadge(contentView, p);
             bindAlertedIcon(contentView, p);
@@ -5216,7 +5222,7 @@
                     && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
                 summaryText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
             }
-            if (summaryText != null) {
+            if (!TextUtils.isEmpty(summaryText)) {
                 // TODO: Remove the span entirely to only have the string with propper formating.
                 contentView.setTextViewText(R.id.header_text, processTextSpans(
                         processLegacyText(summaryText)));
@@ -5288,13 +5294,13 @@
         /**
          * @return true if the app name will be visible
          */
-        private boolean bindHeaderAppName(RemoteViews contentView, StandardTemplateParams p) {
-            if (p.mViewType == StandardTemplateParams.VIEW_TYPE_MINIMIZED) {
-                contentView.setViewVisibility(R.id.app_name_text, View.GONE);
+        private boolean bindHeaderAppName(RemoteViews contentView, StandardTemplateParams p,
+                boolean force) {
+            if (p.mViewType == StandardTemplateParams.VIEW_TYPE_MINIMIZED && !force) {
+                // unless the force flag is set, don't show the app name in the minimized state.
                 return false;
             }
             if (p.mHeaderless && p.hasTitle()) {
-                contentView.setViewVisibility(R.id.app_name_text, View.GONE);
                 // the headerless template will have the TITLE in this position; return true to
                 // keep the divider visible between that title and the next text element.
                 return true;
@@ -11104,7 +11110,9 @@
         }
 
         final boolean hasTitle() {
-            return title != null && title.length() != 0 && !mHasCustomContent;
+            // We hide the title when the notification is a decorated custom view so that decorated
+            // custom views always have to include their own title.
+            return !TextUtils.isEmpty(title) && !mHasCustomContent;
         }
 
         final StandardTemplateParams viewType(int viewType) {
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index d2be8a4..36241a8 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -92,6 +92,54 @@
                 }
             ],
             "file_patterns": ["(/|^)Activity.java"]
+        },
+        {
+            "name": "CtsContentTestCases",
+            "options": [
+                {
+                    "exclude-annotation": "androidx.test.filters.FlakyTest"
+                },
+                {
+                    "exclude-annotation": "org.junit.Ignore"
+                },
+                {
+                    "include-filter": "android.content.wm.cts"
+                }
+            ],
+            "file_patterns": ["(/|^)ContextImpl.java"]
+        },
+        {
+            "name": "CtsOsTestCases",
+            "options": [
+                {
+                    "include-annotation": "android.platform.test.annotations.Presubmit"
+                },
+                {
+                    "exclude-annotation": "androidx.test.filters.FlakyTest"
+                },
+                {
+                    "exclude-annotation": "org.junit.Ignore"
+                },
+                {
+                    "include-filter": "android.os.cts.StrictModeTest"
+                }
+            ],
+            "file_patterns": ["(/|^)ContextImpl.java"]
+        },
+        {
+            "name": "FrameworksCoreTests",
+            "options": [
+                {
+                    "exclude-annotation": "androidx.test.filters.FlakyTest"
+                },
+                {
+                    "exclude-annotation": "org.junit.Ignore"
+                },
+                {
+                    "include-filter": "android.content.ContextTest"
+                }
+            ],
+            "file_patterns": ["(/|^)ContextImpl.java"]
         }
     ],
     "postsubmit": [
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 42427fa..5eb1922 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -87,6 +87,7 @@
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
 import android.util.ArraySet;
+import android.util.DebugUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -2453,19 +2454,45 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface PersonalAppsSuspensionReason {}
 
+    // TODO(b/172376923) - make all (or none) @TestApi
+
     /** @hide */
     @TestApi
     public static final int OPERATION_LOCK_NOW = 1;
 
+    /** @hide */
+    public static final int OPERATION_SWITCH_USER = 2;
+    /** @hide */
+    public static final int OPERATION_START_USER_IN_BACKGROUND = 3;
+    /** @hide */
+    public static final int OPERATION_STOP_USER = 4;
+    /** @hide */
+    public static final int OPERATION_CREATE_AND_MANAGE_USER = 5;
+    /** @hide */
+    public static final int OPERATION_REMOVE_USER = 6;
+
+    private static final String PREFIX_OPERATION = "OPERATION_";
+
+
     // TODO(b/172376923) - add all operations
     /** @hide */
-    @IntDef(prefix = "OPERATION_", value = {
+    @IntDef(prefix = PREFIX_OPERATION, value = {
             OPERATION_LOCK_NOW,
+            OPERATION_SWITCH_USER,
+            OPERATION_START_USER_IN_BACKGROUND,
+            OPERATION_STOP_USER,
+            OPERATION_CREATE_AND_MANAGE_USER,
+            OPERATION_REMOVE_USER
     })
     @Retention(RetentionPolicy.SOURCE)
     public static @interface DevicePolicyOperation {
     }
 
+    /** @hide */
+    public static String operationToString(@DevicePolicyOperation int operation) {
+        return DebugUtils.constantToString(DevicePolicyManager.class, PREFIX_OPERATION, operation);
+    }
+
     /**
      * Return true if the given administrator component is currently active (enabled) in the system.
      *
@@ -3721,6 +3748,27 @@
     }
 
     /**
+     * Returns the password complexity that applies to this user, aggregated from other users if
+     * necessary (for example, if the DPC has set password complexity requirements on the parent
+     * profile DPM instance of a managed profile user, they would apply to the primary user on the
+     * device).
+     * @hide
+     */
+    @PasswordComplexity
+    public int getAggregatedPasswordComplexityForUser(int userId) {
+        if (mService == null) {
+            return PASSWORD_COMPLEXITY_NONE;
+        }
+
+        try {
+            return mService.getAggregatedPasswordComplexityForUser(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /**
      * When called by a profile owner of a managed profile returns true if the profile uses unified
      * challenge with its parent user.
      *
@@ -5192,9 +5240,22 @@
     }
 
     /**
-     * Called by a device or profile owner, or delegated certificate installer, to install a
-     * certificate and corresponding private key. All apps within the profile will be able to access
-     * the certificate and use the private key, given direct user approval.
+     * This API can be called by the following to install a certificate and corresponding
+     * private key:
+     * <ul>
+     *    <li>Device owner</li>
+     *    <li>Profile owner</li>
+     *    <li>Delegated certificate installer</li>
+     *    <li>Credential management app</li>
+     * </ul>
+     * All apps within the profile will be able to access the certificate and use the private key,
+     * given direct user approval.
+     *
+     * <p>From Android {@link android.os.Build.VERSION_CODES#S}, the credential management app
+     * can call this API. However, this API sets the key pair as user selectable by default,
+     * which is not permitted when called by the credential management app. Instead,
+     * {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)} should be
+     * called with {@link #INSTALLKEY_SET_USER_SELECTABLE} not set as a flag.
      *
      * <p>Access to the installed credentials will not be granted to the caller of this API without
      * direct user approval. This is for security - should a certificate installer become
@@ -5225,10 +5286,23 @@
     }
 
     /**
-     * Called by a device or profile owner, or delegated certificate installer, to install a
-     * certificate chain and corresponding private key for the leaf certificate. All apps within the
-     * profile will be able to access the certificate chain and use the private key, given direct
-     * user approval.
+     * This API can be called by the following to install a certificate chain and corresponding
+     * private key for the leaf certificate:
+     * <ul>
+     *    <li>Device owner</li>
+     *    <li>Profile owner</li>
+     *    <li>Delegated certificate installer</li>
+     *    <li>Credential management app</li>
+     * </ul>
+     * All apps within the profile will be able to access the certificate chain and use the private
+     * key, given direct user approval.
+     *
+     * <p>From Android {@link android.os.Build.VERSION_CODES#S}, the credential management app
+     * can call this API. However, this API sets the key pair as user selectable by default,
+     * which is not permitted when called by the credential management app. Instead,
+     * {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)} should be
+     * called with {@link #INSTALLKEY_SET_USER_SELECTABLE} not set as a flag.
+     * Note, there can only be a credential management app on an unmanaged device.
      *
      * <p>The caller of this API may grant itself access to the certificate and private key
      * immediately, without user approval. It is a best practice not to request this unless strictly
@@ -5266,10 +5340,26 @@
     }
 
     /**
-     * Called by a device or profile owner, or delegated certificate installer, to install a
-     * certificate chain and corresponding private key for the leaf certificate. All apps within the
-     * profile will be able to access the certificate chain and use the private key, given direct
-     * user approval (if the user is allowed to select the private key).
+     * This API can be called by the following to install a certificate chain and corresponding
+     * private key for the leaf certificate:
+     * <ul>
+     *    <li>Device owner</li>
+     *    <li>Profile owner</li>
+     *    <li>Delegated certificate installer</li>
+     *    <li>Credential management app</li>
+     * </ul>
+     * All apps within the profile will be able to access the certificate chain and use the
+     * private key, given direct user approval (if the user is allowed to select the private key).
+     *
+     * <p>From Android {@link android.os.Build.VERSION_CODES#S}, the credential management app
+     * can call this API. If called by the credential management app:
+     * <ul>
+     *    <li>The componentName must be {@code null}r</li>
+     *    <li>The alias must exist in the credential management app's
+     *    {@link android.security.AppUriAuthenticationPolicy}</li>
+     *    <li>The key pair must not be user selectable</li>
+     * </ul>
+     * Note, there can only be a credential management app on an unmanaged device.
      *
      * <p>The caller of this API may grant itself access to the certificate and private key
      * immediately, without user approval. It is a best practice not to request this unless strictly
@@ -5295,7 +5385,8 @@
      *        {@link #INSTALLKEY_REQUEST_CREDENTIALS_ACCESS}.
      * @return {@code true} if the keys were installed, {@code false} otherwise.
      * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
-     *         owner.
+     *         owner, or {@code admin} is null but the calling application is not a delegated
+     *         certificate installer or credential management app.
      * @see android.security.KeyChain#getCertificateChain
      * @see #setDelegatedScopes
      * @see #DELEGATION_CERT_INSTALL
@@ -5328,15 +5419,26 @@
     }
 
     /**
-     * Called by a device or profile owner, or delegated certificate installer, to remove a
-     * certificate and private key pair installed under a given alias.
+     * This API can be called by the following to remove a certificate and private key pair
+     * installed under a given alias:
+     * <ul>
+     *    <li>Device owner</li>
+     *    <li>Profile owner</li>
+     *    <li>Delegated certificate installer</li>
+     *    <li>Credential management app</li>
+     * </ul>
+     *
+     * <p>From Android {@link android.os.Build.VERSION_CODES#S}, the credential management app
+     * can call this API. If called by the credential management app, the componentName must be
+     * {@code null}. Note, there can only be a credential management app on an unmanaged device.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
      *        {@code null} if calling from a delegated certificate installer.
      * @param alias The private key alias under which the certificate is installed.
      * @return {@code true} if the private key alias no longer exists, {@code false} otherwise.
      * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
-     *         owner.
+     *         owner, or {@code admin} is null but the calling application is not a delegated
+     *         certificate installer or credential management app.
      * @see #setDelegatedScopes
      * @see #DELEGATION_CERT_INSTALL
      */
@@ -5349,11 +5451,42 @@
         }
     }
 
+    // STOPSHIP(b/174298501): clarify the expected return value following generateKeyPair call.
     /**
-     * Called by a device or profile owner, or delegated certificate installer, to generate a
-     * new private/public key pair. If the device supports key generation via secure hardware,
-     * this method is useful for creating a key in KeyChain that never left the secure hardware.
-     * Access to the key is controlled the same way as in {@link #installKeyPair}.
+     * Called by a device or profile owner, or delegated certificate installer, to query whether a
+     * certificate and private key are installed under a given alias.
+     *
+     * @param alias The alias under which the key pair is installed.
+     * @return {@code true} if a key pair with this alias exists, {@code false} otherwise.
+     * @throws SecurityException if the caller is not a device or profile owner or a delegated
+     *         certificate installer.
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_CERT_INSTALL
+     */
+    public boolean hasKeyPair(@NonNull String alias) {
+        throwIfParentInstance("hasKeyPair");
+        try {
+            return mService.hasKeyPair(mContext.getPackageName(), alias);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * This API can be called by the following to generate a new private/public key pair:
+     * <ul>
+     *    <li>Device owner</li>
+     *    <li>Profile owner</li>
+     *    <li>Delegated certificate installer</li>
+     *    <li>Credential management app</li>
+     * </ul>
+     * If the device supports key generation via secure hardware, this method is useful for
+     * creating a key in KeyChain that never left the secure hardware. Access to the key is
+     * controlled the same way as in {@link #installKeyPair}.
+     *
+     * <p>From Android {@link android.os.Build.VERSION_CODES#S}, the credential management app
+     * can call this API. If called by the credential management app, the componentName must be
+     * {@code null}. Note, there can only be a credential management app on an unmanaged device.
      *
      * <p>Because this method might take several seconds to complete, it should only be called from
      * a worker thread. This method returns {@code null} when called from the main thread.
@@ -5376,9 +5509,10 @@
      * supports these features, refer to {@link #isDeviceIdAttestationSupported()} and
      * {@link #isUniqueDeviceAttestationSupported()}.
      *
-     * <p>Device owner, profile owner and their delegated certificate installer can use
-     * {@link #ID_TYPE_BASE_INFO} to request inclusion of the general device information
-     * including manufacturer, model, brand, device and product in the attestation record.
+     * <p>Device owner, profile owner, their delegated certificate installer and the credential
+     * management app can use {@link #ID_TYPE_BASE_INFO} to request inclusion of the general device
+     * information including manufacturer, model, brand, device and product in the attestation
+     * record.
      * Only device owner, profile owner on an organization-owned device and their delegated
      * certificate installers can use {@link #ID_TYPE_SERIAL}, {@link #ID_TYPE_IMEI} and
      * {@link #ID_TYPE_MEID} to request unique device identifiers to be attested (the serial number,
@@ -5413,9 +5547,11 @@
      *        {@code keySpec}.
      * @return A non-null {@code AttestedKeyPair} if the key generation succeeded, null otherwise.
      * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
-     *         owner. If Device ID attestation is requested (using {@link #ID_TYPE_SERIAL},
-     *         {@link #ID_TYPE_IMEI} or {@link #ID_TYPE_MEID}), the caller must be the Device Owner
-     *         or the Certificate Installer delegate.
+     *         owner, or {@code admin} is null but the calling application is not a delegated
+     *         certificate installer or credential management app. If Device ID attestation is
+     *         requested (using {@link #ID_TYPE_SERIAL}, {@link #ID_TYPE_IMEI} or
+     *         {@link #ID_TYPE_MEID}), the caller must be the Device Owner or the Certificate
+     *         Installer delegate.
      * @throws IllegalArgumentException in the following cases:
      *         <p>
      *         <ul>
@@ -5578,10 +5714,19 @@
     }
 
     /**
-     * Called by a device or profile owner, or delegated certificate installer, to associate
-     * certificates with a key pair that was generated using {@link #generateKeyPair}, and
-     * set whether the key is available for the user to choose in the certificate selection
-     * prompt.
+     * This API can be called by the following to associate certificates with a key pair that was
+     * generated using {@link #generateKeyPair}, and set whether the key is available for the user
+     * to choose in the certificate selection prompt:
+     * <ul>
+     *    <li>Device owner</li>
+     *    <li>Profile owner</li>
+     *    <li>Delegated certificate installer</li>
+     *    <li>Credential management app</li>
+     * </ul>
+     *
+     * <p>From Android {@link android.os.Build.VERSION_CODES#S}, the credential management app
+     * can call this API. If called by the credential management app, the componentName must be
+     * {@code null}. Note, there can only be a credential management app on an unmanaged device.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
      *            {@code null} if calling from a delegated certificate installer.
@@ -5599,7 +5744,7 @@
      *        successfully associated with it, {@code false} otherwise.
      * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
      *         owner, or {@code admin} is null but the calling application is not a delegated
-     *         certificate installer.
+     *         certificate installer or credential management app.
      */
     public boolean setKeyPairCertificate(@Nullable ComponentName admin,
             @NonNull String alias, @NonNull List<Certificate> certs, boolean isUserSelectable) {
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index cb879fc..c02fcab 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -16,6 +16,7 @@
 
 package android.app.admin;
 
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -229,5 +230,11 @@
     /**
      * Returns the profile owner component for the given user, or {@code null} if there is not one.
      */
+    @Nullable
     public abstract ComponentName getProfileOwnerAsUser(int userHandle);
+
+    /**
+     * Returns whether the given package is a device owner or a profile owner in the calling user.
+     */
+    public abstract boolean isDeviceOrProfileOwnerInCallingUser(String packageName);
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 58368bc..8be3cdc 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -89,6 +89,7 @@
     int getPasswordComplexity(boolean parent);
     void setRequiredPasswordComplexity(int passwordComplexity, boolean parent);
     int getRequiredPasswordComplexity(boolean parent);
+    int getAggregatedPasswordComplexityForUser(int userId);
     boolean isUsingUnifiedPassword(in ComponentName admin);
     int getCurrentFailedPasswordAttempts(int userHandle, boolean parent);
     int getProfileWithMinimumFailedPasswordsForWipe(int userHandle, boolean parent);
@@ -184,6 +185,7 @@
             in byte[] certBuffer, in byte[] certChainBuffer, String alias, boolean requestAccess,
             boolean isUserSelectable);
     boolean removeKeyPair(in ComponentName who, in String callerPackage, String alias);
+    boolean hasKeyPair(in String callerPackage, in String alias);
     boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm,
             in ParcelableKeyGenParameterSpec keySpec,
             in int idAttestationFlags, out KeymasterCertificateChain attestationChain);
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 86f91d7..1cf4567 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -85,7 +85,8 @@
             TAG_CRYPTO_SELF_TEST_COMPLETED,
             TAG_KEY_INTEGRITY_VIOLATION,
             TAG_CERT_VALIDATION_FAILURE,
-            TAG_CAMERA_POLICY_SET
+            TAG_CAMERA_POLICY_SET,
+            TAG_PASSWORD_COMPLEXITY_REQUIRED
     })
     public @interface SecurityLogTag {}
 
@@ -478,6 +479,21 @@
             SecurityLogTags.SECURITY_CAMERA_POLICY_SET;
 
     /**
+     * Indicates that an admin has set a password complexity requirement, using the platform's
+     * pre-defined complexity levels. The log entry contains the following information about the
+     * event, encapsulated in an {@link Object} array and accessible via
+     * {@link SecurityEvent#getData()}:
+     * <li> [0] admin package name ({@code String})
+     * <li> [1] admin user ID ({@code Integer})
+     * <li> [2] target user ID ({@code Integer})
+     * <li> [3] Password complexity ({@code Integer})
+     *
+     * @see DevicePolicyManager#setRequiredPasswordComplexity(int)
+     */
+    public static final int TAG_PASSWORD_COMPLEXITY_REQUIRED =
+            SecurityLogTags.SECURITY_PASSWORD_COMPLEXITY_REQUIRED;
+
+    /**
      * Event severity level indicating that the event corresponds to normal workflow.
      */
     public static final int LEVEL_INFO = 1;
@@ -617,6 +633,7 @@
                 case TAG_USER_RESTRICTION_ADDED:
                 case TAG_USER_RESTRICTION_REMOVED:
                 case TAG_CAMERA_POLICY_SET:
+                case TAG_PASSWORD_COMPLEXITY_REQUIRED:
                     return LEVEL_INFO;
                 case TAG_CERT_AUTHORITY_REMOVED:
                 case TAG_CRYPTO_SELF_TEST_COMPLETED:
diff --git a/core/java/android/app/admin/SecurityLogTags.logtags b/core/java/android/app/admin/SecurityLogTags.logtags
index 100fd4c..db5245c 100644
--- a/core/java/android/app/admin/SecurityLogTags.logtags
+++ b/core/java/android/app/admin/SecurityLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
 
 option java_package android.app.admin
 
@@ -39,3 +39,4 @@
 210032 security_key_integrity_violation         (key_id|3),(uid|1)
 210033 security_cert_validation_failure         (reason|3)
 210034 security_camera_policy_set               (package|3),(admin_user|1),(target_user|1),(disabled|1)
+210035 security_password_complexity_required    (package|3),(admin_user|1),(target_user|1),(complexity|1)
diff --git a/core/java/android/app/people/PeopleSpaceTile.java b/core/java/android/app/people/PeopleSpaceTile.java
index f5674e5..e870597 100644
--- a/core/java/android/app/people/PeopleSpaceTile.java
+++ b/core/java/android/app/people/PeopleSpaceTile.java
@@ -18,7 +18,12 @@
 
 import android.annotation.NonNull;
 import android.content.Intent;
+import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Parcel;
@@ -147,10 +152,10 @@
             mPackageName = intent == null ? null : intent.getPackage();
         }
 
-        public Builder(ShortcutInfo info) {
+        public Builder(ShortcutInfo info, LauncherApps launcherApps) {
             mId = info.getId();
             mUserName = info.getLabel();
-            mUserIcon = info.getIcon();
+            mUserIcon = convertDrawableToIcon(launcherApps.getShortcutIconDrawable(info, 0));
             mUid = info.getUserId();
             mPackageName = info.getPackage();
         }
@@ -270,4 +275,32 @@
                     return new PeopleSpaceTile[size];
                 }
             };
+
+    /** Converts {@code drawable} to a {@link Icon}. */
+    public static Icon convertDrawableToIcon(Drawable drawable) {
+        if (drawable == null) {
+            return null;
+        }
+
+        if (drawable instanceof BitmapDrawable) {
+            BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
+            if (bitmapDrawable.getBitmap() != null) {
+                return Icon.createWithBitmap(bitmapDrawable.getBitmap());
+            }
+        }
+
+        Bitmap bitmap;
+        if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
+            bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+            // Single color bitmap will be created of 1x1 pixel
+        } else {
+            bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+                    drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+        }
+
+        Canvas canvas = new Canvas(bitmap);
+        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+        drawable.draw(canvas);
+        return Icon.createWithBitmap(bitmap);
+    }
 }
diff --git a/core/java/android/content/TEST_MAPPING b/core/java/android/content/TEST_MAPPING
new file mode 100644
index 0000000..a2880df
--- /dev/null
+++ b/core/java/android/content/TEST_MAPPING
@@ -0,0 +1,52 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsContentTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        },
+        {
+          "include-filter": "android.content.wm.cts"
+        }
+      ],
+      "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"]
+    },
+    {
+      "name": "CtsOsTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        },
+        {
+          "include-filter": "android.os.cts.StrictModeTest"
+        }
+      ],
+      "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"]
+    },
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        },
+        {
+          "include-filter": "android.content.ContextTest"
+        }
+      ],
+      "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 8b411d5..b290679 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -119,8 +119,9 @@
      *        {@link #getTargetUserProfiles()} if different to the calling user, otherwise a
      *        {@link SecurityException} will be thrown.
      * @param callingActivity The activity to start the new activity from for the purposes of
-     *        deciding which task the new activity should belong to. If {@code null}, the activity
-     *        will always be started in a new task.
+     *        passing back any result and deciding which task the new activity should belong to. If
+     *        {@code null}, the activity will always be started in a new task and no result will be
+     *        returned.
      */
     @RequiresPermission(anyOf = {
             android.Manifest.permission.INTERACT_ACROSS_PROFILES,
@@ -146,8 +147,9 @@
      *        {@link #getTargetUserProfiles()} if different to the calling user, otherwise a
      *        {@link SecurityException} will be thrown.
      * @param callingActivity The activity to start the new activity from for the purposes of
-     *        deciding which task the new activity should belong to. If {@code null}, the activity
-     *        will always be started in a new task.
+     *        passing back any result and deciding which task the new activity should belong to. If
+     *        {@code null}, the activity will always be started in a new task and no result will be
+     *        returned.
      * @param options The activity options or {@code null}. See {@link android.app.ActivityOptions}.
      */
     @RequiresPermission(anyOf = {
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 3290022..ca5eeb1 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -911,12 +911,52 @@
     public interface DeviceConfig {
 
         /**
-         * Key for refresh rate in the zone defined by thresholds.
+         * Key for refresh rate in the low zone defined by thresholds.
          *
+         * Note that the name and value don't match because they were added before we had a high
+         * zone to consider.
          * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
          * @see android.R.integer#config_defaultZoneBehavior
          */
-        String KEY_REFRESH_RATE_IN_ZONE = "refresh_rate_in_zone";
+        String KEY_REFRESH_RATE_IN_LOW_ZONE = "refresh_rate_in_zone";
+
+        /**
+         * Key for accessing the low display brightness thresholds for the configured refresh
+         * rate zone.
+         * The value will be a pair of comma separated integers representing the minimum and maximum
+         * thresholds of the zone, respectively, in display backlight units (i.e. [0, 255]).
+         *
+         * Note that the name and value don't match because they were added before we had a high
+         * zone to consider.
+         *
+         * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
+         * @see android.R.array#config_brightnessThresholdsOfPeakRefreshRate
+         * @hide
+         */
+        String KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS =
+                "peak_refresh_rate_brightness_thresholds";
+
+        /**
+         * Key for accessing the low ambient brightness thresholds for the configured refresh
+         * rate zone. The value will be a pair of comma separated integers representing the minimum
+         * and maximum thresholds of the zone, respectively, in lux.
+         *
+         * Note that the name and value don't match because they were added before we had a high
+         * zone to consider.
+         *
+         * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
+         * @see android.R.array#config_ambientThresholdsOfPeakRefreshRate
+         * @hide
+         */
+        String KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS =
+                "peak_refresh_rate_ambient_thresholds";
+        /**
+         * Key for refresh rate in the high zone defined by thresholds.
+         *
+         * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
+         * @see android.R.integer#config_fixedRefreshRateInHighZone
+         */
+        String KEY_REFRESH_RATE_IN_HIGH_ZONE = "refresh_rate_in_high_zone";
 
         /**
          * Key for accessing the display brightness thresholds for the configured refresh rate zone.
@@ -924,11 +964,11 @@
          * thresholds of the zone, respectively, in display backlight units (i.e. [0, 255]).
          *
          * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
-         * @see android.R.array#config_brightnessThresholdsOfPeakRefreshRate
+         * @see android.R.array#config_brightnessHighThresholdsOfFixedRefreshRate
          * @hide
          */
-        String KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS =
-                "peak_refresh_rate_brightness_thresholds";
+        String KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS =
+                "fixed_refresh_rate_high_display_brightness_thresholds";
 
         /**
          * Key for accessing the ambient brightness thresholds for the configured refresh rate zone.
@@ -936,12 +976,11 @@
          * thresholds of the zone, respectively, in lux.
          *
          * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
-         * @see android.R.array#config_ambientThresholdsOfPeakRefreshRate
+         * @see android.R.array#config_ambientHighThresholdsOfFixedRefreshRate
          * @hide
          */
-        String KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS =
-                "peak_refresh_rate_ambient_thresholds";
-
+        String KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS =
+                "fixed_refresh_rate_high_ambient_brightness_thresholds";
         /**
          * Key for default peak refresh rate
          *
diff --git a/core/java/android/hardware/hdmi/HdmiDeviceInfo.java b/core/java/android/hardware/hdmi/HdmiDeviceInfo.java
index 3fd20f1..c6007f1 100644
--- a/core/java/android/hardware/hdmi/HdmiDeviceInfo.java
+++ b/core/java/android/hardware/hdmi/HdmiDeviceInfo.java
@@ -513,4 +513,19 @@
                 && mDeviceId == other.mDeviceId
                 && mAdopterId == other.mAdopterId;
     }
+
+    @Override
+    public int hashCode() {
+        return java.util.Objects.hash(
+                mHdmiDeviceType,
+                mPhysicalAddress,
+                mPortId,
+                mLogicalAddress,
+                mDeviceType,
+                mVendorId,
+                mDevicePowerStatus,
+                mDisplayName,
+                mDeviceId,
+                mAdopterId);
+    }
 }
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 4a5d831..67e75d2 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -585,10 +585,12 @@
                 Log.w(TAG, "The token has already registered, ignore this initialization.");
                 return;
             }
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initializeInternal");
             mPrivOps.set(privilegedOperations);
             InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
             updateInputMethodDisplay(displayId);
             attachToken(token);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
 
         /**
@@ -642,6 +644,7 @@
         @MainThread
         @Override
         public void bindInput(InputBinding binding) {
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.bindInput");
             mInputBinding = binding;
             mInputConnection = binding.getConnection();
             if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
@@ -649,6 +652,7 @@
             reportFullscreenMode();
             initialize();
             onBindInput();
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
 
         /**
@@ -686,7 +690,9 @@
         @Override
         public void restartInput(InputConnection ic, EditorInfo attribute) {
             if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute);
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.restartInput");
             doStartInput(ic, attribute, true);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
 
         /**
@@ -1253,6 +1259,7 @@
     }
 
     @Override public void onCreate() {
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.onCreate");
         mTheme = Resources.selectSystemTheme(mTheme,
                 getApplicationInfo().targetSdkVersion,
                 android.R.style.Theme_InputMethod,
@@ -1273,6 +1280,7 @@
         // in non-default display.
         mInflater = (LayoutInflater)getSystemService(
                 Context.LAYOUT_INFLATER_SERVICE);
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initSoftInputWindow");
         mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
                 WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
         mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars());
@@ -1294,10 +1302,12 @@
 
         initViews();
         mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
+        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
 
         mInlineSuggestionSessionController = new InlineSuggestionSessionController(
                 this::onCreateInlineSuggestionsRequest, this::getHostInputToken,
                 this::onInlineSuggestionsResponse);
+        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
     /**
@@ -1318,6 +1328,7 @@
     }
 
     void initViews() {
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initViews");
         mInitialized = false;
         mViewsCreated = false;
         mShowInputRequested = false;
@@ -1352,6 +1363,7 @@
         mCandidatesVisibility = getCandidatesHiddenVisibility();
         mCandidatesFrame.setVisibility(mCandidatesVisibility);
         mInputFrame.setVisibility(View.GONE);
+        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
     @Override public void onDestroy() {
@@ -1393,6 +1405,7 @@
     }
 
     private void resetStateForNewConfiguration() {
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.resetStateForNewConfiguration");
         boolean visible = mDecorViewVisible;
         int showFlags = mShowInputFlags;
         boolean showingInput = mShowInputRequested;
@@ -1428,6 +1441,7 @@
             boolean showing = onEvaluateInputViewShown();
             setImeWindowStatus(IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
         }
+        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
     /**
@@ -1589,6 +1603,7 @@
      * is currently running in fullscreen mode.
      */
     public void updateFullscreenMode() {
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.updateFullscreenMode");
         boolean isFullscreen = mShowInputRequested && onEvaluateFullscreenMode();
         boolean changed = mLastShowInputRequested != mShowInputRequested;
         if (mIsFullscreen != isFullscreen || !mFullscreenApplied) {
@@ -1627,6 +1642,7 @@
             onConfigureWindow(mWindow.getWindow(), isFullscreen, !mShowInputRequested);
             mLastShowInputRequested = mShowInputRequested;
         }
+        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
     
     /**
@@ -1755,6 +1771,7 @@
      * @param outInsets Fill in with the current UI insets.
      */
     public void onComputeInsets(Insets outInsets) {
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.onComputeInsets");
         int[] loc = mTmpLocation;
         if (mInputFrame.getVisibility() == View.VISIBLE) {
             mInputFrame.getLocationInWindow(loc);
@@ -1775,6 +1792,7 @@
         outInsets.visibleTopInsets = loc[1];
         outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE;
         outInsets.touchableRegion.setEmpty();
+        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
     
     /**
@@ -2165,7 +2183,7 @@
         }
 
         ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", this);
-
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow");
         mDecorViewWasVisible = mDecorViewVisible;
         mInShowWindow = true;
         final int previousImeWindowStatus =
@@ -2189,6 +2207,7 @@
         }
         mDecorViewWasVisible = true;
         mInShowWindow = false;
+        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
 
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 0f2a9f2..f76eb86 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -1138,8 +1138,7 @@
          *
          * @param primitiveId The primitive to add
          * @param scale The scale to apply to the intensity of the primitive.
-         * @param delay The amount of time, in milliseconds, to wait between playing the prior
-         *              primitive and this one
+         * @param delay The amount of time in milliseconds to wait before playing this primitive
          * @return The {@link Composition} object to enable adding multiple primitives in one chain.
          */
         @NonNull
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index 901494b..237a9f2 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -200,6 +200,21 @@
         internalPath = parcel.readString8();
     }
 
+    public VolumeInfo(VolumeInfo volumeInfo) {
+        this.id = volumeInfo.id;
+        this.type = volumeInfo.type;
+        this.disk = volumeInfo.disk;
+        this.partGuid = volumeInfo.partGuid;
+        this.mountFlags = volumeInfo.mountFlags;
+        this.mountUserId = volumeInfo.mountUserId;
+        this.state = volumeInfo.state;
+        this.fsType = volumeInfo.fsType;
+        this.fsUuid = volumeInfo.fsUuid;
+        this.fsLabel = volumeInfo.fsLabel;
+        this.path = volumeInfo.path;
+        this.internalPath = volumeInfo.internalPath;
+    }
+
     @UnsupportedAppUsage
     public static @NonNull String getEnvironmentForState(int state) {
         final String envState = sStateToEnvironment.get(state);
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 714bcea..44cc0f5 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -451,6 +451,22 @@
             "connectivity_thermal_power_manager";
 
     /**
+     * Namespace for all statsd native features that can be applied immediately.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_STATSD_NATIVE = "statsd_native";
+
+    /**
+     * Namespace for all statsd native features that are applied on boot.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_STATSD_NATIVE_BOOT = "statsd_native_boot";
+
+    /**
      * Namespace for configuration related features.
      *
      * @hide
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 1892163..6ef9e7e 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -16,6 +16,19 @@
 
 package android.security.keymaster;
 
+import android.hardware.keymint.Algorithm;
+import android.hardware.keymint.BlockMode;
+import android.hardware.keymint.Digest;
+import android.hardware.keymint.ErrorCode;
+import android.hardware.keymint.HardwareAuthenticatorType;
+import android.hardware.keymint.KeyFormat;
+import android.hardware.keymint.KeyOrigin;
+import android.hardware.keymint.KeyPurpose;
+import android.hardware.keymint.PaddingMode;
+import android.hardware.keymint.SecurityLevel;
+import android.hardware.keymint.Tag;
+import android.hardware.keymint.TagType;
+
 import java.util.HashMap;
 import java.util.Map;
 
@@ -30,206 +43,284 @@
     private KeymasterDefs() {}
 
     // Tag types.
-    public static final int KM_INVALID = 0 << 28;
-    public static final int KM_ENUM = 1 << 28;
-    public static final int KM_ENUM_REP = 2 << 28;
-    public static final int KM_UINT = 3 << 28;
-    public static final int KM_UINT_REP = 4 << 28;
-    public static final int KM_ULONG = 5 << 28;
-    public static final int KM_DATE = 6 << 28;
-    public static final int KM_BOOL = 7 << 28;
-    public static final int KM_BIGNUM = 8 << 28;
-    public static final int KM_BYTES = 9 << 28;
-    public static final int KM_ULONG_REP = 10 << 28;
+    public static final int KM_INVALID = TagType.INVALID;
+    public static final int KM_ENUM = TagType.ENUM;
+    public static final int KM_ENUM_REP = TagType.ENUM_REP;
+    public static final int KM_UINT = TagType.UINT;
+    public static final int KM_UINT_REP = TagType.UINT_REP;
+    public static final int KM_ULONG = TagType.ULONG;
+    public static final int KM_DATE = TagType.DATE;
+    public static final int KM_BOOL = TagType.BOOL;
+    public static final int KM_BIGNUM = TagType.BIGNUM;
+    public static final int KM_BYTES = TagType.BYTES;
+    public static final int KM_ULONG_REP = TagType.ULONG_REP;
 
     // Tag values.
-    public static final int KM_TAG_INVALID = KM_INVALID | 0;
-    public static final int KM_TAG_PURPOSE = KM_ENUM_REP | 1;
-    public static final int KM_TAG_ALGORITHM = KM_ENUM | 2;
-    public static final int KM_TAG_KEY_SIZE = KM_UINT | 3;
-    public static final int KM_TAG_BLOCK_MODE = KM_ENUM_REP | 4;
-    public static final int KM_TAG_DIGEST = KM_ENUM_REP | 5;
-    public static final int KM_TAG_PADDING = KM_ENUM_REP | 6;
-    public static final int KM_TAG_CALLER_NONCE = KM_BOOL | 7;
-    public static final int KM_TAG_MIN_MAC_LENGTH = KM_UINT | 8;
+    public static final int KM_TAG_INVALID = Tag.INVALID; // KM_INVALID | 0;
+    public static final int KM_TAG_PURPOSE = Tag.PURPOSE; // KM_ENUM_REP | 1;
+    public static final int KM_TAG_ALGORITHM = Tag.ALGORITHM; // KM_ENUM | 2;
+    public static final int KM_TAG_KEY_SIZE = Tag.KEY_SIZE; // KM_UINT | 3;
+    public static final int KM_TAG_BLOCK_MODE = Tag.BLOCK_MODE; // KM_ENUM_REP | 4;
+    public static final int KM_TAG_DIGEST = Tag.DIGEST; // KM_ENUM_REP | 5;
+    public static final int KM_TAG_PADDING = Tag.PADDING; // KM_ENUM_REP | 6;
+    public static final int KM_TAG_CALLER_NONCE = Tag.CALLER_NONCE; // KM_BOOL | 7;
+    public static final int KM_TAG_MIN_MAC_LENGTH = Tag.MIN_MAC_LENGTH; // KM_UINT | 8;
 
-    public static final int KM_TAG_RESCOPING_ADD = KM_ENUM_REP | 101;
-    public static final int KM_TAG_RESCOPING_DEL = KM_ENUM_REP | 102;
-    public static final int KM_TAG_BLOB_USAGE_REQUIREMENTS = KM_ENUM | 705;
+    public static final int KM_TAG_BLOB_USAGE_REQUIREMENTS =
+            Tag.BLOB_USAGE_REQUIREMENTS; // KM_ENUM | 705;
 
-    public static final int KM_TAG_RSA_PUBLIC_EXPONENT = KM_ULONG | 200;
-    public static final int KM_TAG_INCLUDE_UNIQUE_ID = KM_BOOL | 202;
+    public static final int KM_TAG_RSA_PUBLIC_EXPONENT = Tag.RSA_PUBLIC_EXPONENT; // KM_ULONG | 200;
+    public static final int KM_TAG_INCLUDE_UNIQUE_ID = Tag.INCLUDE_UNIQUE_ID; // KM_BOOL | 202;
 
-    public static final int KM_TAG_ACTIVE_DATETIME = KM_DATE | 400;
-    public static final int KM_TAG_ORIGINATION_EXPIRE_DATETIME = KM_DATE | 401;
-    public static final int KM_TAG_USAGE_EXPIRE_DATETIME = KM_DATE | 402;
-    public static final int KM_TAG_MIN_SECONDS_BETWEEN_OPS = KM_UINT | 403;
-    public static final int KM_TAG_MAX_USES_PER_BOOT = KM_UINT | 404;
+    public static final int KM_TAG_ACTIVE_DATETIME = Tag.ACTIVE_DATETIME; // KM_DATE | 400;
+    public static final int KM_TAG_ORIGINATION_EXPIRE_DATETIME =
+            Tag.ORIGINATION_EXPIRE_DATETIME; // KM_DATE | 401;
+    public static final int KM_TAG_USAGE_EXPIRE_DATETIME =
+            Tag.USAGE_EXPIRE_DATETIME; // KM_DATE | 402;
+    public static final int KM_TAG_MIN_SECONDS_BETWEEN_OPS =
+            Tag.MIN_SECONDS_BETWEEN_OPS; // KM_UINT | 403;
+    public static final int KM_TAG_MAX_USES_PER_BOOT = Tag.MAX_USES_PER_BOOT; // KM_UINT | 404;
 
-    public static final int KM_TAG_ALL_USERS = KM_BOOL | 500;
-    public static final int KM_TAG_USER_ID = KM_UINT | 501;
-    public static final int KM_TAG_USER_SECURE_ID = KM_ULONG_REP | 502;
-    public static final int KM_TAG_NO_AUTH_REQUIRED = KM_BOOL | 503;
-    public static final int KM_TAG_USER_AUTH_TYPE = KM_ENUM | 504;
-    public static final int KM_TAG_AUTH_TIMEOUT = KM_UINT | 505;
-    public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506;
-    public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507;
-    public static final int KM_TAG_TRUSTED_CONFIRMATION_REQUIRED = KM_BOOL | 508;
-    public static final int KM_TAG_UNLOCKED_DEVICE_REQUIRED = KM_BOOL | 509;
+    public static final int KM_TAG_USER_ID = Tag.USER_ID; // KM_UINT | 501;
+    public static final int KM_TAG_USER_SECURE_ID = Tag.USER_SECURE_ID; // KM_ULONG_REP | 502;
+    public static final int KM_TAG_NO_AUTH_REQUIRED = Tag.NO_AUTH_REQUIRED; // KM_BOOL | 503;
+    public static final int KM_TAG_USER_AUTH_TYPE = Tag.USER_AUTH_TYPE; // KM_ENUM | 504;
+    public static final int KM_TAG_AUTH_TIMEOUT = Tag.AUTH_TIMEOUT; // KM_UINT | 505;
+    public static final int KM_TAG_ALLOW_WHILE_ON_BODY = Tag.ALLOW_WHILE_ON_BODY; // KM_BOOL | 506;
+    public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED =
+            Tag.TRUSTED_USER_PRESENCE_REQUIRED; // KM_BOOL | 507;
+    public static final int KM_TAG_TRUSTED_CONFIRMATION_REQUIRED =
+            Tag.TRUSTED_CONFIRMATION_REQUIRED; // KM_BOOL | 508;
+    public static final int KM_TAG_UNLOCKED_DEVICE_REQUIRED =
+            Tag.UNLOCKED_DEVICE_REQUIRED; // KM_BOOL | 509;
 
-    public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
-    public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
+    public static final int KM_TAG_APPLICATION_ID = Tag.APPLICATION_ID; // KM_BYTES | 601;
 
-    public static final int KM_TAG_CREATION_DATETIME = KM_DATE | 701;
-    public static final int KM_TAG_ORIGIN = KM_ENUM | 702;
-    public static final int KM_TAG_ROLLBACK_RESISTANT = KM_BOOL | 703;
-    public static final int KM_TAG_ROOT_OF_TRUST = KM_BYTES | 704;
-    public static final int KM_TAG_UNIQUE_ID = KM_BYTES | 707;
-    public static final int KM_TAG_ATTESTATION_CHALLENGE = KM_BYTES | 708;
-    public static final int KM_TAG_ATTESTATION_ID_BRAND = KM_BYTES | 710;
-    public static final int KM_TAG_ATTESTATION_ID_DEVICE = KM_BYTES | 711;
-    public static final int KM_TAG_ATTESTATION_ID_PRODUCT = KM_BYTES | 712;
-    public static final int KM_TAG_ATTESTATION_ID_SERIAL = KM_BYTES | 713;
-    public static final int KM_TAG_ATTESTATION_ID_IMEI = KM_BYTES | 714;
-    public static final int KM_TAG_ATTESTATION_ID_MEID = KM_BYTES | 715;
-    public static final int KM_TAG_ATTESTATION_ID_MANUFACTURER = KM_BYTES | 716;
-    public static final int KM_TAG_ATTESTATION_ID_MODEL = KM_BYTES | 717;
-    public static final int KM_TAG_VENDOR_PATCHLEVEL =  KM_UINT | 718;
-    public static final int KM_TAG_BOOT_PATCHLEVEL =  KM_UINT | 719;
-    public static final int KM_TAG_DEVICE_UNIQUE_ATTESTATION = KM_BOOL | 720;
+    public static final int KM_TAG_CREATION_DATETIME = Tag.CREATION_DATETIME; // KM_DATE | 701;
+    public static final int KM_TAG_ORIGIN = Tag.ORIGIN; // KM_ENUM | 702;
+    public static final int KM_TAG_ROLLBACK_RESISTANT = Tag.ROLLBACK_RESISTANCE; // KM_BOOL | 703;
+    public static final int KM_TAG_ROOT_OF_TRUST = Tag.ROOT_OF_TRUST; // KM_BYTES | 704;
+    public static final int KM_TAG_UNIQUE_ID = Tag.UNIQUE_ID; // KM_BYTES | 707;
+    public static final int KM_TAG_ATTESTATION_CHALLENGE =
+            Tag.ATTESTATION_CHALLENGE; // KM_BYTES | 708;
+    public static final int KM_TAG_ATTESTATION_ID_BRAND =
+            Tag.ATTESTATION_ID_BRAND; // KM_BYTES | 710;
+    public static final int KM_TAG_ATTESTATION_ID_DEVICE =
+            Tag.ATTESTATION_ID_DEVICE; // KM_BYTES | 711;
+    public static final int KM_TAG_ATTESTATION_ID_PRODUCT =
+            Tag.ATTESTATION_ID_PRODUCT; // KM_BYTES | 712;
+    public static final int KM_TAG_ATTESTATION_ID_SERIAL =
+            Tag.ATTESTATION_ID_SERIAL; // KM_BYTES | 713;
+    public static final int KM_TAG_ATTESTATION_ID_IMEI =
+            Tag.ATTESTATION_ID_IMEI; // KM_BYTES | 714;
+    public static final int KM_TAG_ATTESTATION_ID_MEID =
+            Tag.ATTESTATION_ID_MEID; // KM_BYTES | 715;
+    public static final int KM_TAG_ATTESTATION_ID_MANUFACTURER =
+            Tag.ATTESTATION_ID_MANUFACTURER; // KM_BYTES | 716;
+    public static final int KM_TAG_ATTESTATION_ID_MODEL =
+            Tag.ATTESTATION_ID_MODEL; // KM_BYTES | 717;
+    public static final int KM_TAG_VENDOR_PATCHLEVEL =
+            Tag.VENDOR_PATCHLEVEL; // KM_UINT | 718;
+    public static final int KM_TAG_BOOT_PATCHLEVEL =
+            Tag.BOOT_PATCHLEVEL; // KM_UINT | 719;
+    public static final int KM_TAG_DEVICE_UNIQUE_ATTESTATION =
+            Tag.DEVICE_UNIQUE_ATTESTATION; // KM_BOOL | 720;
 
-    public static final int KM_TAG_ASSOCIATED_DATA = KM_BYTES | 1000;
-    public static final int KM_TAG_NONCE = KM_BYTES | 1001;
-    public static final int KM_TAG_AUTH_TOKEN = KM_BYTES | 1002;
-    public static final int KM_TAG_MAC_LENGTH = KM_UINT | 1003;
+    public static final int KM_TAG_ASSOCIATED_DATA = Tag.ASSOCIATED_DATA; // KM_BYTES | 1000;
+    public static final int KM_TAG_NONCE = Tag.NONCE; // KM_BYTES | 1001;
+    public static final int KM_TAG_MAC_LENGTH = Tag.MAC_LENGTH; // KM_UINT | 1003;
 
     // Algorithm values.
-    public static final int KM_ALGORITHM_RSA = 1;
-    public static final int KM_ALGORITHM_EC = 3;
-    public static final int KM_ALGORITHM_AES = 32;
-    public static final int KM_ALGORITHM_3DES = 33;
-    public static final int KM_ALGORITHM_HMAC = 128;
+    public static final int KM_ALGORITHM_RSA = Algorithm.RSA;
+    public static final int KM_ALGORITHM_EC = Algorithm.EC;
+    public static final int KM_ALGORITHM_AES = Algorithm.AES;
+    public static final int KM_ALGORITHM_3DES = Algorithm.TRIPLE_DES;
+    public static final int KM_ALGORITHM_HMAC = Algorithm.HMAC;
 
     // Block modes.
-    public static final int KM_MODE_ECB = 1;
-    public static final int KM_MODE_CBC = 2;
-    public static final int KM_MODE_CTR = 3;
-    public static final int KM_MODE_GCM = 32;
+    public static final int KM_MODE_ECB = BlockMode.ECB;
+    public static final int KM_MODE_CBC = BlockMode.CBC;
+    public static final int KM_MODE_CTR = BlockMode.CTR;
+    public static final int KM_MODE_GCM = BlockMode.GCM;
 
     // Padding modes.
-    public static final int KM_PAD_NONE = 1;
-    public static final int KM_PAD_RSA_OAEP = 2;
-    public static final int KM_PAD_RSA_PSS = 3;
-    public static final int KM_PAD_RSA_PKCS1_1_5_ENCRYPT = 4;
-    public static final int KM_PAD_RSA_PKCS1_1_5_SIGN = 5;
-    public static final int KM_PAD_PKCS7 = 64;
+    public static final int KM_PAD_NONE = PaddingMode.NONE;
+    public static final int KM_PAD_RSA_OAEP = PaddingMode.RSA_OAEP;
+    public static final int KM_PAD_RSA_PSS = PaddingMode.RSA_PSS;
+    public static final int KM_PAD_RSA_PKCS1_1_5_ENCRYPT = PaddingMode.RSA_PKCS1_1_5_ENCRYPT;
+    public static final int KM_PAD_RSA_PKCS1_1_5_SIGN = PaddingMode.RSA_PKCS1_1_5_SIGN;
+    public static final int KM_PAD_PKCS7 = PaddingMode.PKCS7;
 
     // Digest modes.
-    public static final int KM_DIGEST_NONE = 0;
-    public static final int KM_DIGEST_MD5 = 1;
-    public static final int KM_DIGEST_SHA1 = 2;
-    public static final int KM_DIGEST_SHA_2_224 = 3;
-    public static final int KM_DIGEST_SHA_2_256 = 4;
-    public static final int KM_DIGEST_SHA_2_384 = 5;
-    public static final int KM_DIGEST_SHA_2_512 = 6;
+    public static final int KM_DIGEST_NONE = Digest.NONE;
+    public static final int KM_DIGEST_MD5 = Digest.MD5;
+    public static final int KM_DIGEST_SHA1 = Digest.SHA1;
+    public static final int KM_DIGEST_SHA_2_224 = Digest.SHA_2_224;
+    public static final int KM_DIGEST_SHA_2_256 = Digest.SHA_2_256;
+    public static final int KM_DIGEST_SHA_2_384 = Digest.SHA_2_384;
+    public static final int KM_DIGEST_SHA_2_512 = Digest.SHA_2_512;
 
     // Key origins.
-    public static final int KM_ORIGIN_GENERATED = 0;
-    public static final int KM_ORIGIN_IMPORTED = 2;
-    public static final int KM_ORIGIN_UNKNOWN = 3;
-    public static final int KM_ORIGIN_SECURELY_IMPORTED = 4;
+    public static final int KM_ORIGIN_GENERATED = KeyOrigin.GENERATED;
+    public static final int KM_ORIGIN_DERIVED = KeyOrigin.DERIVED;
+    public static final int KM_ORIGIN_IMPORTED = KeyOrigin.IMPORTED;
+    public static final int KM_ORIGIN_UNKNOWN = KeyOrigin.RESERVED;
+    public static final int KM_ORIGIN_SECURELY_IMPORTED = KeyOrigin.SECURELY_IMPORTED;
 
     // Key usability requirements.
     public static final int KM_BLOB_STANDALONE = 0;
     public static final int KM_BLOB_REQUIRES_FILE_SYSTEM = 1;
 
     // Operation Purposes.
-    public static final int KM_PURPOSE_ENCRYPT = 0;
-    public static final int KM_PURPOSE_DECRYPT = 1;
-    public static final int KM_PURPOSE_SIGN = 2;
-    public static final int KM_PURPOSE_VERIFY = 3;
-    public static final int KM_PURPOSE_WRAP = 5;
+    public static final int KM_PURPOSE_ENCRYPT = KeyPurpose.ENCRYPT;
+    public static final int KM_PURPOSE_DECRYPT = KeyPurpose.DECRYPT;
+    public static final int KM_PURPOSE_SIGN = KeyPurpose.SIGN;
+    public static final int KM_PURPOSE_VERIFY = KeyPurpose.VERIFY;
+    public static final int KM_PURPOSE_WRAP = KeyPurpose.WRAP_KEY;
 
     // Key formats.
-    public static final int KM_KEY_FORMAT_X509 = 0;
-    public static final int KM_KEY_FORMAT_PKCS8 = 1;
-    public static final int KM_KEY_FORMAT_RAW = 3;
+    public static final int KM_KEY_FORMAT_X509 = KeyFormat.X509;
+    public static final int KM_KEY_FORMAT_PKCS8 = KeyFormat.PKCS8;
+    public static final int KM_KEY_FORMAT_RAW = KeyFormat.RAW;
 
     // User authenticators.
-    public static final int HW_AUTH_PASSWORD = 1 << 0;
-    public static final int HW_AUTH_BIOMETRIC = 1 << 1;
+    public static final int HW_AUTH_PASSWORD = HardwareAuthenticatorType.PASSWORD;
+    public static final int HW_AUTH_BIOMETRIC = HardwareAuthenticatorType.FINGERPRINT;
 
     // Security Levels.
-    public static final int KM_SECURITY_LEVEL_SOFTWARE = 0;
-    public static final int KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 1;
-    public static final int KM_SECURITY_LEVEL_STRONGBOX = 2;
+    public static final int KM_SECURITY_LEVEL_SOFTWARE = SecurityLevel.SOFTWARE;
+    public static final int KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT =
+            SecurityLevel.TRUSTED_ENVIRONMENT;
+    public static final int KM_SECURITY_LEVEL_STRONGBOX = SecurityLevel.STRONGBOX;
 
     // Error codes.
-    public static final int KM_ERROR_OK = 0;
-    public static final int KM_ERROR_ROOT_OF_TRUST_ALREADY_SET = -1;
-    public static final int KM_ERROR_UNSUPPORTED_PURPOSE = -2;
-    public static final int KM_ERROR_INCOMPATIBLE_PURPOSE = -3;
-    public static final int KM_ERROR_UNSUPPORTED_ALGORITHM = -4;
-    public static final int KM_ERROR_INCOMPATIBLE_ALGORITHM = -5;
-    public static final int KM_ERROR_UNSUPPORTED_KEY_SIZE = -6;
-    public static final int KM_ERROR_UNSUPPORTED_BLOCK_MODE = -7;
-    public static final int KM_ERROR_INCOMPATIBLE_BLOCK_MODE = -8;
-    public static final int KM_ERROR_UNSUPPORTED_MAC_LENGTH = -9;
-    public static final int KM_ERROR_UNSUPPORTED_PADDING_MODE = -10;
-    public static final int KM_ERROR_INCOMPATIBLE_PADDING_MODE = -11;
-    public static final int KM_ERROR_UNSUPPORTED_DIGEST = -12;
-    public static final int KM_ERROR_INCOMPATIBLE_DIGEST = -13;
-    public static final int KM_ERROR_INVALID_EXPIRATION_TIME = -14;
-    public static final int KM_ERROR_INVALID_USER_ID = -15;
-    public static final int KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT = -16;
-    public static final int KM_ERROR_UNSUPPORTED_KEY_FORMAT = -17;
-    public static final int KM_ERROR_INCOMPATIBLE_KEY_FORMAT = -18;
-    public static final int KM_ERROR_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = -19;
-    public static final int KM_ERROR_UNSUPPORTED_KEY_VERIFICATION_ALGORITHM = -20;
-    public static final int KM_ERROR_INVALID_INPUT_LENGTH = -21;
-    public static final int KM_ERROR_KEY_EXPORT_OPTIONS_INVALID = -22;
-    public static final int KM_ERROR_DELEGATION_NOT_ALLOWED = -23;
-    public static final int KM_ERROR_KEY_NOT_YET_VALID = -24;
-    public static final int KM_ERROR_KEY_EXPIRED = -25;
-    public static final int KM_ERROR_KEY_USER_NOT_AUTHENTICATED = -26;
-    public static final int KM_ERROR_OUTPUT_PARAMETER_NULL = -27;
-    public static final int KM_ERROR_INVALID_OPERATION_HANDLE = -28;
-    public static final int KM_ERROR_INSUFFICIENT_BUFFER_SPACE = -29;
-    public static final int KM_ERROR_VERIFICATION_FAILED = -30;
-    public static final int KM_ERROR_TOO_MANY_OPERATIONS = -31;
-    public static final int KM_ERROR_UNEXPECTED_NULL_POINTER = -32;
-    public static final int KM_ERROR_INVALID_KEY_BLOB = -33;
-    public static final int KM_ERROR_IMPORTED_KEY_NOT_ENCRYPTED = -34;
-    public static final int KM_ERROR_IMPORTED_KEY_DECRYPTION_FAILED = -35;
-    public static final int KM_ERROR_IMPORTED_KEY_NOT_SIGNED = -36;
-    public static final int KM_ERROR_IMPORTED_KEY_VERIFICATION_FAILED = -37;
-    public static final int KM_ERROR_INVALID_ARGUMENT = -38;
-    public static final int KM_ERROR_UNSUPPORTED_TAG = -39;
-    public static final int KM_ERROR_INVALID_TAG = -40;
-    public static final int KM_ERROR_MEMORY_ALLOCATION_FAILED = -41;
-    public static final int KM_ERROR_INVALID_RESCOPING = -42;
-    public static final int KM_ERROR_IMPORT_PARAMETER_MISMATCH = -44;
-    public static final int KM_ERROR_SECURE_HW_ACCESS_DENIED = -45;
-    public static final int KM_ERROR_OPERATION_CANCELLED = -46;
-    public static final int KM_ERROR_CONCURRENT_ACCESS_CONFLICT = -47;
-    public static final int KM_ERROR_SECURE_HW_BUSY = -48;
-    public static final int KM_ERROR_SECURE_HW_COMMUNICATION_FAILED = -49;
-    public static final int KM_ERROR_UNSUPPORTED_EC_FIELD = -50;
-    public static final int KM_ERROR_MISSING_NONCE = -51;
-    public static final int KM_ERROR_INVALID_NONCE = -52;
-    public static final int KM_ERROR_MISSING_MAC_LENGTH = -53;
-    public static final int KM_ERROR_KEY_RATE_LIMIT_EXCEEDED = -54;
-    public static final int KM_ERROR_CALLER_NONCE_PROHIBITED = -55;
-    public static final int KM_ERROR_KEY_MAX_OPS_EXCEEDED = -56;
-    public static final int KM_ERROR_INVALID_MAC_LENGTH = -57;
-    public static final int KM_ERROR_MISSING_MIN_MAC_LENGTH = -58;
-    public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH = -59;
-    public static final int KM_ERROR_CANNOT_ATTEST_IDS = -66;
-    public static final int KM_ERROR_HARDWARE_TYPE_UNAVAILABLE = -68;
-    public static final int KM_ERROR_DEVICE_LOCKED = -72;
-    public static final int KM_ERROR_UNIMPLEMENTED = -100;
-    public static final int KM_ERROR_VERSION_MISMATCH = -101;
-    public static final int KM_ERROR_UNKNOWN_ERROR = -1000;
+    public static final int KM_ERROR_OK = ErrorCode.OK;
+    public static final int KM_ERROR_ROOT_OF_TRUST_ALREADY_SET =
+            ErrorCode.ROOT_OF_TRUST_ALREADY_SET; // -1;
+    public static final int KM_ERROR_UNSUPPORTED_PURPOSE =
+            ErrorCode.UNSUPPORTED_PURPOSE; // -2;
+    public static final int KM_ERROR_INCOMPATIBLE_PURPOSE =
+            ErrorCode.INCOMPATIBLE_PURPOSE; // -3;
+    public static final int KM_ERROR_UNSUPPORTED_ALGORITHM =
+            ErrorCode.UNSUPPORTED_ALGORITHM; // -4;
+    public static final int KM_ERROR_INCOMPATIBLE_ALGORITHM =
+            ErrorCode.INCOMPATIBLE_ALGORITHM; // -5;
+    public static final int KM_ERROR_UNSUPPORTED_KEY_SIZE =
+            ErrorCode.UNSUPPORTED_KEY_SIZE; // -6;
+    public static final int KM_ERROR_UNSUPPORTED_BLOCK_MODE =
+            ErrorCode.UNSUPPORTED_BLOCK_MODE; // -7;
+    public static final int KM_ERROR_INCOMPATIBLE_BLOCK_MODE =
+            ErrorCode.INCOMPATIBLE_BLOCK_MODE; // -8;
+    public static final int KM_ERROR_UNSUPPORTED_MAC_LENGTH =
+            ErrorCode.UNSUPPORTED_MAC_LENGTH; // -9;
+    public static final int KM_ERROR_UNSUPPORTED_PADDING_MODE =
+            ErrorCode.UNSUPPORTED_PADDING_MODE; // -10;
+    public static final int KM_ERROR_INCOMPATIBLE_PADDING_MODE =
+            ErrorCode.INCOMPATIBLE_PADDING_MODE; // -11;
+    public static final int KM_ERROR_UNSUPPORTED_DIGEST =
+            ErrorCode.UNSUPPORTED_DIGEST; // -12;
+    public static final int KM_ERROR_INCOMPATIBLE_DIGEST =
+            ErrorCode.INCOMPATIBLE_DIGEST; // -13;
+    public static final int KM_ERROR_INVALID_EXPIRATION_TIME =
+            ErrorCode.INVALID_EXPIRATION_TIME; // -14;
+    public static final int KM_ERROR_INVALID_USER_ID =
+            ErrorCode.INVALID_USER_ID; // -15;
+    public static final int KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT =
+            ErrorCode.INVALID_AUTHORIZATION_TIMEOUT; // -16;
+    public static final int KM_ERROR_UNSUPPORTED_KEY_FORMAT =
+            ErrorCode.UNSUPPORTED_KEY_FORMAT; // -17;
+    public static final int KM_ERROR_INCOMPATIBLE_KEY_FORMAT =
+            ErrorCode.INCOMPATIBLE_KEY_FORMAT; // -18;
+    public static final int KM_ERROR_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM =
+            ErrorCode.UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM; // -19;
+    public static final int KM_ERROR_UNSUPPORTED_KEY_VERIFICATION_ALGORITHM =
+            ErrorCode.UNSUPPORTED_KEY_VERIFICATION_ALGORITHM; // -20;
+    public static final int KM_ERROR_INVALID_INPUT_LENGTH =
+            ErrorCode.INVALID_INPUT_LENGTH; // -21;
+    public static final int KM_ERROR_KEY_EXPORT_OPTIONS_INVALID =
+            ErrorCode.KEY_EXPORT_OPTIONS_INVALID; // -22;
+    public static final int KM_ERROR_DELEGATION_NOT_ALLOWED =
+            ErrorCode.DELEGATION_NOT_ALLOWED; // -23;
+    public static final int KM_ERROR_KEY_NOT_YET_VALID =
+            ErrorCode.KEY_NOT_YET_VALID; // -24;
+    public static final int KM_ERROR_KEY_EXPIRED =
+            ErrorCode.KEY_EXPIRED; // -25;
+    public static final int KM_ERROR_KEY_USER_NOT_AUTHENTICATED =
+            ErrorCode.KEY_USER_NOT_AUTHENTICATED; // -26;
+    public static final int KM_ERROR_OUTPUT_PARAMETER_NULL =
+            ErrorCode.OUTPUT_PARAMETER_NULL; // -27;
+    public static final int KM_ERROR_INVALID_OPERATION_HANDLE =
+            ErrorCode.INVALID_OPERATION_HANDLE; // -28;
+    public static final int KM_ERROR_INSUFFICIENT_BUFFER_SPACE =
+            ErrorCode.INSUFFICIENT_BUFFER_SPACE; // -29;
+    public static final int KM_ERROR_VERIFICATION_FAILED =
+            ErrorCode.VERIFICATION_FAILED; // -30;
+    public static final int KM_ERROR_TOO_MANY_OPERATIONS =
+            ErrorCode.TOO_MANY_OPERATIONS; // -31;
+    public static final int KM_ERROR_UNEXPECTED_NULL_POINTER =
+            ErrorCode.UNEXPECTED_NULL_POINTER; // -32;
+    public static final int KM_ERROR_INVALID_KEY_BLOB =
+            ErrorCode.INVALID_KEY_BLOB; // -33;
+    public static final int KM_ERROR_IMPORTED_KEY_NOT_ENCRYPTED =
+            ErrorCode.IMPORTED_KEY_NOT_ENCRYPTED; // -34;
+    public static final int KM_ERROR_IMPORTED_KEY_DECRYPTION_FAILED =
+            ErrorCode.IMPORTED_KEY_DECRYPTION_FAILED; // -35;
+    public static final int KM_ERROR_IMPORTED_KEY_NOT_SIGNED =
+            ErrorCode.IMPORTED_KEY_NOT_SIGNED; // -36;
+    public static final int KM_ERROR_IMPORTED_KEY_VERIFICATION_FAILED =
+            ErrorCode.IMPORTED_KEY_VERIFICATION_FAILED; // -37;
+    public static final int KM_ERROR_INVALID_ARGUMENT =
+            ErrorCode.INVALID_ARGUMENT; // -38;
+    public static final int KM_ERROR_UNSUPPORTED_TAG =
+            ErrorCode.UNSUPPORTED_TAG; // -39;
+    public static final int KM_ERROR_INVALID_TAG =
+            ErrorCode.INVALID_TAG; // -40;
+    public static final int KM_ERROR_MEMORY_ALLOCATION_FAILED =
+            ErrorCode.MEMORY_ALLOCATION_FAILED; // -41;
+    public static final int KM_ERROR_IMPORT_PARAMETER_MISMATCH =
+            ErrorCode.IMPORT_PARAMETER_MISMATCH; // -44;
+    public static final int KM_ERROR_SECURE_HW_ACCESS_DENIED =
+            ErrorCode.SECURE_HW_ACCESS_DENIED; // -45;
+    public static final int KM_ERROR_OPERATION_CANCELLED =
+            ErrorCode.OPERATION_CANCELLED; // -46;
+    public static final int KM_ERROR_CONCURRENT_ACCESS_CONFLICT =
+            ErrorCode.CONCURRENT_ACCESS_CONFLICT; // -47;
+    public static final int KM_ERROR_SECURE_HW_BUSY =
+            ErrorCode.SECURE_HW_BUSY; // -48;
+    public static final int KM_ERROR_SECURE_HW_COMMUNICATION_FAILED =
+            ErrorCode.SECURE_HW_COMMUNICATION_FAILED; // -49;
+    public static final int KM_ERROR_UNSUPPORTED_EC_FIELD =
+            ErrorCode.UNSUPPORTED_EC_FIELD; // -50;
+    public static final int KM_ERROR_MISSING_NONCE =
+            ErrorCode.MISSING_NONCE; // -51;
+    public static final int KM_ERROR_INVALID_NONCE =
+            ErrorCode.INVALID_NONCE; // -52;
+    public static final int KM_ERROR_MISSING_MAC_LENGTH =
+            ErrorCode.MISSING_MAC_LENGTH; // -53;
+    public static final int KM_ERROR_KEY_RATE_LIMIT_EXCEEDED =
+            ErrorCode.KEY_RATE_LIMIT_EXCEEDED; // -54;
+    public static final int KM_ERROR_CALLER_NONCE_PROHIBITED =
+            ErrorCode.CALLER_NONCE_PROHIBITED; // -55;
+    public static final int KM_ERROR_KEY_MAX_OPS_EXCEEDED =
+            ErrorCode.KEY_MAX_OPS_EXCEEDED; // -56;
+    public static final int KM_ERROR_INVALID_MAC_LENGTH =
+            ErrorCode.INVALID_MAC_LENGTH; // -57;
+    public static final int KM_ERROR_MISSING_MIN_MAC_LENGTH =
+            ErrorCode.MISSING_MIN_MAC_LENGTH; // -58;
+    public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH =
+            ErrorCode.UNSUPPORTED_MIN_MAC_LENGTH; // -59;
+    public static final int KM_ERROR_CANNOT_ATTEST_IDS =
+            ErrorCode.CANNOT_ATTEST_IDS; // -66;
+    public static final int KM_ERROR_HARDWARE_TYPE_UNAVAILABLE =
+            ErrorCode.HARDWARE_TYPE_UNAVAILABLE; // -68;
+    public static final int KM_ERROR_DEVICE_LOCKED =
+            ErrorCode.DEVICE_LOCKED; // -72;
+    public static final int KM_ERROR_UNIMPLEMENTED =
+            ErrorCode.UNIMPLEMENTED; // -100;
+    public static final int KM_ERROR_VERSION_MISMATCH =
+            ErrorCode.VERSION_MISMATCH; // -101;
+    public static final int KM_ERROR_UNKNOWN_ERROR =
+            ErrorCode.UNKNOWN_ERROR; // -1000;
 
     public static final Map<Integer, String> sErrorCodeToString = new HashMap<Integer, String>();
     static {
diff --git a/core/java/android/service/autofill/TEST_MAPPING b/core/java/android/service/autofill/TEST_MAPPING
new file mode 100644
index 0000000..87a17eb
--- /dev/null
+++ b/core/java/android/service/autofill/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "imports": [
+    {
+      "path": "frameworks/base/services/autofill/java/com/android/server/autofill"
+    }
+  ]
+}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 9a76f19..3763711 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -199,6 +199,7 @@
         final InsetsSourceControl[] mTempControls = new InsetsSourceControl[0];
         final MergedConfiguration mMergedConfiguration = new MergedConfiguration();
         private final Point mSurfaceSize = new Point();
+        private final Point mLastSurfaceSize = new Point();
         private final Matrix mTmpMatrix = new Matrix();
         private final float[] mTmpValues = new float[9];
 
@@ -908,6 +909,14 @@
                     if (mSurfaceControl.isValid()) {
                         mSurfaceHolder.mSurface.copyFrom(mSurfaceControl);
                     }
+                    if (!mLastSurfaceSize.equals(mSurfaceSize)) {
+                        mLastSurfaceSize.set(mSurfaceSize.x, mSurfaceSize.y);
+                        if (mSurfaceControl != null && mSurfaceControl.isValid()) {
+                            SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+                            t.setBufferSize(mSurfaceControl, mSurfaceSize.x, mSurfaceSize.y);
+                            t.apply();
+                        }
+                    }
 
                     if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
                             + ", frame=" + mWinFrames);
diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java
index 6d5e830..ece6b35 100644
--- a/core/java/android/util/DebugUtils.java
+++ b/core/java/android/util/DebugUtils.java
@@ -271,6 +271,26 @@
         return res.toString();
     }
 
+    /**
+     * Gets human-readable representation of constants (static final values).
+     *
+     * @hide
+     */
+    public static String constantToString(Class<?> clazz, String prefix, int value) {
+        for (Field field : clazz.getDeclaredFields()) {
+            final int modifiers = field.getModifiers();
+            try {
+                if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)
+                        && field.getType().equals(int.class) && field.getName().startsWith(prefix)
+                        && field.getInt(null) == value) {
+                    return constNameWithoutPrefix(prefix, field);
+                }
+            } catch (IllegalAccessException ignored) {
+            }
+        }
+        return prefix + Integer.toString(value);
+    }
+
     private static String constNameWithoutPrefix(String prefix, Field field) {
         return field.getName().substring(prefix.length());
     }
diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java
index defa58e..c8bfd36 100644
--- a/core/java/android/view/Gravity.java
+++ b/core/java/android/view/Gravity.java
@@ -15,8 +15,13 @@
  */
 
 package android.view;
+
+import android.annotation.IntDef;
 import android.graphics.Rect;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Standard constants and tools for placing an object within a potentially
  * larger container.
@@ -122,6 +127,32 @@
      */
     public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = START | END;
 
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, value = {
+        Gravity.FILL,
+        Gravity.FILL_HORIZONTAL,
+        Gravity.FILL_VERTICAL,
+        Gravity.START,
+        Gravity.END,
+        Gravity.LEFT,
+        Gravity.RIGHT,
+        Gravity.TOP,
+        Gravity.BOTTOM,
+        Gravity.CENTER,
+        Gravity.CENTER_HORIZONTAL,
+        Gravity.CENTER_VERTICAL,
+        Gravity.DISPLAY_CLIP_HORIZONTAL,
+        Gravity.DISPLAY_CLIP_VERTICAL,
+        Gravity.CLIP_HORIZONTAL,
+        Gravity.CLIP_VERTICAL,
+        Gravity.NO_GRAVITY
+    })
+    public @interface GravityFlags {}
+
     /**
      * Apply a gravity constant to an object. This supposes that the layout direction is LTR.
      * 
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 1a6eea5..a23b7e1 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -32,6 +32,7 @@
 import android.os.Bundle;
 import android.os.IRemoteCallback;
 import android.os.ParcelFileDescriptor;
+import android.service.attestation.ImpressionToken;
 import android.view.DisplayCutout;
 import android.view.IApplicationToken;
 import android.view.IAppTransitionAnimationSpecsFuture;
@@ -760,4 +761,23 @@
      * {@link android.content.pm.PackageManager#getHoldLockToken()}.
      */
     void holdLock(in IBinder token, in int durationMs);
+
+    /**
+     * Gets an array of support hashing algorithms that can be used to generate the hash of the
+     * screenshot. The String value of one algorithm should be used when requesting to generate
+     * the impression attestation token.
+     *
+     * @return a String array of supported hashing algorithms.
+     */
+    String[] getSupportedImpressionAlgorithms();
+
+    /**
+     * Validate the impression token was generated by the system. The impression token passed in
+     * should be the token generated when calling {@link IWindowSession#generateImpressionToken}
+     *
+     * @param impressionToken The token to verify that it was generated by the system.
+     * @return true if the token was generated by the system or false if the token cannot be
+     *         verified.
+     */
+    boolean verifyImpressionToken(in ImpressionToken impressionToken);
 }
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 0089a85..cfdaf8c 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -22,6 +22,7 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Bundle;
+import android.service.attestation.ImpressionToken;
 import android.util.MergedConfiguration;
 import android.view.DisplayCutout;
 import android.view.InputChannel;
@@ -344,4 +345,16 @@
      *                     window, the system will try to find a new focus target.
      */
     void grantEmbeddedWindowFocus(IWindow window, in IBinder inputToken, boolean grantFocus);
+
+    /**
+     * Generates an impression token that can be used to validate whether specific content was on
+     * screen.
+     *
+     * @param window The token for the window where the view to attest is shown.
+     * @param boundsInWindow The size and position of the ads view in the window
+     * @param hashAlgorithm The String for the hashing algorithm to use based on values returned
+     *                      from {@link IWindowManager#getSupportedImpressionAlgorithms()}
+     */
+    ImpressionToken generateImpressionToken(IWindow window, in Rect boundsInWindow,
+            in String hashAlgorithm);
 }
diff --git a/core/java/android/view/TEST_MAPPING b/core/java/android/view/TEST_MAPPING
index 4ea4310..c8b746f 100644
--- a/core/java/android/view/TEST_MAPPING
+++ b/core/java/android/view/TEST_MAPPING
@@ -2,6 +2,24 @@
   "presubmit": [
     {
       "name": "CtsAccelerationTestCases"
+    },
+    {
+      "name": "CtsOsTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        },
+        {
+          "include-filter": "android.os.cts.StrictModeTest"
+        }
+      ],
+      "file_patterns": ["(/|^)ViewConfiguration.java", "(/|^)GestureDetector.java"]
     }
   ],
   "imports": [
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index ef2153e..b9afbc9 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -104,6 +104,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
+import android.view.Gravity.GravityFlags;
 import android.view.View.OnApplyWindowInsetsListener;
 import android.view.WindowInsets.Side;
 import android.view.WindowInsets.Side.InsetsSide;
@@ -2572,6 +2573,7 @@
          *
          * @see Gravity
          */
+        @GravityFlags
         public int gravity;
 
         /**
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 0ed7ca7..673073e 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -24,6 +24,7 @@
 import android.graphics.Region;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.service.attestation.ImpressionToken;
 import android.util.Log;
 import android.util.MergedConfiguration;
 import android.window.ClientWindowFrames;
@@ -460,4 +461,10 @@
     public void grantEmbeddedWindowFocus(IWindow callingWindow, IBinder targetInputToken,
                                          boolean grantFocus) {
     }
+
+    @Override
+    public ImpressionToken generateImpressionToken(IWindow window, Rect boundsInWindow,
+            String hashAlgorithm) {
+        return null;
+    }
 }
diff --git a/core/java/android/view/autofill/TEST_MAPPING b/core/java/android/view/autofill/TEST_MAPPING
new file mode 100644
index 0000000..87a17eb
--- /dev/null
+++ b/core/java/android/view/autofill/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "imports": [
+    {
+      "path": "frameworks/base/services/autofill/java/com/android/server/autofill"
+    }
+  ]
+}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index c3d3985..8d2c2d9 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -88,8 +88,10 @@
 import android.view.autofill.AutofillManager;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.Completable;
 import com.android.internal.inputmethod.InputMethodDebug;
 import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
+import com.android.internal.inputmethod.ResultCallbacks;
 import com.android.internal.inputmethod.StartInputFlags;
 import com.android.internal.inputmethod.StartInputReason;
 import com.android.internal.inputmethod.UnbindReason;
@@ -666,6 +668,7 @@
                     final int startInputReason =
                             nextFocusHasConnection ? WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION
                                     : WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION;
+                    final Completable.InputBindResult value = Completable.createInputBindResult();
                     mService.startInputOrWindowGainedFocus(
                             startInputReason, mClient,
                             focusedView.getWindowToken(), startInputFlags, softInputMode,
@@ -673,7 +676,9 @@
                             null,
                             null,
                             0 /* missingMethodFlags */,
-                            mCurRootView.mContext.getApplicationInfo().targetSdkVersion);
+                            mCurRootView.mContext.getApplicationInfo().targetSdkVersion,
+                            ResultCallbacks.of(value));
+                    Completable.getResult(value); // ignore the result
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 }
@@ -2039,10 +2044,13 @@
                 if (DEBUG) Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic="
                         + ic + " tba=" + tba + " startInputFlags="
                         + InputMethodDebug.startInputFlagsToString(startInputFlags));
-                res = mService.startInputOrWindowGainedFocus(
+                final Completable.InputBindResult value = Completable.createInputBindResult();
+                mService.startInputOrWindowGainedFocus(
                         startInputReason, mClient, windowGainingFocus, startInputFlags,
                         softInputMode, windowFlags, tba, servedContext, missingMethodFlags,
-                        view.getContext().getApplicationInfo().targetSdkVersion);
+                        view.getContext().getApplicationInfo().targetSdkVersion,
+                        ResultCallbacks.of(value));
+                res = Completable.getResult(value);
                 if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
                 if (res == null) {
                     Log.wtf(TAG, "startInputOrWindowGainedFocus must not return"
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index 14abbdb..9c63d56 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -71,7 +71,8 @@
     // TODO: remove this
     private static final String EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME =
             "UntranslatableReplacementStringInSubtypeName";
-    private static final int SUBTYPE_ID_NONE = 0;
+    /** {@hide} */
+    public static final int SUBTYPE_ID_NONE = 0;
 
     private final boolean mIsAuxiliary;
     private final boolean mOverridesImplicitlyEnabledSubtype;
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index dde9c30..b91e7d3 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -251,15 +251,8 @@
             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
             try {
                 Class<WebViewFactoryProvider> providerClass = getProviderClass();
-                Method staticFactory = null;
-                try {
-                    staticFactory = providerClass.getMethod(
+                Method staticFactory = providerClass.getMethod(
                         CHROMIUM_WEBVIEW_FACTORY_METHOD, WebViewDelegate.class);
-                } catch (Exception e) {
-                    if (DEBUG) {
-                        Log.w(LOGTAG, "error instantiating provider with static factory method", e);
-                    }
-                }
 
                 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactoryProvider invocation");
                 try {
@@ -267,12 +260,12 @@
                             staticFactory.invoke(null, new WebViewDelegate());
                     if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance);
                     return sProviderInstance;
-                } catch (Exception e) {
-                    Log.e(LOGTAG, "error instantiating provider", e);
-                    throw new AndroidRuntimeException(e);
                 } finally {
                     Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
                 }
+            } catch (Exception e) {
+                Log.e(LOGTAG, "error instantiating provider", e);
+                throw new AndroidRuntimeException(e);
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
             }
diff --git a/core/java/com/android/internal/inputmethod/CallbackUtils.java b/core/java/com/android/internal/inputmethod/CallbackUtils.java
new file mode 100644
index 0000000..ec67792
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/CallbackUtils.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.os.RemoteException;
+
+import com.android.internal.view.InputBindResult;
+
+import java.util.function.Supplier;
+
+/**
+ * Defines a set of helper methods to callback corresponding results in {@link ResultCallbacks}.
+ */
+public final class CallbackUtils {
+
+    /**
+     * Not intended to be instantiated.
+     */
+    private CallbackUtils() {
+    }
+
+    /**
+     * A utility method using given {@link IInputBindResultResultCallback} to callback the
+     * {@link InputBindResult}.
+     *
+     * @param callback {@link IInputBindResultResultCallback} to be called back.
+     * @param resultSupplier the supplier from which {@link InputBindResult} is provided.
+     */
+    @AnyThread
+    public static void onResult(@NonNull IInputBindResultResultCallback callback,
+            @NonNull Supplier<InputBindResult> resultSupplier) {
+        try {
+            callback.onResult(resultSupplier.get());
+        } catch (RemoteException ignored) { }
+    }
+}
diff --git a/core/java/com/android/internal/inputmethod/Completable.java b/core/java/com/android/internal/inputmethod/Completable.java
index d8d1a7d..b9e1cf0 100644
--- a/core/java/com/android/internal/inputmethod/Completable.java
+++ b/core/java/com/android/internal/inputmethod/Completable.java
@@ -124,6 +124,16 @@
                 return true;
             }
         }
+
+        /**
+         * Blocks the calling thread until this object becomes ready to return the value.
+         */
+        @AnyThread
+        public void await() {
+            try {
+                mLatch.await();
+            } catch (InterruptedException ignored) { }
+        }
     }
 
     /**
@@ -250,6 +260,13 @@
     }
 
     /**
+     * @return an instance of {@link Completable.InputBindResult}.
+     */
+    public static Completable.InputBindResult createInputBindResult() {
+        return new Completable.InputBindResult();
+    }
+
+    /**
      * Completable object of {@link java.lang.Boolean}.
      */
     public static final class Boolean extends Values<java.lang.Boolean> { }
@@ -278,6 +295,18 @@
             extends Values<com.android.internal.view.InputBindResult> { }
 
     /**
+     * Await the result by the {@link Completable.Values}.
+     *
+     * @return the result once {@link ValueBase#onComplete()}
+     */
+    @AnyThread
+    @Nullable
+    public static <T> T getResult(@NonNull Completable.Values<T> value) {
+        value.await();
+        return value.getValue();
+    }
+
+    /**
      * Await the result by the {@link Completable.Int}, and log it if there is no result after
      * given timeout.
      *
diff --git a/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl b/core/java/com/android/internal/inputmethod/IInputBindResultResultCallback.aidl
similarity index 69%
copy from services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl
copy to core/java/com/android/internal/inputmethod/IInputBindResultResultCallback.aidl
index 45e4c69..b52b3b1 100644
--- a/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputBindResultResultCallback.aidl
@@ -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.
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package com.android.internal.inputmethod;
 
-/** @hide */
-parcelable WifiApiServiceInfo {
-    String name;
-    IBinder binder;
-}
+import com.android.internal.view.InputBindResult;
+
+oneway interface IInputBindResultResultCallback {
+    void onResult(in InputBindResult result);
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/ResultCallbacks.java b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
index 7131284..c59dcf4 100644
--- a/core/java/com/android/internal/inputmethod/ResultCallbacks.java
+++ b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
@@ -21,6 +21,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
+import com.android.internal.view.InputBindResult;
+
 import java.lang.ref.WeakReference;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -154,4 +156,31 @@
             }
         };
     }
+
+    /**
+     * Creates {@link IInputBindResultResultCallback.Stub} that is to set
+     * {@link Completable.InputBindResult} when receiving the result.
+     *
+     * @param value {@link Completable.InputBindResult} to be set when receiving the result.
+     * @return {@link IInputBindResultResultCallback.Stub} that can be passed as a binder IPC
+     *         parameter.
+     */
+    @AnyThread
+    public static IInputBindResultResultCallback.Stub of(
+            @NonNull Completable.InputBindResult value) {
+        final AtomicReference<WeakReference<Completable.InputBindResult>>
+                atomicRef = new AtomicReference<>(new WeakReference<>(value));
+
+        return new IInputBindResultResultCallback.Stub() {
+            @BinderThread
+            @Override
+            public void onResult(InputBindResult result) {
+                final Completable.InputBindResult value = unwrap(atomicRef);
+                if (value == null) {
+                    return;
+                }
+                value.onComplete(result);
+            }
+        };
+    }
 }
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
index ffc7f05..1553e2e 100644
--- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -48,7 +48,8 @@
         SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED,
         SoftInputShowHideReason.HIDE_RECENTS_ANIMATION,
         SoftInputShowHideReason.HIDE_BUBBLES,
-        SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR})
+        SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR,
+        SoftInputShowHideReason.HIDE_REMOVE_CLIENT})
 public @interface SoftInputShowHideReason {
     /** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */
     int SHOW_SOFT_INPUT = 0;
@@ -161,4 +162,9 @@
      * soft-input when the same window focused again to align with the same behavior prior to R.
      */
     int HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR = 20;
+
+    /**
+     * Hide soft input when a {@link com.android.internal.view.IInputMethodClient} is removed.
+     */
+    int HIDE_REMOVE_CLIENT = 21;
 }
diff --git a/core/java/com/android/internal/util/FastXmlSerializer.java b/core/java/com/android/internal/util/FastXmlSerializer.java
index 94e07a8..929c9e8 100644
--- a/core/java/com/android/internal/util/FastXmlSerializer.java
+++ b/core/java/com/android/internal/util/FastXmlSerializer.java
@@ -367,8 +367,11 @@
 
     public void startDocument(String encoding, Boolean standalone) throws IOException,
             IllegalArgumentException, IllegalStateException {
-        append("<?xml version='1.0' encoding='utf-8' standalone='"
-                + (standalone ? "yes" : "no") + "' ?>\n");
+        append("<?xml version='1.0' encoding='utf-8'");
+        if (standalone != null) {
+            append(" standalone='" + (standalone ? "yes" : "no") + "'");
+        }
+        append(" ?>\n");
         mLineStart = true;
     }
 
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 33abbe8..e78ed4e 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -24,6 +24,7 @@
 import com.android.internal.view.InputBindResult;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
+import com.android.internal.inputmethod.IInputBindResultResultCallback;
 
 /**
  * Public interface to the global input method manager, used by all client
@@ -48,14 +49,15 @@
     // If windowToken is null, this just does startInput().  Otherwise this reports that a window
     // has gained focus, and if 'attribute' is non-null then also does startInput.
     // @NonNull
-    InputBindResult startInputOrWindowGainedFocus(
+    void startInputOrWindowGainedFocus(
             /* @StartInputReason */ int startInputReason,
             in IInputMethodClient client, in IBinder windowToken,
             /* @StartInputFlags */ int startInputFlags,
             /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
             int windowFlags, in EditorInfo attribute, IInputContext inputContext,
             /* @InputConnectionInspector.MissingMethodFlags */ int missingMethodFlags,
-            int unverifiedTargetSdkVersion);
+            int unverifiedTargetSdkVersion,
+            in IInputBindResultResultCallback inputBindResult);
 
     void showInputMethodPickerFromClient(in IInputMethodClient client,
             int auxiliarySubtypeMode);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index b562ef8..9712b4e 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -311,6 +311,15 @@
         return getDevicePolicyManager().getPasswordMinimumMetrics(userId);
     }
 
+    /**
+     * Returns the effective complexity for the user.
+     * @param userId  The user to return the complexity for.
+     * @return complexity level for the user.
+     */
+    public @DevicePolicyManager.PasswordComplexity int getRequestedPasswordComplexity(int userId) {
+        return getDevicePolicyManager().getAggregatedPasswordComplexityForUser(userId);
+    }
+
     public int getRequestedPasswordQuality(int userId) {
         return getDevicePolicyManager().getPasswordQuality(null, userId);
     }
diff --git a/core/proto/android/view/windowlayoutparams.proto b/core/proto/android/view/windowlayoutparams.proto
index 4bb56f8..062485d 100644
--- a/core/proto/android/view/windowlayoutparams.proto
+++ b/core/proto/android/view/windowlayoutparams.proto
@@ -35,7 +35,7 @@
     optional int32 height = 5;
     optional float horizontal_margin = 6;
     optional float vertical_margin = 7;
-    optional int32 gravity = 8;
+    optional int32 gravity = 8 [(.android.typedef) = "android.view.Gravity.GravityFlags"];
     optional int32 soft_input_mode = 9 [(.android.typedef) = "android.view.WindowManager.LayoutParams.SoftInputModeFlags"];
     optional .android.graphics.PixelFormatProto.Format format = 10;
     optional int32 window_animations = 11;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index db80287..60dd0eb 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -120,6 +120,12 @@
     <protected-broadcast android:name="android.app.action.EXIT_DESK_MODE" />
     <protected-broadcast android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" />
 
+    <protected-broadcast android:name="android.app.action.USER_ADDED" />
+    <protected-broadcast android:name="android.app.action.USER_REMOVED" />
+    <protected-broadcast android:name="android.app.action.USER_STARTED" />
+    <protected-broadcast android:name="android.app.action.USER_STOPPED" />
+    <protected-broadcast android:name="android.app.action.USER_SWITCHED" />
+
     <protected-broadcast android:name="android.app.action.BUGREPORT_SHARING_DECLINED" />
     <protected-broadcast android:name="android.app.action.BUGREPORT_FAILED" />
     <protected-broadcast android:name="android.app.action.BUGREPORT_SHARE" />
@@ -1809,6 +1815,16 @@
     <permission android:name="android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi @hide Allows system APK to update Wifi/Cellular coex channels to avoid.
+             <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi @hide Allows applications to access Wifi/Cellular coex channels being avoided.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS"
+                android:protectionLevel="signature|privileged" />
+
     <!-- ======================================= -->
     <!-- Permissions for short range, peripheral networks -->
     <!-- ======================================= -->
@@ -3104,6 +3120,12 @@
     <permission android:name="android.permission.DUMP"
         android:protectionLevel="signature|privileged|development" />
 
+    <!-- Allows an application to start tracing for InputMethod and WindowManager.
+    <p>Not for use by third-party applications.
+    @hide -->
+    <permission android:name="android.permission.CONTROL_UI_TRACING"
+        android:protectionLevel="signature|privileged|development" />
+
     <!-- Allows an application to read the low-level system log files.
     <p>Not for use by third-party applications, because
     Log entries can contain the user's private information. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b881fe6..f8cbfeb 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4239,6 +4239,35 @@
          If non-positive, then the refresh rate is unchanged even if thresholds are configured. -->
     <integer name="config_defaultRefreshRateInZone">0</integer>
 
+    <!-- The display uses different gamma curves for different refresh rates. It's hard for panel
+         vendor to tune the curves to have exact same brightness for different refresh rate. So
+         flicker could be observed at switch time. The issue can be observed on the screen with
+         even full white content at the high brightness. To prevent flickering, we support fixed
+         refresh rates if the display and ambient brightness are equal to or above the provided
+         thresholds. You can define multiple threshold levels as higher brightness environments
+         may have lower display brightness requirements for the flickering is visible. And the
+         high brightness environment could have higher threshold.
+         For example, fixed refresh rate if
+             display brightness >= disp0 && ambient brightness >= amb0
+             || display brightness >= disp1 && ambient brightness >= amb1 -->
+    <integer-array translatable="false" name="config_highDisplayBrightnessThresholdsOfFixedRefreshRate">
+         <!--
+           <item>disp0</item>
+           <item>disp1</item>
+        -->
+    </integer-array>
+
+    <integer-array translatable="false" name="config_highAmbientBrightnessThresholdsOfFixedRefreshRate">
+         <!--
+           <item>amb0</item>
+           <item>amb1</item>
+        -->
+    </integer-array>
+
+    <!-- Default refresh rate in the high zone defined by brightness and ambient thresholds.
+         If non-positive, then the refresh rate is unchanged even if thresholds are configured. -->
+    <integer name="config_fixedRefreshRateInHighZone">0</integer>
+
     <!-- The type of the light sensor to be used by the display framework for things like
          auto-brightness. If unset, then it just gets the default sensor of type TYPE_LIGHT. -->
     <string name="config_displayLightSensorType" translatable="false" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6da7eba..84556d4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3813,6 +3813,11 @@
   <java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" />
   <java-symbol type="array" name="config_ambientThresholdsOfPeakRefreshRate" />
 
+  <!-- For fixed refresh rate displays in high brightness-->
+  <java-symbol type="integer" name="config_fixedRefreshRateInHighZone" />
+  <java-symbol type="array" name="config_highDisplayBrightnessThresholdsOfFixedRefreshRate" />
+  <java-symbol type="array" name="config_highAmbientBrightnessThresholdsOfFixedRefreshRate" />
+
   <!-- For Auto-Brightness -->
   <java-symbol type="string" name="config_displayLightSensorType" />
 
diff --git a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java
index 27584a5..7552ec4 100644
--- a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java
+++ b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java
@@ -21,11 +21,18 @@
 import static junit.framework.Assert.assertFalse;
 
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
 
 import android.app.Notification;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutInfo;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.UserHandle;
@@ -38,26 +45,37 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class PeopleSpaceTileTest {
 
     private Context mContext;
+    private final Drawable mDrawable = new ColorDrawable(Color.BLUE);
+    private final Icon mIcon = PeopleSpaceTile.convertDrawableToIcon(mDrawable);
+
+    @Mock
+    private LauncherApps mLauncherApps;
 
     @Before
     public void setUp() {
         mContext = InstrumentationRegistry.getContext();
+        MockitoAnnotations.initMocks(this);
+        when(mLauncherApps.getShortcutIconDrawable(any(), eq(0))).thenReturn(mDrawable);
     }
 
     @Test
     public void testId() {
         PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
-                new ShortcutInfo.Builder(mContext, "123").build()).build();
+                new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build();
         assertThat(tile.getId()).isEqualTo("123");
 
-        tile = new PeopleSpaceTile.Builder(new ShortcutInfo.Builder(mContext, "123").build()).setId(
-                "5").build();
+        tile = new PeopleSpaceTile
+                .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps)
+                .setId("5")
+                .build();
         assertThat(tile.getId()).isEqualTo("5");
 
         tile = new PeopleSpaceTile.Builder("12", null, null, null).build();
@@ -67,11 +85,13 @@
     @Test
     public void testUserName() {
         PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
-                new ShortcutInfo.Builder(mContext, "123").build()).build();
+                new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build();
         assertThat(tile.getUserName()).isNull();
 
-        tile = new PeopleSpaceTile.Builder(
-                new ShortcutInfo.Builder(mContext, "123").build()).setUserName("Name 1").build();
+        tile = new PeopleSpaceTile
+                .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps)
+                .setUserName("Name 1")
+                .build();
         assertThat(tile.getUserName()).isEqualTo("Name 1");
 
         tile = new PeopleSpaceTile.Builder(null, "Name 2", null, null).build();
@@ -81,21 +101,19 @@
     @Test
     public void testUserIcon() {
         PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
-                new ShortcutInfo.Builder(mContext, "123").build()).setUserIcon(
-                Icon.createWithResource(mContext, 1)).build();
-        assertThat(tile.getUserIcon().toString()).isEqualTo(
-                Icon.createWithResource(mContext, 1).toString());
+                new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).setUserIcon(
+                mIcon).build();
+        assertThat(tile.getUserIcon().toString()).isEqualTo(mIcon.toString());
 
-        tile = new PeopleSpaceTile.Builder("12", null, Icon.createWithResource(mContext, 2),
+        tile = new PeopleSpaceTile.Builder("12", null, mIcon,
                 null).build();
-        assertThat(tile.getUserIcon().toString()).isEqualTo(
-                Icon.createWithResource(mContext, 2).toString());
+        assertThat(tile.getUserIcon().toString()).isEqualTo(mIcon.toString());
     }
 
     @Test
     public void testContactUri() {
         PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
-                new ShortcutInfo.Builder(mContext, "123").build()).setContactUri(
+                new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).setContactUri(
                 Uri.parse("test")).build();
 
         assertThat(tile.getContactUri()).isEqualTo(Uri.parse("test"));
@@ -103,8 +121,10 @@
 
     @Test
     public void testUid() {
-        PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
-                new ShortcutInfo.Builder(mContext, "123").build()).setUid(42).build();
+        PeopleSpaceTile tile = new PeopleSpaceTile
+                .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps)
+                .setUid(42)
+                .build();
 
         assertThat(tile.getUid()).isEqualTo(42);
     }
@@ -112,12 +132,12 @@
     @Test
     public void testPackageName() {
         PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
-                new ShortcutInfo.Builder(mContext, "123").build()).build();
+                new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build();
         // Automatically added by creating a ShortcutInfo.
         assertThat(tile.getPackageName()).isEqualTo("com.android.frameworks.coretests");
 
         tile = new PeopleSpaceTile.Builder(
-                new ShortcutInfo.Builder(mContext, "123").build()).setPackageName(
+                new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).setPackageName(
                 "package.name").build();
         assertThat(tile.getPackageName()).isEqualTo("package.name");
 
@@ -129,36 +149,39 @@
     @Test
     public void testLastInteractionTimestamp() {
         PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
-                new ShortcutInfo.Builder(mContext, "123").build()).build();
+                new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build();
         assertThat(tile.getLastInteractionTimestamp()).isEqualTo(0L);
 
-        tile = new PeopleSpaceTile.Builder(
-                new ShortcutInfo.Builder(mContext, "123").build()).setLastInteractionTimestamp(
-                7L).build();
+        tile = new PeopleSpaceTile
+                .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps)
+                .setLastInteractionTimestamp(7L)
+                .build();
         assertThat(tile.getLastInteractionTimestamp()).isEqualTo(7L);
     }
 
     @Test
     public void testImportantConversation() {
         PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
-                new ShortcutInfo.Builder(mContext, "123").build()).build();
+                new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build();
         assertFalse(tile.isImportantConversation());
 
-        tile = new PeopleSpaceTile.Builder(
-                new ShortcutInfo.Builder(mContext, "123").build()).setIsImportantConversation(
-                true).build();
+        tile = new PeopleSpaceTile
+                .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps)
+                .setIsImportantConversation(true)
+                .build();
         assertTrue(tile.isImportantConversation());
     }
 
     @Test
     public void testHiddenConversation() {
         PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
-                new ShortcutInfo.Builder(mContext, "123").build()).build();
+                new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build();
         assertFalse(tile.isHiddenConversation());
 
-        tile = new PeopleSpaceTile.Builder(
-                new ShortcutInfo.Builder(mContext, "123").build()).setIsHiddenConversation(
-                true).build();
+        tile = new PeopleSpaceTile
+                .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps)
+                .setIsHiddenConversation(true)
+                .build();
         assertTrue(tile.isHiddenConversation());
     }
 
@@ -168,8 +191,10 @@
         StatusBarNotification sbn = new StatusBarNotification("pkg" /* pkg */, "pkg" /* opPkg */,
                 1 /* id */, "" /* tag */, 0 /* uid */, 0 /* initialPid */, 0 /* score */,
                 notification, UserHandle.CURRENT, 0 /* postTime */);
-        PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
-                new ShortcutInfo.Builder(mContext, "123").build()).setNotification(sbn).build();
+        PeopleSpaceTile tile = new PeopleSpaceTile
+                .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps)
+                .setNotification(sbn)
+                .build();
 
         assertThat(tile.getNotification()).isEqualTo(sbn);
     }
@@ -177,11 +202,13 @@
     @Test
     public void testIntent() {
         PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
-                new ShortcutInfo.Builder(mContext, "123").build()).build();
+                new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build();
         assertThat(tile.getIntent()).isNull();
 
-        tile = new PeopleSpaceTile.Builder(
-                new ShortcutInfo.Builder(mContext, "123").build()).setIntent(new Intent()).build();
+        tile = new PeopleSpaceTile
+                .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps)
+                .setIntent(new Intent())
+                .build();
         assertThat(tile.getIntent().toString()).isEqualTo(new Intent().toString());
 
         tile = new PeopleSpaceTile.Builder("12", null, null, new Intent()).build();
diff --git a/core/tests/hdmitests/Android.bp b/core/tests/hdmitests/Android.bp
index 2194d4b..4755e0e 100644
--- a/core/tests/hdmitests/Android.bp
+++ b/core/tests/hdmitests/Android.bp
@@ -19,6 +19,7 @@
     static_libs: [
         "androidx.test.rules",
         "frameworks-base-testutils",
+        "guava-android-testlib",
         "truth-prebuilt",
     ],
     libs: ["android.test.runner"],
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java
new file mode 100755
index 0000000..4c0de62
--- /dev/null
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.hardware.hdmi;
+
+import androidx.test.filters.SmallTest;
+
+import com.google.common.testing.EqualsTester;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link HdmiDeviceInfo} */
+@RunWith(JUnit4.class)
+@SmallTest
+public class HdmiDeviceInfoTest {
+
+    @Test
+    public void testEquals() {
+        int logicalAddr = 0x00;
+        int phyAddr = 0x1000;
+        int portId = 1;
+        int deviceType = 0;
+        int vendorId = 0x123456;
+        String displayName = "test device";
+        int powerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
+        int deviceId = 3;
+        int adopterId = 2;
+
+        new EqualsTester()
+                .addEqualityGroup(new HdmiDeviceInfo())
+                .addEqualityGroup(
+                        new HdmiDeviceInfo(phyAddr, portId), new HdmiDeviceInfo(phyAddr, portId))
+                .addEqualityGroup(
+                        new HdmiDeviceInfo(phyAddr, portId, adopterId, deviceId),
+                        new HdmiDeviceInfo(phyAddr, portId, adopterId, deviceId))
+                .addEqualityGroup(
+                        new HdmiDeviceInfo(
+                                logicalAddr, phyAddr, portId, deviceType, vendorId, displayName),
+                        new HdmiDeviceInfo(
+                                logicalAddr, phyAddr, portId, deviceType, vendorId, displayName))
+                .addEqualityGroup(
+                        new HdmiDeviceInfo(
+                                logicalAddr,
+                                phyAddr,
+                                portId,
+                                deviceType,
+                                vendorId,
+                                displayName,
+                                powerStatus),
+                        new HdmiDeviceInfo(
+                                logicalAddr,
+                                phyAddr,
+                                portId,
+                                deviceType,
+                                vendorId,
+                                displayName,
+                                powerStatus))
+                .testEquals();
+    }
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 8406fdf..1fb63f0 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -322,6 +322,7 @@
         <permission name="android.permission.DELETE_CACHE_FILES"/>
         <permission name="android.permission.DELETE_PACKAGES"/>
         <permission name="android.permission.DUMP"/>
+        <permission name="android.permission.CONTROL_UI_TRACING"/>
         <permission name="android.permission.ACTIVITY_EMBEDDING"/>
         <permission name="android.permission.FORCE_STOP_PACKAGES"/>
         <permission name="android.permission.GET_APP_OPS_STATS"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index a52eca7..6bcab8a 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -2749,6 +2749,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1105210816": {
+      "message": "Skipping config check in destroyed state %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "1112047265": {
       "message": "finishDrawingWindow: %s mDrawState=%s",
       "level": "DEBUG",
diff --git a/keystore/java/android/security/AppUriAuthenticationPolicy.java b/keystore/java/android/security/AppUriAuthenticationPolicy.java
index 30f5a94..0244ce9 100644
--- a/keystore/java/android/security/AppUriAuthenticationPolicy.java
+++ b/keystore/java/android/security/AppUriAuthenticationPolicy.java
@@ -28,8 +28,10 @@
 
 import java.io.IOException;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * The app-URI authentication policy is set by the credential management app. This policy determines
@@ -223,4 +225,17 @@
         }
     }
 
+    /**
+     * Get the set of aliases found in the policy.
+     *
+     * @hide
+     */
+    public Set<String> getAliases() {
+        Set<String> aliases = new HashSet<>();
+        for (UrisToAliases appsToUris : mAppToUris.values()) {
+            aliases.addAll(appsToUris.getUrisToAliases().values());
+        }
+        return aliases;
+    }
+
 }
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index 97da3cc..add52fa 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -18,6 +18,8 @@
 import android.content.pm.StringParceledListSlice;
 import android.security.keymaster.KeymasterCertificateChain;
 import android.security.keystore.ParcelableKeyGenParameterSpec;
+import android.security.AppUriAuthenticationPolicy;
+import android.net.Uri;
 
 /**
  * Caller is required to ensure that {@link KeyStore#unlock
@@ -46,6 +48,7 @@
     boolean installKeyPair(
         in byte[] privateKey, in byte[] userCert, in byte[] certChain, String alias, int uid);
     boolean removeKeyPair(String alias);
+    boolean containsKeyPair(String alias);
 
     // APIs used by Settings
     boolean deleteCaCertificate(String alias);
@@ -55,6 +58,13 @@
     boolean containsCaAlias(String alias);
     byte[] getEncodedCaCertificate(String alias, boolean includeDeletedSystem);
     List<String> getCaCertificateChainAliases(String rootAlias, boolean includeDeletedSystem);
+    void setCredentialManagementApp(String packageName, in AppUriAuthenticationPolicy policy);
+    void updateCredentialManagementAppPolicy(in AppUriAuthenticationPolicy policy);
+    boolean hasCredentialManagementApp();
+    String getCredentialManagementAppPackageName();
+    AppUriAuthenticationPolicy getCredentialManagementAppPolicy();
+    String getPredefinedAliasForPackageAndUri(String packageName, in Uri uri);
+    void removeCredentialManagementApp();
 
     // APIs used by KeyChainActivity
     void setGrant(int uid, String alias, boolean value);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index f199072..d3032f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -136,13 +136,6 @@
         mAppPairLayout = null;
     }
 
-    void setVisible(boolean visible) {
-        if (mAppPairLayout == null) {
-            return;
-        }
-        mAppPairLayout.setDividerVisibility(visible);
-    }
-
     @Override
     public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
         if (mRootTaskInfo == null || taskInfo.taskId == mRootTaskInfo.taskId) {
@@ -160,32 +153,41 @@
 
         if (mTaskLeash1 == null || mTaskLeash2 == null) return;
 
-        setVisible(true);
+        mAppPairLayout.init();
         final SurfaceControl dividerLeash = mAppPairLayout.getDividerLeash();
         final Rect dividerBounds = mAppPairLayout.getDividerBounds();
 
         // TODO: Is there more we need to do here?
-        mSyncQueue.runInSync(t -> t
-                .setPosition(mTaskLeash1, mTaskInfo1.positionInParent.x,
-                        mTaskInfo1.positionInParent.y)
-                .setPosition(mTaskLeash2, mTaskInfo2.positionInParent.x,
-                        mTaskInfo2.positionInParent.y)
-                .setLayer(dividerLeash, Integer.MAX_VALUE)
-                .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
-                .show(mRootTaskLeash)
-                .show(dividerLeash)
-                .show(mTaskLeash1)
-                .show(mTaskLeash2));
+        mSyncQueue.runInSync(t -> {
+            t.setPosition(mTaskLeash1, mTaskInfo1.positionInParent.x,
+                    mTaskInfo1.positionInParent.y)
+                    .setPosition(mTaskLeash2, mTaskInfo2.positionInParent.x,
+                            mTaskInfo2.positionInParent.y)
+                    .setLayer(dividerLeash, Integer.MAX_VALUE)
+                    .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
+                    .show(mRootTaskLeash)
+                    .show(mTaskLeash1)
+                    .show(mTaskLeash2);
+        });
     }
 
     @Override
     public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
         if (taskInfo.taskId == getRootTaskId()) {
+            if (mRootTaskInfo.isVisible != taskInfo.isVisible) {
+                mSyncQueue.runInSync(t -> {
+                    if (taskInfo.isVisible) {
+                        t.show(mRootTaskLeash);
+                    } else {
+                        t.hide(mRootTaskLeash);
+                    }
+                });
+            }
             mRootTaskInfo = taskInfo;
 
             if (mAppPairLayout != null
                     && mAppPairLayout.updateConfiguration(mRootTaskInfo.configuration)) {
-                // Update bounds when there is root bounds or orientation changed.
+                // Update bounds when root bounds or its orientation changed.
                 final WindowContainerTransaction wct = new WindowContainerTransaction();
                 final SurfaceControl dividerLeash = mAppPairLayout.getDividerLeash();
                 final Rect dividerBounds = mAppPairLayout.getDividerBounds();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java
index f8703f7..8c8655e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.apppairs;
 
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
 import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
@@ -45,21 +44,18 @@
 /**
  * Records and handles layout of a pair of apps.
  */
-// TODO(172704238): add tests
 final class AppPairLayout {
     private static final String DIVIDER_WINDOW_TITLE = "AppPairDivider";
-    private final Context mContext;
-    private final AppPairWindowManager mAppPairWindowManager;
-    private final SurfaceControlViewHost mViewHost;
-
+    private final Display mDisplay;
     private final int mDividerWindowWidth;
     private final int mDividerWindowInsets;
+    private final AppPairWindowManager mAppPairWindowManager;
 
-    private boolean mIsLandscape;
+    private Context mContext;
     private Rect mRootBounds;
     private DIVIDE_POLICY mDividePolicy;
 
-    private DividerView mDividerView;
+    private SurfaceControlViewHost mViewHost;
     private SurfaceControl mDividerLeash;
 
     AppPairLayout(
@@ -68,7 +64,7 @@
             Configuration configuration,
             SurfaceControl rootLeash) {
         mContext = context.createConfigurationContext(configuration);
-        mIsLandscape = isLandscape(configuration);
+        mDisplay = display;
         mRootBounds = configuration.windowConfiguration.getBounds();
         mDividerWindowWidth = mContext.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.docked_stack_divider_thickness);
@@ -76,26 +72,22 @@
                 com.android.internal.R.dimen.docked_stack_divider_insets);
 
         mAppPairWindowManager = new AppPairWindowManager(configuration, rootLeash);
-        mViewHost = new SurfaceControlViewHost(mContext, display, mAppPairWindowManager);
         mDividePolicy = DIVIDE_POLICY.MIDDLE;
-        mDividePolicy.update(mIsLandscape, mRootBounds, mDividerWindowWidth, mDividerWindowInsets);
+        mDividePolicy.update(mRootBounds, mDividerWindowWidth, mDividerWindowInsets);
     }
 
     boolean updateConfiguration(Configuration configuration) {
         mAppPairWindowManager.setConfiguration(configuration);
         final Rect rootBounds = configuration.windowConfiguration.getBounds();
-        final boolean isLandscape = isLandscape(configuration);
-        if (mIsLandscape == isLandscape && isIdenticalBounds(mRootBounds, rootBounds)) {
+        if (isIdenticalBounds(mRootBounds, rootBounds)) {
             return false;
         }
 
-        mIsLandscape = isLandscape;
+        mContext = mContext.createConfigurationContext(configuration);
         mRootBounds = rootBounds;
-        mDividePolicy.update(mIsLandscape, mRootBounds, mDividerWindowWidth, mDividerWindowInsets);
-        mViewHost.relayout(
-                mDividePolicy.mDividerBounds.width(),
-                mDividePolicy.mDividerBounds.height());
-        // TODO(172704238): handle divider bar rotation.
+        mDividePolicy.update(mRootBounds, mDividerWindowWidth, mDividerWindowInsets);
+        release();
+        init();
         return true;
     }
 
@@ -116,22 +108,19 @@
     }
 
     void release() {
-        if (mViewHost == null) return;
+        if (mViewHost == null) {
+            return;
+        }
         mViewHost.release();
+        mDividerLeash = null;
+        mViewHost = null;
     }
 
-    void setDividerVisibility(boolean visible) {
-        if (mDividerView == null) {
-            initDivider();
+    void init() {
+        if (mViewHost == null) {
+            mViewHost = new SurfaceControlViewHost(mContext, mDisplay, mAppPairWindowManager);
         }
-        if (visible) {
-            mDividerView.show();
-        } else {
-            mDividerView.hide();
-        }
-    }
 
-    private void initDivider() {
         final DividerView dividerView = (DividerView) LayoutInflater.from(mContext)
                 .inflate(R.layout.split_divider, null);
 
@@ -147,14 +136,9 @@
         lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
 
         mViewHost.setView(dividerView, lp);
-        mDividerView = dividerView;
         mDividerLeash = mAppPairWindowManager.getSurfaceControl(mViewHost.getWindowToken());
     }
 
-    private static boolean isLandscape(Configuration configuration) {
-        return configuration.orientation == ORIENTATION_LANDSCAPE;
-    }
-
     private static boolean isIdenticalBounds(Rect bounds1, Rect bounds2) {
         return bounds1.left == bounds2.left && bounds1.top == bounds2.top
                 && bounds1.right == bounds2.right && bounds1.bottom == bounds2.bottom;
@@ -167,8 +151,7 @@
     enum DIVIDE_POLICY {
         MIDDLE;
 
-        void update(boolean isLandscape, Rect rootBounds, int dividerWindowWidth,
-                int dividerWindowInsets) {
+        void update(Rect rootBounds, int dividerWindowWidth, int dividerWindowInsets) {
             final int dividerOffset = dividerWindowWidth / 2;
             final int boundsOffset = dividerOffset - dividerWindowInsets;
 
@@ -179,7 +162,7 @@
             switch (this) {
                 case MIDDLE:
                 default:
-                    if (isLandscape) {
+                    if (isLandscape(rootBounds)) {
                         mDividerBounds.left = rootBounds.width() / 2 - dividerOffset;
                         mDividerBounds.right = rootBounds.width() / 2 + dividerOffset;
                         mBounds1.left = rootBounds.width() / 2 + boundsOffset;
@@ -193,6 +176,10 @@
             }
         }
 
+        private boolean isLandscape(Rect bounds) {
+            return bounds.width() > bounds.height();
+        }
+
         Rect mDividerBounds;
         Rect mBounds1;
         Rect mBounds2;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
index af06764..ef3e3e0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
@@ -36,6 +36,4 @@
     void dump(@NonNull PrintWriter pw, String prefix);
     /** Called when the shell organizer has been registered. */
     void onOrganizerRegistered();
-    /** Called when the visibility of the keyguard changes. */
-    void onKeyguardVisibilityChanged(boolean showing);
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
index 925a4f3..f2f0982 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
@@ -16,8 +16,6 @@
 
 package com.android.wm.shell.apppairs;
 
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
 
 import android.app.ActivityManager;
@@ -30,15 +28,13 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.TaskStackListenerCallback;
-import com.android.wm.shell.common.TaskStackListenerImpl;
 
 import java.io.PrintWriter;
 
 /**
  * Class manages app-pairs multitasking mode and implements the main interface {@link AppPairs}.
  */
-public class AppPairsController implements AppPairs, TaskStackListenerCallback {
+public class AppPairsController implements AppPairs {
     private static final String TAG = AppPairsController.class.getSimpleName();
 
     private final ShellTaskOrganizer mTaskOrganizer;
@@ -48,14 +44,12 @@
     // Active app-pairs mapped by root task id key.
     private final SparseArray<AppPair> mActiveAppPairs = new SparseArray<>();
     private final DisplayController mDisplayController;
-    private int mForegroundTaskId = INVALID_TASK_ID;
 
     public AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue,
-                DisplayController displayController, TaskStackListenerImpl taskStackListener) {
+                DisplayController displayController) {
         mTaskOrganizer = organizer;
         mSyncQueue = syncQueue;
         mDisplayController = displayController;
-        taskStackListener.addListener(this);
     }
 
     @Override
@@ -71,27 +65,6 @@
     }
 
     @Override
-    public void onTaskMovedToFront(int taskId) {
-        mForegroundTaskId = INVALID_TASK_ID;
-        for (int i = mActiveAppPairs.size() - 1; i >= 0; --i) {
-            final AppPair candidate = mActiveAppPairs.valueAt(i);
-            final boolean containForegroundTask = candidate.contains(taskId);
-            candidate.setVisible(containForegroundTask);
-            if (containForegroundTask) {
-                mForegroundTaskId = candidate.getRootTaskId();
-            }
-        }
-    }
-
-    @Override
-    public void onKeyguardVisibilityChanged(boolean showing) {
-        if (mForegroundTaskId == INVALID_TASK_ID) {
-            return;
-        }
-        mActiveAppPairs.get(mForegroundTaskId).setVisible(!showing);
-    }
-
-    @Override
     public boolean pair(int taskId1, int taskId2) {
         final ActivityManager.RunningTaskInfo task1 = mTaskOrganizer.getRunningTaskInfo(taskId1);
         final ActivityManager.RunningTaskInfo task2 = mTaskOrganizer.getRunningTaskInfo(taskId2);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java
index 061d3f8..6e87f13 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java
@@ -107,6 +107,7 @@
         transaction.setWindowCrop(leash, crop);
     }
 
+    // TODO(b/173440321): Correct presentation of letterboxed activities in One-handed mode.
     private void resolveTaskPositionAndCrop(
                 ActivityManager.RunningTaskInfo taskInfo,
                 Point positionInParent,
@@ -125,15 +126,18 @@
         final Rect activityBounds = taskInfo.letterboxActivityBounds;
 
         Insets insets = getInsets();
+        Rect displayBoundsWithInsets =
+                new Rect(mWindowManager.getMaximumWindowMetrics().getBounds());
+        displayBoundsWithInsets.inset(insets);
 
         Rect taskBoundsWithInsets = new Rect(taskBounds);
-        applyInsets(taskBoundsWithInsets, insets, taskInfo.parentBounds);
+        taskBoundsWithInsets.intersect(displayBoundsWithInsets);
 
         Rect activityBoundsWithInsets = new Rect(activityBounds);
-        applyInsets(activityBoundsWithInsets, insets, taskInfo.parentBounds);
+        activityBoundsWithInsets.intersect(displayBoundsWithInsets);
 
         Rect parentBoundsWithInsets = new Rect(parentBounds);
-        applyInsets(parentBoundsWithInsets, insets, parentBounds);
+        parentBoundsWithInsets.intersect(displayBoundsWithInsets);
 
         // Crop need to be in the task coordinates.
         crop.set(activityBoundsWithInsets);
@@ -217,10 +221,4 @@
                                 | WindowInsets.Type.displayCutout());
     }
 
-    private void applyInsets(Rect innerBounds, Insets insets, Rect outerBounds) {
-        Rect outerBoundsWithInsets = new Rect(outerBounds);
-        outerBoundsWithInsets.inset(insets);
-        innerBounds.intersect(outerBoundsWithInsets);
-    }
-
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt
index 2fc6944..ced99de 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.apppairs
 
+import android.platform.test.annotations.Presubmit
 import android.os.SystemClock
 import android.util.Log
 import android.view.Surface
@@ -43,6 +44,7 @@
  * Test AppPairs launch.
  * To run this test: `atest WMShellFlickerTests:AppPairsTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index 6c4e658..6b44ce6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.dsl.FlickerBuilder
@@ -36,6 +37,7 @@
  * Test Pip launch.
  * To run this test: `atest WMShellFlickerTests:PipKeyboardTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt
index a0056df..c61a0f1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.splitscreen
 
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.dsl.FlickerBuilder
@@ -38,6 +39,7 @@
  * Test SplitScreen launch.
  * To run this test: `atest WMShellFlickerTests:EnterSplitScreenTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt
index 32e112d..bf92869 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.splitscreen
 
+import android.platform.test.annotations.Presubmit
 import android.util.Rational
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -40,6 +41,7 @@
  * Test exit SplitScreen mode.
  * To run this test: `atest WMShellFlickerTests:ExitSplitScreenTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairLayoutTests.java
new file mode 100644
index 0000000..c9d32c4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairLayoutTests.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 com.android.wm.shell.apppairs;
+
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.view.Display;
+import android.view.SurfaceControl;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for {@link AppPairLayout} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppPairLayoutTests extends ShellTestCase {
+    @Mock SurfaceControl mSurfaceControl;
+    private Display mDisplay;
+    private Configuration mConfiguration;
+    private AppPairLayout mAppPairLayout;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mConfiguration = getConfiguration(false);
+        mDisplay = mContext.getDisplay();
+        mAppPairLayout = new AppPairLayout(mContext, mDisplay, mConfiguration, mSurfaceControl);
+    }
+
+    @After
+    @UiThreadTest
+    public void tearDown() {
+        mAppPairLayout.release();
+    }
+
+    @Test
+    @UiThreadTest
+    public void testUpdateConfiguration() {
+        assertThat(mAppPairLayout.updateConfiguration(getConfiguration(false))).isFalse();
+        assertThat(mAppPairLayout.updateConfiguration(getConfiguration(true))).isTrue();
+    }
+
+    @Test
+    @UiThreadTest
+    public void testInitRelease() {
+        mAppPairLayout.init();
+        assertThat(mAppPairLayout.getDividerLeash()).isNotNull();
+        mAppPairLayout.release();
+        assertThat(mAppPairLayout.getDividerLeash()).isNull();
+    }
+
+    private static Configuration getConfiguration(boolean isLandscape) {
+        final Configuration configuration = new Configuration();
+        configuration.unset();
+        configuration.orientation = isLandscape ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
+        configuration.windowConfiguration.setBounds(
+                new Rect(0, 0, isLandscape ? 2160 : 1080, isLandscape ? 1080 : 2160));
+        return configuration;
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java
index 754f732..f12648a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java
@@ -34,7 +34,6 @@
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.TaskStackListenerImpl;
 
 import org.junit.After;
 import org.junit.Before;
@@ -52,7 +51,6 @@
     @Mock private SyncTransactionQueue mSyncQueue;
     @Mock private ShellTaskOrganizer mTaskOrganizer;
     @Mock private DisplayController mDisplayController;
-    @Mock private TaskStackListenerImpl mTaskStackListener;
 
     @Before
     public void setUp() {
@@ -60,8 +58,7 @@
         mController = new TestAppPairsController(
                 mTaskOrganizer,
                 mSyncQueue,
-                mDisplayController,
-                mTaskStackListener);
+                mDisplayController);
         when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext);
         when(mDisplayController.getDisplay(anyInt())).thenReturn(
                 mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java
index 6d441ab..f8c68d2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java
@@ -34,7 +34,6 @@
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.TaskStackListenerImpl;
 
 import org.junit.After;
 import org.junit.Before;
@@ -52,7 +51,6 @@
     @Mock private SyncTransactionQueue mSyncQueue;
     @Mock private ShellTaskOrganizer mTaskOrganizer;
     @Mock private DisplayController mDisplayController;
-    @Mock private TaskStackListenerImpl mTaskStackListener;
 
     @Before
     public void setUp() {
@@ -60,8 +58,7 @@
         mController = new TestAppPairsController(
                 mTaskOrganizer,
                 mSyncQueue,
-                mDisplayController,
-                mTaskStackListener);
+                mDisplayController);
         mPool = mController.getPool();
         when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext);
         when(mDisplayController.getDisplay(anyInt())).thenReturn(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java
index d3dbbfe..8ece913 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java
@@ -24,7 +24,6 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.TaskStackListenerImpl;
 
 import org.junit.After;
 import org.junit.Before;
@@ -42,7 +41,6 @@
     @Mock private SyncTransactionQueue mSyncQueue;
     @Mock private ShellTaskOrganizer mTaskOrganizer;
     @Mock private DisplayController mDisplayController;
-    @Mock private TaskStackListenerImpl mTaskStackListener;
 
     @Before
     public void setUp() {
@@ -50,8 +48,7 @@
         mController = new TestAppPairsController(
                 mTaskOrganizer,
                 mSyncQueue,
-                mDisplayController,
-                mTaskStackListener);
+                mDisplayController);
         mPool = mController.getPool();
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java
index e61cc91..be09636 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java
@@ -19,14 +19,13 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.TaskStackListenerImpl;
 
 public class TestAppPairsController extends AppPairsController {
     TestAppPairsPool mPool;
 
     public TestAppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue,
-            DisplayController displayController, TaskStackListenerImpl taskStackListener) {
-        super(organizer, syncQueue, displayController, taskStackListener);
+            DisplayController displayController) {
+        super(organizer, syncQueue, displayController);
         mPool = new TestAppPairsPool(this);
         setPairsPool(mPool);
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java
index 0f719af..fc0e20b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java
@@ -96,6 +96,7 @@
         mLetterboxTaskListener.onTaskAppeared(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds
                         /* parentBounds */ new Rect(0, 0, 200, 100),
                         /* activityBounds */ new Rect(75, 0, 125, 75),
                         /* taskBounds */ new Rect(50, 0, 125, 100)),
@@ -109,6 +110,7 @@
         mLetterboxTaskListener.onTaskInfoChanged(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds
                         /* parentBounds */ new Rect(0, 0, 200, 100),
                         // Activity is offset by 25 to the left
                         /* activityBounds */ new Rect(50, 0, 100, 75),
@@ -130,6 +132,7 @@
         mLetterboxTaskListener.onTaskAppeared(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds
                         /* parentBounds */ new Rect(0, 0, 200, 100),
                         /* activityBounds */ new Rect(150, 0, 200, 75),
                         /* taskBounds */ new Rect(125, 0, 200, 100)),
@@ -150,6 +153,7 @@
         mLetterboxTaskListener.onTaskAppeared(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds
                         /* parentBounds */ new Rect(0, 0, 200, 100),
                         /* activityBounds */ new Rect(150, 0, 200, 75),
                         /* taskBounds */ new Rect(125, 0, 200, 100)),
@@ -170,6 +174,7 @@
         mLetterboxTaskListener.onTaskAppeared(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds
                         /* parentBounds */ new Rect(0, 0, 200, 100),
                         /* activityBounds */ new Rect(50, 0, 100, 75),
                         /* taskBounds */ new Rect(25, 0, 100, 100)),
@@ -190,6 +195,7 @@
         mLetterboxTaskListener.onTaskAppeared(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds
                         /* parentBounds */ new Rect(0, 0, 100, 150),
                         /* activityBounds */ new Rect(0, 75, 50, 125),
                         /* taskBounds */ new Rect(0, 50, 100, 125)),
@@ -210,6 +216,7 @@
         mLetterboxTaskListener.onTaskAppeared(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds
                         /* parentBounds */ new Rect(0, 0, 100, 150),
                         /* activityBounds */ new Rect(0, 75, 50, 125),
                         /* taskBounds */ new Rect(0, 50, 100, 125)),
@@ -230,6 +237,7 @@
         mLetterboxTaskListener.onTaskAppeared(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds
                         /* parentBounds */ new Rect(0, 0, 100, 150),
                         /* activityBounds */ new Rect(0, 75, 50, 125),
                         /* taskBounds */ new Rect(0, 50, 100, 125)),
@@ -250,6 +258,7 @@
         mLetterboxTaskListener.onTaskAppeared(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 200, 125), // equal to parent bounds
                         /* parentBounds */ new Rect(0, 0, 200, 125),
                         /* activityBounds */ new Rect(15, 0, 175, 120),
                         /* taskBounds */ new Rect(0, 0, 100, 125)), // equal to parent bounds
@@ -272,6 +281,7 @@
         mLetterboxTaskListener.onTaskAppeared(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 100, 150),
                         /* parentBounds */ new Rect(0, 75, 100, 225),
                         /* activityBounds */ new Rect(25, 75, 75, 125),
                         /* taskBounds */ new Rect(0, 75, 100, 125)),
@@ -285,7 +295,7 @@
     public void testOnTaskAppeared_calledSecondTimeWithSameTaskId_throwsException() {
         setWindowBoundsAndInsets(new Rect(),  Insets.NONE);
         RunningTaskInfo taskInfo =
-                createTaskInfo(/* taskId */ 1, new Rect(), new Rect(), new Rect());
+                createTaskInfo(/* taskId */ 1, new Rect(),  new Rect(), new Rect(), new Rect());
         mLetterboxTaskListener.onTaskAppeared(taskInfo, mLeash);
         mLetterboxTaskListener.onTaskAppeared(taskInfo, mLeash);
     }
@@ -306,11 +316,13 @@
 
     private static RunningTaskInfo createTaskInfo(
                 int taskId,
+                final Rect maxBounds,
                 final Rect parentBounds,
                 final Rect activityBounds,
                 final Rect taskBounds) {
         RunningTaskInfo taskInfo = new RunningTaskInfo();
         taskInfo.taskId = taskId;
+        taskInfo.configuration.windowConfiguration.setMaxBounds(maxBounds);
         taskInfo.parentBounds = parentBounds;
         taskInfo.configuration.windowConfiguration.setBounds(taskBounds);
         taskInfo.letterboxActivityBounds = Rect.copyOrNull(activityBounds);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 4ed5457..cd53217 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -429,6 +429,7 @@
     whole_static_libs: ["libskia"],
 
     srcs: [
+        "canvas/CanvasFrontend.cpp",
         "canvas/CanvasOpBuffer.cpp",
         "canvas/CanvasOpRasterizer.cpp",
         "pipeline/skia/SkiaDisplayList.cpp",
@@ -607,6 +608,7 @@
         "tests/unit/CacheManagerTests.cpp",
         "tests/unit/CanvasContextTests.cpp",
         "tests/unit/CanvasOpTests.cpp",
+        "tests/unit/CanvasFrontendTests.cpp",
         "tests/unit/CommonPoolTests.cpp",
         "tests/unit/DamageAccumulatorTests.cpp",
         "tests/unit/DeferredLayerUpdaterTests.cpp",
diff --git a/libs/hwui/SaveFlags.h b/libs/hwui/SaveFlags.h
new file mode 100644
index 0000000..f3579a8
--- /dev/null
+++ b/libs/hwui/SaveFlags.h
@@ -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.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+// TODO: Move this to an enum class
+namespace android::SaveFlags {
+
+// These must match the corresponding Canvas API constants.
+enum {
+    Matrix = 0x01,
+    Clip = 0x02,
+    HasAlphaLayer = 0x04,
+    ClipToLayer = 0x10,
+
+    // Helper constant
+    MatrixClip = Matrix | Clip,
+};
+typedef uint32_t Flags;
+
+}  // namespace android::SaveFlags
diff --git a/libs/hwui/canvas/CanvasFrontend.cpp b/libs/hwui/canvas/CanvasFrontend.cpp
new file mode 100644
index 0000000..2c839b0
--- /dev/null
+++ b/libs/hwui/canvas/CanvasFrontend.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+#include "CanvasFrontend.h"
+#include "CanvasOps.h"
+#include "CanvasOpBuffer.h"
+
+namespace android::uirenderer {
+
+CanvasStateHelper::CanvasStateHelper(int width, int height) {
+    mInitialBounds = SkIRect::MakeWH(width, height);
+    mSaveStack.emplace_back();
+    mClipStack.emplace_back().setRect(mInitialBounds);
+    mTransformStack.emplace_back();
+    mCurrentClipIndex = 0;
+    mCurrentTransformIndex = 0;
+}
+
+bool CanvasStateHelper::internalSave(SaveEntry saveEntry) {
+    mSaveStack.push_back(saveEntry);
+    if (saveEntry.matrix) {
+        // We need to push before accessing transform() to ensure the reference doesn't move
+        // across vector resizes
+        mTransformStack.emplace_back() = transform();
+        mCurrentTransformIndex += 1;
+    }
+    if (saveEntry.clip) {
+        // We need to push before accessing clip() to ensure the reference doesn't move
+        // across vector resizes
+        mClipStack.emplace_back() = clip();
+        mCurrentClipIndex += 1;
+        return true;
+    }
+    return false;
+}
+
+// Assert that the cast from SkClipOp to SkRegion::Op is valid
+static_assert(static_cast<int>(SkClipOp::kDifference) == SkRegion::Op::kDifference_Op);
+static_assert(static_cast<int>(SkClipOp::kIntersect) == SkRegion::Op::kIntersect_Op);
+static_assert(static_cast<int>(SkClipOp::kUnion_deprecated) == SkRegion::Op::kUnion_Op);
+static_assert(static_cast<int>(SkClipOp::kXOR_deprecated) == SkRegion::Op::kXOR_Op);
+static_assert(static_cast<int>(SkClipOp::kReverseDifference_deprecated) == SkRegion::Op::kReverseDifference_Op);
+static_assert(static_cast<int>(SkClipOp::kReplace_deprecated) == SkRegion::Op::kReplace_Op);
+
+void CanvasStateHelper::internalClipRect(const SkRect& rect, SkClipOp op) {
+    clip().opRect(rect, transform(), mInitialBounds, (SkRegion::Op)op, false);
+}
+
+void CanvasStateHelper::internalClipPath(const SkPath& path, SkClipOp op) {
+    clip().opPath(path, transform(), mInitialBounds, (SkRegion::Op)op, true);
+}
+
+bool CanvasStateHelper::internalRestore() {
+    // Prevent underflows
+    if (saveCount() <= 1) {
+        return false;
+    }
+
+    SaveEntry entry = mSaveStack[mSaveStack.size() - 1];
+    mSaveStack.pop_back();
+    bool needsRestorePropagation = entry.layer;
+    if (entry.matrix) {
+        mTransformStack.pop_back();
+        mCurrentTransformIndex -= 1;
+    }
+    if (entry.clip) {
+        // We need to push before accessing clip() to ensure the reference doesn't move
+        // across vector resizes
+        mClipStack.pop_back();
+        mCurrentClipIndex -= 1;
+        needsRestorePropagation = true;
+    }
+    return needsRestorePropagation;
+}
+
+SkRect CanvasStateHelper::getClipBounds() const {
+    SkIRect ibounds = clip().getBounds();
+
+    if (ibounds.isEmpty()) {
+        return SkRect::MakeEmpty();
+    }
+
+    SkMatrix inverse;
+    // if we can't invert the CTM, we can't return local clip bounds
+    if (!transform().invert(&inverse)) {
+        return SkRect::MakeEmpty();
+    }
+
+    SkRect ret = SkRect::MakeEmpty();
+    inverse.mapRect(&ret, SkRect::Make(ibounds));
+    return ret;
+}
+
+bool CanvasStateHelper::quickRejectRect(float left, float top, float right, float bottom) const {
+    // TODO: Implement
+    return false;
+}
+
+bool CanvasStateHelper::quickRejectPath(const SkPath& path) const {
+    // TODO: Implement
+    return false;
+}
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/canvas/CanvasFrontend.h b/libs/hwui/canvas/CanvasFrontend.h
new file mode 100644
index 0000000..5fccccb
--- /dev/null
+++ b/libs/hwui/canvas/CanvasFrontend.h
@@ -0,0 +1,200 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+// TODO: Can we get the dependencies scoped down more?
+#include "CanvasOps.h"
+#include "CanvasOpBuffer.h"
+#include <SaveFlags.h>
+
+#include <SkRasterClip.h>
+#include <ui/FatVector.h>
+
+#include <optional>
+
+namespace android::uirenderer {
+
+// Exists to avoid forcing all this common logic into the templated class
+class CanvasStateHelper {
+protected:
+    CanvasStateHelper(int width, int height);
+    ~CanvasStateHelper() = default;
+
+    struct SaveEntry {
+        bool clip : 1 = false;
+        bool matrix : 1 = false;
+        bool layer : 1 = false;
+    };
+
+    constexpr SaveEntry saveEntryForLayer() {
+        return {
+            .clip = true,
+            .matrix = true,
+            .layer = true,
+        };
+    }
+
+    constexpr SaveEntry flagsToSaveEntry(SaveFlags::Flags flags) {
+        return SaveEntry {
+            .clip = static_cast<bool>(flags & SaveFlags::Clip),
+            .matrix = static_cast<bool>(flags & SaveFlags::Matrix),
+            .layer = false
+        };
+    }
+
+    bool internalSave(SaveEntry saveEntry);
+    bool internalSave(SaveFlags::Flags flags) {
+        return internalSave(flagsToSaveEntry(flags));
+    }
+    void internalSaveLayer(const SkCanvas::SaveLayerRec& layerRec) {
+        internalSave({
+            .clip = true,
+            .matrix = true,
+            .layer = true
+        });
+        internalClipRect(*layerRec.fBounds, SkClipOp::kIntersect);
+    }
+
+    bool internalRestore();
+
+    void internalClipRect(const SkRect& rect, SkClipOp op);
+    void internalClipPath(const SkPath& path, SkClipOp op);
+
+    SkIRect mInitialBounds;
+    FatVector<SaveEntry, 6> mSaveStack;
+    FatVector<SkMatrix, 6> mTransformStack;
+    FatVector<SkConservativeClip, 6> mClipStack;
+
+    size_t mCurrentTransformIndex;
+    size_t mCurrentClipIndex;
+
+    const SkConservativeClip& clip() const {
+        return mClipStack[mCurrentClipIndex];
+    }
+
+    SkConservativeClip& clip() {
+        return mClipStack[mCurrentClipIndex];
+    }
+
+public:
+    int saveCount() const { return mSaveStack.size(); }
+
+    SkRect getClipBounds() const;
+    bool quickRejectRect(float left, float top, float right, float bottom) const;
+    bool quickRejectPath(const SkPath& path) const;
+
+    const SkMatrix& transform() const {
+        return mTransformStack[mCurrentTransformIndex];
+    }
+
+    SkMatrix& transform() {
+        return mTransformStack[mCurrentTransformIndex];
+    }
+
+    // For compat with existing HWUI Canvas interface
+    void getMatrix(SkMatrix* outMatrix) const {
+        *outMatrix = transform();
+    }
+
+    void setMatrix(const SkMatrix& matrix) {
+        transform() = matrix;
+    }
+
+    void concat(const SkMatrix& matrix) {
+        transform().preConcat(matrix);
+    }
+
+    void rotate(float degrees) {
+        SkMatrix m;
+        m.setRotate(degrees);
+        concat(m);
+    }
+
+    void scale(float sx, float sy) {
+        SkMatrix m;
+        m.setScale(sx, sy);
+        concat(m);
+    }
+
+    void skew(float sx, float sy) {
+        SkMatrix m;
+        m.setSkew(sx, sy);
+        concat(m);
+    }
+
+    void translate(float dx, float dy) {
+        transform().preTranslate(dx, dy);
+    }
+};
+
+// Front-end canvas that handles queries, up-front state, and produces CanvasOp<> output downstream
+template <typename CanvasOpReceiver>
+class CanvasFrontend final : public CanvasStateHelper {
+public:
+    template<class... Args>
+    CanvasFrontend(int width, int height, Args&&... args) : CanvasStateHelper(width, height),
+            mReceiver(std::forward<Args>(args)...) { }
+    ~CanvasFrontend() = default;
+
+    void save(SaveFlags::Flags flags = SaveFlags::MatrixClip) {
+        if (internalSave(flagsToSaveEntry(flags))) {
+            submit<CanvasOpType::Save>({});
+        }
+    }
+
+    void restore() {
+        if (internalRestore()) {
+            submit<CanvasOpType::Restore>({});
+        }
+    }
+
+    template <CanvasOpType T>
+    void draw(CanvasOp<T>&& op) {
+        // The front-end requires going through certain front-doors, which these aren't.
+        static_assert(T != CanvasOpType::Save, "Must use CanvasFrontend::save() call instead");
+        static_assert(T != CanvasOpType::Restore, "Must use CanvasFrontend::restore() call instead");
+
+        if constexpr (T == CanvasOpType::SaveLayer) {
+            internalSaveLayer(op.saveLayerRec);
+        }
+        if constexpr (T == CanvasOpType::SaveBehind) {
+            // Don't use internalSaveLayer as this doesn't apply clipping, it's a "regular" save
+            // But we do want to flag it as a layer, such that restore is Definitely Required
+            internalSave(saveEntryForLayer());
+        }
+        if constexpr (T == CanvasOpType::ClipRect) {
+            internalClipRect(op.rect, op.op);
+        }
+        if constexpr (T == CanvasOpType::ClipPath) {
+            internalClipPath(op.path, op.op);
+        }
+
+        submit(std::move(op));
+    }
+
+    const CanvasOpReceiver& receiver() const { return mReceiver; }
+
+private:
+    CanvasOpReceiver mReceiver;
+
+    template <CanvasOpType T>
+    void submit(CanvasOp<T>&& op) {
+        mReceiver.push_container(CanvasOpContainer(std::move(op), transform()));
+    }
+};
+
+} // namespace android::uirenderer
diff --git a/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl b/libs/hwui/canvas/CanvasOpRecorder.cpp
similarity index 72%
copy from services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl
copy to libs/hwui/canvas/CanvasOpRecorder.cpp
index 45e4c69..bb968ee8 100644
--- a/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl
+++ b/libs/hwui/canvas/CanvasOpRecorder.cpp
@@ -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.
@@ -14,10 +14,9 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+#include "CanvasOpRecorder.h"
 
-/** @hide */
-parcelable WifiApiServiceInfo {
-    String name;
-    IBinder binder;
-}
+#include "CanvasOpBuffer.h"
+#include "CanvasOps.h"
+
+namespace android::uirenderer {}  // namespace android::uirenderer
diff --git a/libs/hwui/canvas/CanvasOpRecorder.h b/libs/hwui/canvas/CanvasOpRecorder.h
new file mode 100644
index 0000000..7d95bc4
--- /dev/null
+++ b/libs/hwui/canvas/CanvasOpRecorder.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "hwui/Canvas.h"
+#include "CanvasOpBuffer.h"
+
+#include <vector>
+
+namespace android::uirenderer {
+
+// Interop with existing HWUI Canvas
+class CanvasOpRecorder final : /* todo: public Canvas */ {
+public:
+    // Transform ops
+private:
+    struct SaveEntry {
+
+    };
+
+    std::vector<SaveEntry> mSaveStack;
+};
+
+}  // namespace android::uirenderer
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index f94bae274..4d67166 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -18,6 +18,7 @@
 
 #include <cutils/compiler.h>
 #include <utils/Functor.h>
+#include <SaveFlags.h>
 
 #include <androidfw/ResourceTypes.h>
 #include "Properties.h"
@@ -57,22 +58,6 @@
 using DisplayList = skiapipeline::SkiaDisplayList;
 }
 
-namespace SaveFlags {
-
-// These must match the corresponding Canvas API constants.
-enum {
-    Matrix = 0x01,
-    Clip = 0x02,
-    HasAlphaLayer = 0x04,
-    ClipToLayer = 0x10,
-
-    // Helper constant
-    MatrixClip = Matrix | Clip,
-};
-typedef uint32_t Flags;
-
-}  // namespace SaveFlags
-
 namespace uirenderer {
 namespace VectorDrawable {
 class Tree;
diff --git a/libs/hwui/tests/unit/CanvasFrontendTests.cpp b/libs/hwui/tests/unit/CanvasFrontendTests.cpp
new file mode 100644
index 0000000..05b1179
--- /dev/null
+++ b/libs/hwui/tests/unit/CanvasFrontendTests.cpp
@@ -0,0 +1,213 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <canvas/CanvasFrontend.h>
+#include <canvas/CanvasOpBuffer.h>
+#include <canvas/CanvasOps.h>
+#include <canvas/CanvasOpRasterizer.h>
+
+#include <tests/common/CallCountingCanvas.h>
+
+#include "SkPictureRecorder.h"
+#include "SkColor.h"
+#include "SkLatticeIter.h"
+#include "pipeline/skia/AnimatedDrawables.h"
+#include <SkNoDrawCanvas.h>
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::test;
+
+class CanvasOpCountingReceiver {
+public:
+    template <CanvasOpType T>
+    void push_container(CanvasOpContainer<T>&& op) {
+        mOpCounts[static_cast<size_t>(T)] += 1;
+    }
+
+    int operator[](CanvasOpType op) const {
+        return mOpCounts[static_cast<size_t>(op)];
+    }
+
+private:
+    std::array<int, static_cast<size_t>(CanvasOpType::COUNT)> mOpCounts;
+};
+
+TEST(CanvasFrontend, saveCount) {
+    SkNoDrawCanvas skiaCanvas(100, 100);
+    CanvasFrontend<CanvasOpCountingReceiver> opCanvas(100, 100);
+    const auto& receiver = opCanvas.receiver();
+
+    EXPECT_EQ(1, skiaCanvas.getSaveCount());
+    EXPECT_EQ(1, opCanvas.saveCount());
+
+    skiaCanvas.save();
+    opCanvas.save(SaveFlags::MatrixClip);
+    EXPECT_EQ(2, skiaCanvas.getSaveCount());
+    EXPECT_EQ(2, opCanvas.saveCount());
+
+    skiaCanvas.restore();
+    opCanvas.restore();
+    EXPECT_EQ(1, skiaCanvas.getSaveCount());
+    EXPECT_EQ(1, opCanvas.saveCount());
+
+    skiaCanvas.restore();
+    opCanvas.restore();
+    EXPECT_EQ(1, skiaCanvas.getSaveCount());
+    EXPECT_EQ(1, opCanvas.saveCount());
+
+    EXPECT_EQ(1, receiver[CanvasOpType::Save]);
+    EXPECT_EQ(1, receiver[CanvasOpType::Restore]);
+}
+
+TEST(CanvasFrontend, transform) {
+    SkNoDrawCanvas skiaCanvas(100, 100);
+    CanvasFrontend<CanvasOpCountingReceiver> opCanvas(100, 100);
+
+    skiaCanvas.translate(10, 10);
+    opCanvas.translate(10, 10);
+    EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform());
+
+    {
+        skiaCanvas.save();
+        opCanvas.save(SaveFlags::Matrix);
+        skiaCanvas.scale(2.0f, 1.125f);
+        opCanvas.scale(2.0f, 1.125f);
+
+        EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform());
+        skiaCanvas.restore();
+        opCanvas.restore();
+    }
+
+    EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform());
+
+    {
+        skiaCanvas.save();
+        opCanvas.save(SaveFlags::Matrix);
+        skiaCanvas.rotate(90.f);
+        opCanvas.rotate(90.f);
+
+        EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform());
+
+        {
+            skiaCanvas.save();
+            opCanvas.save(SaveFlags::Matrix);
+            skiaCanvas.skew(5.0f, 2.25f);
+            opCanvas.skew(5.0f, 2.25f);
+
+            EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform());
+            skiaCanvas.restore();
+            opCanvas.restore();
+        }
+
+        skiaCanvas.restore();
+        opCanvas.restore();
+    }
+
+    EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform());
+}
+
+TEST(CanvasFrontend, drawOpTransform) {
+    CanvasFrontend<CanvasOpBuffer> opCanvas(100, 100);
+    const auto& receiver = opCanvas.receiver();
+
+    auto makeDrawRect = [] {
+        return CanvasOp<CanvasOpType::DrawRect>{
+            .rect = SkRect::MakeWH(50, 50),
+            .paint = SkPaint(SkColors::kBlack),
+        };
+    };
+
+    opCanvas.draw(makeDrawRect());
+
+    opCanvas.translate(10, 10);
+    opCanvas.draw(makeDrawRect());
+
+    opCanvas.save();
+    opCanvas.scale(2.0f, 4.0f);
+    opCanvas.draw(makeDrawRect());
+    opCanvas.restore();
+
+    opCanvas.save();
+    opCanvas.translate(20, 15);
+    opCanvas.draw(makeDrawRect());
+    opCanvas.save();
+    opCanvas.rotate(90.f);
+    opCanvas.draw(makeDrawRect());
+    opCanvas.restore();
+    opCanvas.restore();
+
+    // Validate the results
+    std::vector<SkMatrix> transforms;
+    transforms.reserve(5);
+    receiver.for_each([&](auto op) {
+        // Filter for the DrawRect calls; ignore the save & restores
+        // (TODO: Add a filtered for_each variant to OpBuffer?)
+        if (op->type() == CanvasOpType::DrawRect) {
+            transforms.push_back(op->transform());
+        }
+    });
+
+    EXPECT_EQ(transforms.size(), 5);
+
+    {
+        // First result should be identity
+        const auto& result = transforms[0];
+        EXPECT_EQ(SkMatrix::kIdentity_Mask, result.getType());
+        EXPECT_EQ(SkMatrix::I(), result);
+    }
+
+    {
+        // Should be translate 10, 10
+        const auto& result = transforms[1];
+        EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType());
+        SkMatrix m;
+        m.setTranslate(10, 10);
+        EXPECT_EQ(m, result);
+    }
+
+    {
+        // Should be translate 10, 10 + scale 2, 4
+        const auto& result = transforms[2];
+        EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask, result.getType());
+        SkMatrix m;
+        m.setTranslate(10, 10);
+        m.preScale(2.0f, 4.0f);
+        EXPECT_EQ(m, result);
+    }
+
+    {
+        // Should be translate 10, 10 + translate 20, 15
+        const auto& result = transforms[3];
+        EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType());
+        SkMatrix m;
+        m.setTranslate(30, 25);
+        EXPECT_EQ(m, result);
+    }
+
+    {
+        // Should be translate 10, 10 + translate 20, 15 + rotate 90
+        const auto& result = transforms[4];
+        EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kAffine_Mask | SkMatrix::kScale_Mask,
+                result.getType());
+        SkMatrix m;
+        m.setTranslate(30, 25);
+        m.preRotate(90.f);
+        EXPECT_EQ(m, result);
+    }
+}
\ No newline at end of file
diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp
index b15c322..f186e55 100644
--- a/libs/hwui/tests/unit/CanvasOpTests.cpp
+++ b/libs/hwui/tests/unit/CanvasOpTests.cpp
@@ -16,6 +16,7 @@
 
 #include <gtest/gtest.h>
 
+#include <canvas/CanvasFrontend.h>
 #include <canvas/CanvasOpBuffer.h>
 #include <canvas/CanvasOps.h>
 #include <canvas/CanvasOpRasterizer.h>
@@ -26,6 +27,7 @@
 #include "SkColor.h"
 #include "SkLatticeIter.h"
 #include "pipeline/skia/AnimatedDrawables.h"
+#include <SkNoDrawCanvas.h>
 
 using namespace android;
 using namespace android::uirenderer;
@@ -78,6 +80,21 @@
 
 using MockBuffer = OpBuffer<MockTypes, MockOpContainer>;
 
+class CanvasOpCountingReceiver {
+public:
+    template <CanvasOpType T>
+    void push_container(CanvasOpContainer<T>&& op) {
+        mOpCounts[static_cast<size_t>(T)] += 1;
+    }
+
+    int operator[](CanvasOpType op) const {
+        return mOpCounts[static_cast<size_t>(op)];
+    }
+
+private:
+    std::array<int, static_cast<size_t>(CanvasOpType::COUNT)> mOpCounts;
+};
+
 template<typename T>
 static int countItems(const T& t) {
     int count = 0;
@@ -614,4 +631,35 @@
     rasterizer.draw(op);
     EXPECT_EQ(1, canvas->drawRectCount);
     EXPECT_EQ(1, canvas->sumTotalDrawCalls());
+}
+
+TEST(CanvasOp, frontendSaveCount) {
+    SkNoDrawCanvas skiaCanvas(100, 100);
+    CanvasFrontend<CanvasOpCountingReceiver> opCanvas(100, 100);
+    const auto& receiver = opCanvas.receiver();
+
+    EXPECT_EQ(1, skiaCanvas.getSaveCount());
+    EXPECT_EQ(1, opCanvas.saveCount());
+
+    skiaCanvas.save();
+    opCanvas.save(SaveFlags::MatrixClip);
+    EXPECT_EQ(2, skiaCanvas.getSaveCount());
+    EXPECT_EQ(2, opCanvas.saveCount());
+
+    skiaCanvas.restore();
+    opCanvas.restore();
+    EXPECT_EQ(1, skiaCanvas.getSaveCount());
+    EXPECT_EQ(1, opCanvas.saveCount());
+
+    skiaCanvas.restore();
+    opCanvas.restore();
+    EXPECT_EQ(1, skiaCanvas.getSaveCount());
+    EXPECT_EQ(1, opCanvas.saveCount());
+
+    EXPECT_EQ(1, receiver[Op::Save]);
+    EXPECT_EQ(1, receiver[Op::Restore]);
+}
+
+TEST(CanvasOp, frontendTransform) {
+
 }
\ No newline at end of file
diff --git a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl
index fcb7d60..47debe9 100644
--- a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl
+++ b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl
@@ -23,5 +23,6 @@
  * @hide
  */
 interface IPlaybackMetricsManager {
-    void reportPlaybackMetrics(in PlaybackMetrics metrics, int userId);
+    void reportPlaybackMetrics(in String sessionId, in PlaybackMetrics metrics, int userId);
+    String getSessionId(int userId);
 }
\ No newline at end of file
diff --git a/media/java/android/media/metrics/PlaybackMetricsManager.java b/media/java/android/media/metrics/PlaybackMetricsManager.java
index 3606f53..d51ff47 100644
--- a/media/java/android/media/metrics/PlaybackMetricsManager.java
+++ b/media/java/android/media/metrics/PlaybackMetricsManager.java
@@ -16,6 +16,7 @@
 
 package android.media.metrics;
 
+import android.annotation.NonNull;
 import android.os.RemoteException;
 
 /**
@@ -38,10 +39,24 @@
 
     /**
      * Reports playback metrics.
+     * @hide
      */
-    public void reportPlaybackMetrics(PlaybackMetrics metrics) {
+    public void reportPlaybackMetrics(@NonNull String sessionId, PlaybackMetrics metrics) {
         try {
-            mService.reportPlaybackMetrics(metrics, mUserId);
+            mService.reportPlaybackMetrics(sessionId, metrics, mUserId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Creates a playback session.
+     */
+    public PlaybackSession createSession() {
+        try {
+            String id = mService.getSessionId(mUserId);
+            PlaybackSession session = new PlaybackSession(id, this);
+            return session;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/media/java/android/media/metrics/PlaybackSession.java b/media/java/android/media/metrics/PlaybackSession.java
new file mode 100644
index 0000000..4ad8906
--- /dev/null
+++ b/media/java/android/media/metrics/PlaybackSession.java
@@ -0,0 +1,74 @@
+/*
+ * 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.metrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.AnnotationValidations;
+
+import java.util.Objects;
+
+/**
+ * @hide
+ */
+public final class PlaybackSession implements AutoCloseable {
+    private final @NonNull String mId;
+    private final @NonNull PlaybackMetricsManager mManager;
+    private boolean mClosed = false;
+
+    /**
+     * Creates a new PlaybackSession.
+     *
+     * @hide
+     */
+    public PlaybackSession(@NonNull String id, @NonNull PlaybackMetricsManager manager) {
+        mId = id;
+        mManager = manager;
+        AnnotationValidations.validate(NonNull.class, null, mId);
+        AnnotationValidations.validate(NonNull.class, null, mManager);
+    }
+
+    /**
+     * Reports playback metrics.
+     */
+    public void reportPlaybackMetrics(@NonNull PlaybackMetrics metrics) {
+        mManager.reportPlaybackMetrics(mId, metrics);
+    }
+
+    public @NonNull String getId() {
+        return mId;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PlaybackSession that = (PlaybackSession) o;
+        return Objects.equals(mId, that.mId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mId);
+    }
+
+    @Override
+    public void close() throws Exception {
+        mClosed = true;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 9f16d03..ac20ee1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -408,7 +408,8 @@
     }
 
     /**
-     * Checks if an admin has enforced minimum password quality requirements on the given user.
+     * Checks if an admin has enforced minimum password quality or complexity requirements on the
+     * given user.
      *
      * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
      * or {@code null} if no quality requirements are set. If the requirements are set by
@@ -428,6 +429,30 @@
         }
 
         LockPatternUtils lockPatternUtils = new LockPatternUtils(context);
+        final int aggregatedComplexity = dpm.getAggregatedPasswordComplexityForUser(userId);
+        if (aggregatedComplexity > DevicePolicyManager.PASSWORD_COMPLEXITY_NONE) {
+            // First, check if there's a Device Owner. If so, then only it can apply password
+            // complexity requiremnts (there can be no secondary profiles).
+            final UserHandle deviceOwnerUser = dpm.getDeviceOwnerUser();
+            if (deviceOwnerUser != null) {
+                return new EnforcedAdmin(dpm.getDeviceOwnerComponentOnAnyUser(), deviceOwnerUser);
+            }
+
+            // The complexity could be enforced by a Profile Owner - either in the current user
+            // or the current user is the parent user that is affected by the profile owner.
+            for (UserInfo userInfo : UserManager.get(context).getProfiles(userId)) {
+                final ComponentName profileOwnerComponent = dpm.getProfileOwnerAsUser(userInfo.id);
+                if (profileOwnerComponent != null) {
+                    return new EnforcedAdmin(profileOwnerComponent, getUserHandleOf(userInfo.id));
+                }
+            }
+
+            // Should not get here: A Device Owner or Profile Owner should be found.
+            throw new IllegalStateException(
+                    String.format("Could not find admin enforcing complexity %d for user %d",
+                            aggregatedComplexity, userId));
+        }
+
         if (sProxy.isSeparateProfileChallengeEnabled(lockPatternUtils, userId)) {
             // userId is managed profile and has a separate challenge, only consider
             // the admins in that user.
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index 00f94f5..9d4669a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -18,6 +18,7 @@
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
@@ -56,8 +57,9 @@
 
     @Override
     public Drawable getIcon() {
-        final Drawable drawable = getIconWithoutBackground();
-        if (!isFastPairDevice()) {
+        final Drawable drawable =
+                BluetoothUtils.getBtDrawableWithDescription(mContext, mCachedDevice).first;
+        if (!(drawable instanceof BitmapDrawable)) {
             setColorFilter(drawable);
         }
         return BluetoothUtils.buildAdvancedDrawable(mContext, drawable);
@@ -65,9 +67,7 @@
 
     @Override
     public Drawable getIconWithoutBackground() {
-        return isFastPairDevice()
-                ? BluetoothUtils.getBtDrawableWithDescription(mContext, mCachedDevice).first
-                : mContext.getDrawable(R.drawable.ic_headphone);
+        return BluetoothUtils.getBtClassDrawableWithDescription(mContext, mCachedDevice).first;
     }
 
     @Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
index 8973d11..e887c450 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
@@ -24,6 +24,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
+import android.graphics.drawable.BitmapDrawable;
 
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 
@@ -96,4 +97,17 @@
 
         assertThat(mBluetoothMediaDevice.isFastPairDevice()).isFalse();
     }
+
+    @Test
+    public void getIcon_isNotFastPairDevice_drawableTypeIsNotBitmapDrawable() {
+        final BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class);
+        when(mDevice.getDevice()).thenReturn(bluetoothDevice);
+
+        final String value = "False";
+        final byte[] bytes = value.getBytes();
+        when(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
+                .thenReturn(bytes);
+
+        assertThat(mBluetoothMediaDevice.getIcon() instanceof BitmapDrawable).isFalse();
+    }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java b/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java
index 5e5a9d9..c33f02df 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java
@@ -82,6 +82,7 @@
     private static PendingIntent getPendingActivity(Context context) {
         Intent intent = new Intent("com.android.settings.WIFI_TETHER_SETTINGS")
                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+        return PendingIntent.getActivity(context, 0, intent,
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
     }
 }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 2a699ea..2e3ea24 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -70,6 +70,7 @@
     <uses-permission android:name="android.permission.SET_PROCESS_LIMIT" />
     <uses-permission android:name="android.permission.SET_ALWAYS_FINISH" />
     <uses-permission android:name="android.permission.DUMP" />
+    <uses-permission android:name="android.permission.CONTROL_UI_TRACING" />
     <uses-permission android:name="android.permission.SIGNAL_PERSISTENT_PROCESSES" />
     <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
     <!-- Internal permissions granted to the shell. -->
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 7120cc2..52b41a4 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -530,6 +530,14 @@
             androidprv:alwaysFocusable="true"
             android:excludeFromRecents="true" />
 
+        <!-- started from TvNotificationPanel -->
+        <activity
+            android:name=".statusbar.tv.notifications.TvNotificationPanelActivity"
+            android:excludeFromRecents="true"
+            android:launchMode="singleTask"
+            android:noHistory="true"
+            android:theme="@style/TvSidePanelTheme" />
+
         <!-- started from SliceProvider -->
         <activity android:name=".SlicePermissionActivity"
             android:theme="@style/Theme.SystemUI.Dialog.Alert"
diff --git a/packages/SystemUI/res-keyguard/drawable/circle_white.xml b/packages/SystemUI/res-keyguard/drawable/circle_white.xml
new file mode 100644
index 0000000..d1b2097
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/circle_white.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="oval">
+    <solid android:color="#33FFFFFF" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/disabled_udfps_view.xml b/packages/SystemUI/res/layout/disabled_udfps_view.xml
new file mode 100644
index 0000000..aab8661
--- /dev/null
+++ b/packages/SystemUI/res/layout/disabled_udfps_view.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<com.android.keyguard.DisabledUdfpsView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/disabled_udfps_view"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="@drawable/circle_white"
+/>
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index 6c20c1e..2bf41d2 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -40,18 +40,4 @@
         android:visibility="gone"
         android:pointerIcon="crosshair"/>
     <include layout="@layout/global_screenshot_static"/>
-    <FrameLayout
-        android:id="@+id/global_screenshot_dismiss_button"
-        android:layout_width="@dimen/screenshot_dismiss_button_tappable_size"
-        android:layout_height="@dimen/screenshot_dismiss_button_tappable_size"
-        android:elevation="7dp"
-        android:visibility="gone"
-        android:contentDescription="@string/screenshot_dismiss_description">
-        <ImageView
-            android:id="@+id/global_screenshot_dismiss_image"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_margin="@dimen/screenshot_dismiss_button_margin"
-            android:src="@drawable/screenshot_cancel"/>
-    </FrameLayout>
 </com.android.systemui.screenshot.ScreenshotView>
diff --git a/packages/SystemUI/res/layout/global_screenshot_static.xml b/packages/SystemUI/res/layout/global_screenshot_static.xml
index 096ec7d..9f63c43 100644
--- a/packages/SystemUI/res/layout/global_screenshot_static.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_static.xml
@@ -17,7 +17,6 @@
 <androidx.constraintlayout.widget.ConstraintLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:fitsSystemWindows="true"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
     <ImageView
@@ -62,4 +61,22 @@
         </LinearLayout>
     </HorizontalScrollView>
     <include layout="@layout/global_screenshot_preview"/>
+    <FrameLayout
+        android:id="@+id/global_screenshot_dismiss_button"
+        android:layout_width="@dimen/screenshot_dismiss_button_tappable_size"
+        android:layout_height="@dimen/screenshot_dismiss_button_tappable_size"
+        android:elevation="7dp"
+        android:visibility="gone"
+        app:layout_constraintStart_toEndOf="@id/global_screenshot_preview"
+        app:layout_constraintEnd_toEndOf="@id/global_screenshot_preview"
+        app:layout_constraintTop_toTopOf="@id/global_screenshot_preview"
+        app:layout_constraintBottom_toTopOf="@id/global_screenshot_preview"
+        android:contentDescription="@string/screenshot_dismiss_description">
+        <ImageView
+            android:id="@+id/global_screenshot_dismiss_image"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_margin="@dimen/screenshot_dismiss_button_margin"
+            android:src="@drawable/screenshot_cancel"/>
+    </FrameLayout>
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/quick_settings_footer.xml b/packages/SystemUI/res/layout/quick_settings_footer.xml
index e7c7b5f..13572fa 100644
--- a/packages/SystemUI/res/layout/quick_settings_footer.xml
+++ b/packages/SystemUI/res/layout/quick_settings_footer.xml
@@ -26,11 +26,19 @@
     android:layout_gravity="center_vertical|center_horizontal"
     android:background="@android:color/transparent">
 
+    <ImageView
+        android:id="@+id/primary_footer_icon"
+        android:layout_width="@dimen/qs_footer_icon_size"
+        android:layout_height="@dimen/qs_footer_icon_size"
+        android:gravity="start"
+        android:layout_marginEnd="8dp"
+        android:contentDescription="@null"
+        android:tint="?android:attr/textColorPrimary" />
+
     <com.android.systemui.util.AutoMarqueeTextView
         android:id="@+id/footer_text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:gravity="start"
         android:layout_weight="1"
         android:singleLine="true"
         android:ellipsize="marquee"
diff --git a/packages/SystemUI/res/layout/quick_settings_footer_dialog_parental_controls.xml b/packages/SystemUI/res/layout/quick_settings_footer_dialog_parental_controls.xml
index 1a35676..3e40321 100644
--- a/packages/SystemUI/res/layout/quick_settings_footer_dialog_parental_controls.xml
+++ b/packages/SystemUI/res/layout/quick_settings_footer_dialog_parental_controls.xml
@@ -29,8 +29,8 @@
         android:orientation="vertical">
         <ImageView
             android:id="@+id/parental_controls_icon"
-            android:layout_width="36dip"
-            android:layout_height="36dip"
+            android:layout_width="24dip"
+            android:layout_height="24dip"
             android:layout_gravity="center_horizontal"
 
         />
diff --git a/packages/SystemUI/res/layout/tv_notification_item.xml b/packages/SystemUI/res/layout/tv_notification_item.xml
new file mode 100644
index 0000000..711cd4e
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_notification_item.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/tv_notification_panel_width"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/selectableItemBackground"
+    android:orientation="vertical"
+    android:padding="12dp">
+
+    <TextView
+        android:id="@+id/tv_notification_title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingBottom="12dp"
+        android:textColor="@color/tv_notification_text_color"
+        android:textSize="18sp" />
+
+    <TextView
+        android:id="@+id/tv_notification_details"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textColor="@color/tv_notification_text_color" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_notification_panel.xml b/packages/SystemUI/res/layout/tv_notification_panel.xml
new file mode 100644
index 0000000..8f00a72
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_notification_panel.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/tv_notification_panel"
+    android:layout_width="@dimen/tv_notification_panel_width"
+    android:layout_height="match_parent"
+    android:layout_gravity="end"
+    android:background="@color/tv_notification_background_color"
+    android:orientation="vertical">
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:padding="12dp"
+        android:paddingTop="24dp"
+        android:text="@string/tv_notification_panel_title"
+        android:textColor="@color/tv_notification_text_color"
+        android:textSize="24sp"
+        android:textStyle="bold" />
+
+    <TextView
+        android:id="@+id/no_tv_notifications"
+        style="?android:attr/titleTextStyle"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:gravity="top|center"
+        android:paddingTop="24dp"
+        android:text="@string/tv_notification_panel_no_notifications"
+        android:textColor="@color/tv_notification_text_color"
+        android:visibility="gone" />
+
+    <androidx.leanback.widget.VerticalGridView
+        android:id="@+id/notifications_list"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1" />
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
index ccd235d..c078805 100644
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
 <com.android.systemui.biometrics.UdfpsView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index 09ec439..0c8c5c4 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -29,7 +29,8 @@
         <item>com.android.systemui.util.NotificationChannels</item>
         <item>com.android.systemui.volume.VolumeUI</item>
         <item>com.android.systemui.statusbar.tv.TvStatusBar</item>
-        <item>com.android.systemui.statusbar.tv.TvNotificationPanel</item>
+        <item>com.android.systemui.statusbar.tv.notifications.TvNotificationPanel</item>
+        <item>com.android.systemui.statusbar.tv.notifications.TvNotificationHandler</item>
         <item>com.android.systemui.statusbar.tv.VpnStatusObserver</item>
         <item>com.android.systemui.usb.StorageNotification</item>
         <item>com.android.systemui.power.PowerUI</item>
diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml
index 9b0ae1d..0961f50 100644
--- a/packages/SystemUI/res/values/colors_tv.xml
+++ b/packages/SystemUI/res/values/colors_tv.xml
@@ -33,4 +33,7 @@
     <color name="tv_volume_dialog_seek_bar_background">#A03C4043</color>
     <color name="tv_volume_dialog_seek_bar_fill">#FFF8F9FA</color>
     <color name="tv_volume_dialog_accent">#FFDADCE0</color>
+
+    <color name="tv_notification_background_color">#383838</color>
+    <color name="tv_notification_text_color">#FFFFFF</color>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml
new file mode 100644
index 0000000..9545bfd
--- /dev/null
+++ b/packages/SystemUI/res/values/dimens_tv.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<resources>
+    <dimen name="tv_notification_panel_width">360dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
index 13271d6..b51cb56 100644
--- a/packages/SystemUI/res/values/strings_tv.xml
+++ b/packages/SystemUI/res/values/strings_tv.xml
@@ -26,4 +26,6 @@
     <!-- Disclosure text in the connected notification that indicates that the device is connected to a VPN. The placeholder is the VPN name. [CHAR LIMIT=40] -->
     <string name="notification_disclosure_vpn_text">Via <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string>
 
+    <string name="tv_notification_panel_title">Notifications</string>
+    <string name="tv_notification_panel_no_notifications">No Notifications</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles_tv.xml b/packages/SystemUI/res/values/styles_tv.xml
index 0c4fd23..cb433f3 100644
--- a/packages/SystemUI/res/values/styles_tv.xml
+++ b/packages/SystemUI/res/values/styles_tv.xml
@@ -23,4 +23,12 @@
         <item name="android:backgroundDimEnabled">false</item>
         <item name="android:windowDisablePreview">true</item>
      </style>
+
+    <style name="TvSidePanelTheme">
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:backgroundDimEnabled">false</item>
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java
new file mode 100644
index 0000000..f01b67b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
+
+import android.hardware.biometrics.BiometricSourceType;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.util.ViewController;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Controls when to show the DisabledUdfpsView to unlock the device on the lockscreen.
+ * If the device is not authenticated, the bouncer will show.
+ *
+ * This tap target will only show when:
+ * - User has UDFPS enrolled
+ * - UDFPS is currently unavailable see {@link KeyguardUpdateMonitor#shouldListenForUdfps}
+ */
+@SysUISingleton
+public class DisabledUdfpsController extends ViewController<DisabledUdfpsView> implements Dumpable {
+    @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @NonNull private final KeyguardViewController mKeyguardViewController;
+    @NonNull private final StatusBarStateController mStatusBarStateController;
+
+    private boolean mIsDozing;
+    private boolean mIsBouncerShowing;
+    private boolean mIsKeyguardShowing;
+    private boolean mRunningFPS;
+    private boolean mAuthenticated;
+
+    private boolean mShowButton;
+
+    public DisabledUdfpsController(
+            @NonNull DisabledUdfpsView view,
+            @NonNull StatusBarStateController statusBarStateController,
+            @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
+            @NonNull AuthController authController,
+            @NonNull KeyguardViewController keyguardViewController
+    ) {
+        super(view);
+        mView.setOnClickListener(mOnClickListener);
+        mView.setSensorProperties(authController.getUdfpsProps().get(0));
+
+        mStatusBarStateController = statusBarStateController;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mKeyguardViewController = keyguardViewController;
+    }
+
+    @Override
+    protected void onViewAttached() {
+        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
+        mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
+
+        mStatusBarStateController.addCallback(mStatusBarStateListener);
+        mIsKeyguardShowing = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
+        mIsDozing = mStatusBarStateController.isDozing();
+        mAuthenticated = false;
+    }
+
+    @Override
+    protected void onViewDetached() {
+        mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
+        mStatusBarStateController.removeCallback(mStatusBarStateListener);
+    }
+
+    private void updateButtonVisibility() {
+        mShowButton = !mAuthenticated && !mIsDozing && mIsKeyguardShowing
+                && !mIsBouncerShowing && !mRunningFPS;
+        if (mShowButton) {
+            mView.setVisibility(View.VISIBLE);
+        } else {
+            mView.setVisibility(View.INVISIBLE);
+        }
+    }
+
+    @Override
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+        pw.println("DisabledUdfpsController state:");
+        pw.println("  mShowBouncerButton: " + mShowButton);
+        pw.println("  mIsDozing: " + mIsDozing);
+        pw.println("  mIsKeyguardShowing: " + mIsKeyguardShowing);
+        pw.println("  mIsBouncerShowing: " + mIsBouncerShowing);
+        pw.println("  mRunningFPS: " + mRunningFPS);
+        pw.println("  mAuthenticated: " + mAuthenticated);
+    }
+
+    private final View.OnClickListener mOnClickListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            mKeyguardViewController.showBouncer(/* scrim */ true);
+        }
+    };
+
+    private StatusBarStateController.StateListener mStatusBarStateListener =
+            new StatusBarStateController.StateListener() {
+                @Override
+                public void onStateChanged(int newState) {
+                    mIsKeyguardShowing = newState == StatusBarState.KEYGUARD;
+                    updateButtonVisibility();
+                }
+
+                @Override
+                public void onDozingChanged(boolean isDozing) {
+                    mIsDozing = isDozing;
+                    updateButtonVisibility();
+                }
+            };
+
+    private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
+            new KeyguardUpdateMonitorCallback() {
+                @Override
+                public void onKeyguardBouncerChanged(boolean bouncer) {
+                    mIsBouncerShowing = bouncer;
+                    updateButtonVisibility();
+                }
+
+                @Override
+                public void onBiometricRunningStateChanged(boolean running,
+                        BiometricSourceType biometricSourceType) {
+                    mRunningFPS = running && biometricSourceType == FINGERPRINT;
+                    updateButtonVisibility();
+                }
+
+                @Override
+                public void onUserUnlocked() {
+                    mAuthenticated = true;
+                    updateButtonVisibility();
+                }
+            };
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsView.java b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsView.java
new file mode 100644
index 0000000..d8ab780
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsView.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 com.android.keyguard;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.graphics.RectF;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.util.AttributeSet;
+import android.view.Surface;
+import android.widget.Button;
+import android.widget.FrameLayout;
+
+/**
+ * A full screen view with an oval target where the UDFPS sensor is.
+ * Controlled by {@link DisabledUdfpsController}.
+ */
+public class DisabledUdfpsView extends Button {
+    @NonNull private final RectF mSensorRect;
+    @NonNull private final Context mContext;
+
+    // Used to obtain the sensor location.
+    @NonNull private FingerprintSensorPropertiesInternal mSensorProps;
+
+    public DisabledUdfpsView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+        mSensorRect = new RectF();
+    }
+
+    public void setSensorProperties(@NonNull FingerprintSensorPropertiesInternal properties) {
+        mSensorProps = properties;
+    }
+
+    // The "h" and "w" are the display's height and width relative to its current rotation.
+    private void updateSensorRect(int h, int w) {
+        // mSensorProps coordinates assume portrait mode.
+        mSensorRect.set(mSensorProps.sensorLocationX - mSensorProps.sensorRadius,
+                mSensorProps.sensorLocationY - mSensorProps.sensorRadius,
+                mSensorProps.sensorLocationX + mSensorProps.sensorRadius,
+                mSensorProps.sensorLocationY + mSensorProps.sensorRadius);
+
+        // Transform mSensorRect if the device is in landscape mode.
+        switch (mContext.getDisplay().getRotation()) {
+            case Surface.ROTATION_90:
+                mSensorRect.set(mSensorRect.top, h - mSensorRect.right, mSensorRect.bottom,
+                        h - mSensorRect.left);
+                break;
+            case Surface.ROTATION_270:
+                mSensorRect.set(w - mSensorRect.bottom, mSensorRect.left, w - mSensorRect.top,
+                        mSensorRect.right);
+                break;
+            default:
+                // Do nothing to stay in portrait mode.
+        }
+
+        setX(mSensorRect.left);
+        setY(mSensorRect.top);
+        setLayoutParams(new FrameLayout.LayoutParams(
+                (int) (mSensorRect.right - mSensorRect.left),
+                (int) (mSensorRect.bottom - mSensorRect.top)));
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        // Always re-compute the layout regardless of whether "changed" is true. It is usually false
+        // when the device goes from landscape to seascape and vice versa, but mSensorRect and
+        // its dependencies need to be recalculated to stay at the same physical location on the
+        // screen.
+        final int w = getLayoutParams().width;
+        final int h = getLayoutParams().height;
+        updateSensorRect(h, w);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index c5c36e9..829ff97 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -62,6 +62,7 @@
     private AnimatableClockController mNewLockScreenClockViewController;
     private FrameLayout mNewLockScreenClockFrame;
     private AnimatableClockController mNewLockScreenLargeClockViewController;
+    private FrameLayout mNewLockScreenLargeClockFrame;
 
     private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
 
@@ -126,6 +127,7 @@
         mView.updateColors(getGradientColors());
         updateAodIcons();
         mNewLockScreenClockFrame = mView.findViewById(R.id.new_lockscreen_clock_view);
+        mNewLockScreenLargeClockFrame = mView.findViewById(R.id.new_lockscreen_clock_view_large);
     }
 
     @Override
@@ -199,13 +201,18 @@
     /**
      * Update position of the view, with optional animation. Move the slice view and the clock
      * slightly towards the center in order to prevent burn-in. Y positioning occurs at the
-     * view parent level.
+     * view parent level. The large clock view will scale instead of using x position offsets, to
+     * keep the clock centered.
      */
-    void updatePosition(int x, AnimationProperties props, boolean animate) {
+    void updatePosition(int x, float scale, AnimationProperties props, boolean animate) {
         x = Math.abs(x);
         if (mNewLockScreenClockFrame != null) {
             PropertyAnimator.setProperty(mNewLockScreenClockFrame, AnimatableProperty.TRANSLATION_X,
                     -x, props, animate);
+            PropertyAnimator.setProperty(mNewLockScreenLargeClockFrame, AnimatableProperty.SCALE_X,
+                    scale, props, animate);
+            PropertyAnimator.setProperty(mNewLockScreenLargeClockFrame, AnimatableProperty.SCALE_Y,
+                    scale, props, animate);
         }
         mKeyguardSliceViewController.updatePosition(x, props, animate);
         mNotificationIconAreaController.updatePosition(x, props, animate);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index c0e06e8..bc81a198 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -193,7 +193,7 @@
     /**
      * Update position of the view with an optional animation
      */
-    public void updatePosition(int x, int y, boolean animate) {
+    public void updatePosition(int x, int y, float scale, boolean animate) {
         PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, CLOCK_ANIMATION_PROPERTIES,
                 animate);
 
@@ -202,10 +202,12 @@
             PropertyAnimator.setProperty(mView, AnimatableProperty.X, 0,
                     CLOCK_ANIMATION_PROPERTIES, animate);
 
-            mKeyguardClockSwitchController.updatePosition(x, CLOCK_ANIMATION_PROPERTIES, animate);
+            mKeyguardClockSwitchController.updatePosition(x, scale, CLOCK_ANIMATION_PROPERTIES,
+                    animate);
         } else {
             // reset any prior movement
-            mKeyguardClockSwitchController.updatePosition(0, CLOCK_ANIMATION_PROPERTIES, animate);
+            mKeyguardClockSwitchController.updatePosition(0, 0f, CLOCK_ANIMATION_PROPERTIES,
+                    animate);
 
             PropertyAnimator.setProperty(mView, AnimatableProperty.X, x,
                     CLOCK_ANIMATION_PROPERTIES, animate);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 611131f..a7e5195 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1925,6 +1925,7 @@
         }
 
         // TODO: Add support for multiple fingerprint sensors, b/173730729
+        updateUdfpsEnrolled(getCurrentUser());
         boolean shouldListenForFingerprint =
                 isUdfpsEnrolled() ? shouldListenForUdfps() : shouldListenForFingerprint();
         boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
@@ -2137,7 +2138,6 @@
         }
         if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
         int userId = getCurrentUser();
-        updateUdfpsEnrolled(userId);
         if (isUnlockWithFingerprintPossible(userId)) {
             if (mFingerprintCancelSignal != null) {
                 mFingerprintCancelSignal.cancel();
@@ -2627,7 +2627,6 @@
         Assert.isMainThread();
         if (DEBUG) Log.d(TAG, "handleKeyguardBouncerChanged(" + bouncerVisible + ")");
         mBouncer = bouncerVisible == 1;
-
         if (mBouncer) {
             // If the bouncer is shown, always clear this flag. This can happen in the following
             // situations: 1) Default camera with SHOW_WHEN_LOCKED is not chosen yet. 2) Secure
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 8e5e9ea..6b4e8bd 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -113,8 +113,10 @@
                     .setShellCommandHandler(mWMComponent.getShellCommandHandler())
                     .setAppPairs(mWMComponent.getAppPairs());
         } else {
-            // TODO: Call on prepareSysUIComponentBuilder but not with real components.
-            builder = builder.setPip(Optional.ofNullable(null))
+            // TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
+            // is separating this logic into newly creating SystemUITestsFactory.
+            builder = prepareSysUIComponentBuilder(builder, mWMComponent)
+                    .setPip(Optional.ofNullable(null))
                     .setSplitScreen(Optional.ofNullable(null))
                     .setOneHanded(Optional.ofNullable(null))
                     .setBubbles(Optional.ofNullable(null))
@@ -194,4 +196,4 @@
             AssetManager am, String modelName) {
         return new BackGestureTfClassifierProvider();
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 061d8cf..9edfee7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -410,6 +410,11 @@
         mCurrentDialog.onHelp(message);
     }
 
+    @Nullable
+    public List<FingerprintSensorPropertiesInternal> getUdfpsProps() {
+        return mUdfpsProps;
+    }
+
     private String getErrorString(int modality, int error, int vendorCode) {
         switch (modality) {
             case TYPE_FACE:
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 13ff3f5..ec4a91c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -22,6 +22,7 @@
 import com.android.systemui.keyguard.WorkLockActivity;
 import com.android.systemui.screenrecord.ScreenRecordDialog;
 import com.android.systemui.settings.brightness.BrightnessDialog;
+import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity;
 import com.android.systemui.tuner.TunerActivity;
 import com.android.systemui.usb.UsbDebuggingActivity;
 import com.android.systemui.usb.UsbDebuggingSecondaryUserActivity;
@@ -85,4 +86,10 @@
     @IntoMap
     @ClassKey(CreateUserActivity.class)
     public abstract Activity bindCreateUserActivity(CreateUserActivity activity);
+
+    /** Inject into TvNotificationPanelActivity. */
+    @Binds
+    @IntoMap
+    @ClassKey(TvNotificationPanelActivity.class)
+    public abstract Activity bindTvNotificationPanelActivity(TvNotificationPanelActivity activity);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index c0013d8..9f6c19b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -34,8 +34,8 @@
 import com.android.systemui.statusbar.dagger.StatusBarModule;
 import com.android.systemui.statusbar.notification.InstantAppNotifier;
 import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.tv.TvNotificationPanel;
 import com.android.systemui.statusbar.tv.TvStatusBar;
+import com.android.systemui.statusbar.tv.notifications.TvNotificationPanel;
 import com.android.systemui.theme.ThemeOverlayController;
 import com.android.systemui.toast.ToastUI;
 import com.android.systemui.util.leak.GarbageMonitor;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt b/packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt
index d1e5059..73abf45 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt
@@ -21,6 +21,7 @@
 private const val MILLIS_PER_MINUTES = 1000 * 60f
 private const val BURN_IN_PREVENTION_PERIOD_Y = 521f
 private const val BURN_IN_PREVENTION_PERIOD_X = 83f
+private const val BURN_IN_PREVENTION_PERIOD_SCALE = 180f
 
 /**
  * Returns the translation offset that should be used to avoid burn in at
@@ -36,6 +37,14 @@
 }
 
 /**
+ * Returns a value to scale a view in order to avoid burn in.
+ */
+fun getBurnInScale(): Float {
+    return 0.8f + zigzag(System.currentTimeMillis() / MILLIS_PER_MINUTES,
+            0.2f, BURN_IN_PREVENTION_PERIOD_SCALE)
+}
+
+/**
  * Implements a continuous, piecewise linear, periodic zig-zag function
  *
  * Can be thought of as a linear approximation of abs(sin(x)))
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index 3295595..5cd3b33 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -19,10 +19,10 @@
 import android.app.Activity;
 import android.app.INotificationManager;
 import android.app.people.IPeopleManager;
+import android.app.people.PeopleSpaceTile;
 import android.content.Context;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
-import android.content.pm.ShortcutInfo;
 import android.os.Bundle;
 import android.os.ServiceManager;
 import android.util.Log;
@@ -68,13 +68,13 @@
      */
     private void setTileViewsWithPriorityConversations() {
         try {
-            List<Map.Entry<Long, ShortcutInfo>> shortcutInfos = PeopleSpaceUtils.getShortcutInfos(
-                    mContext, mNotificationManager, mPeopleManager);
-            for (Map.Entry<Long, ShortcutInfo> entry : shortcutInfos) {
-                ShortcutInfo shortcutInfo = entry.getValue();
+            List<Map.Entry<Long, PeopleSpaceTile>> tiles = PeopleSpaceUtils.getTiles(
+                    mContext, mNotificationManager, mPeopleManager, mLauncherApps);
+            for (Map.Entry<Long, PeopleSpaceTile> entry : tiles) {
+                PeopleSpaceTile tile = entry.getValue();
                 PeopleSpaceTileView tileView = new PeopleSpaceTileView(mContext, mPeopleSpaceLayout,
-                        shortcutInfo.getId());
-                setTileView(tileView, shortcutInfo, entry.getKey());
+                        tile.getId());
+                setTileView(tileView, tile, entry.getKey());
             }
         } catch (Exception e) {
             Log.e(TAG, "Couldn't retrieve conversations", e);
@@ -82,18 +82,18 @@
     }
 
     /** Sets {@code tileView} with the data in {@code conversation}. */
-    private void setTileView(PeopleSpaceTileView tileView, ShortcutInfo shortcutInfo,
+    private void setTileView(PeopleSpaceTileView tileView, PeopleSpaceTile tile,
             long lastInteraction) {
         try {
-            String pkg = shortcutInfo.getPackage();
+            String pkg = tile.getPackageName();
             String status =
                     PeopleSpaceUtils.getLastInteractionString(mContext, lastInteraction);
             tileView.setStatus(status);
 
-            tileView.setName(shortcutInfo.getLabel().toString());
+            tileView.setName(tile.getUserName().toString());
             tileView.setPackageIcon(mPackageManager.getApplicationIcon(pkg));
-            tileView.setPersonIcon(mLauncherApps.getShortcutIconDrawable(shortcutInfo, 0));
-            tileView.setOnClickListener(mLauncherApps, shortcutInfo);
+            tileView.setPersonIcon(tile.getUserIcon());
+            tileView.setOnClickListener(mLauncherApps, tile);
         } catch (Exception e) {
             Log.e(TAG, "Couldn't retrieve shortcut information", e);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java
index d5ef190..4aea5b8 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java
@@ -16,10 +16,12 @@
 
 package com.android.systemui.people;
 
+import android.app.people.PeopleSpaceTile;
 import android.content.Context;
 import android.content.pm.LauncherApps;
-import android.content.pm.ShortcutInfo;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.os.UserHandle;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -71,13 +73,15 @@
         mPackageIconView.setImageDrawable(drawable);
     }
 
-    /** Sets the person drawable on the tile. */
-    public void setPersonIcon(Drawable drawable) {
-        mPersonIconView.setImageDrawable(drawable);
+    /** Sets the person bitmap on the tile. */
+    public void setPersonIcon(Icon icon) {
+        mPersonIconView.setImageIcon(icon);
     }
 
     /** Sets the click listener of the tile. */
-    public void setOnClickListener(LauncherApps launcherApps, ShortcutInfo shortcutInfo) {
-        mTileView.setOnClickListener(v -> launcherApps.startShortcut(shortcutInfo, null, null));
+    public void setOnClickListener(LauncherApps launcherApps, PeopleSpaceTile tile) {
+        mTileView.setOnClickListener(v ->
+                launcherApps.startShortcut(tile.getPackageName(), tile.getId(), null, null,
+                        UserHandle.getUserHandleForUid(tile.getUid())));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 35e445b..fe262b4 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -19,8 +19,9 @@
 import android.app.INotificationManager;
 import android.app.people.ConversationChannel;
 import android.app.people.IPeopleManager;
+import android.app.people.PeopleSpaceTile;
 import android.content.Context;
-import android.content.pm.ShortcutInfo;
+import android.content.pm.LauncherApps;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
@@ -51,30 +52,34 @@
     private static final int MIN_HOUR = 1;
     private static final int ONE_DAY = 1;
 
-
     /** Returns a list of map entries corresponding to user's conversations. */
-    public static List<Map.Entry<Long, ShortcutInfo>> getShortcutInfos(Context context,
-            INotificationManager notificationManager, IPeopleManager peopleManager)
+    public static List<Map.Entry<Long, PeopleSpaceTile>> getTiles(
+            Context context, INotificationManager notificationManager, IPeopleManager peopleManager,
+            LauncherApps launcherApps)
             throws Exception {
         boolean showAllConversations = Settings.Global.getInt(context.getContentResolver(),
                 Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
         List<ConversationChannelWrapper> conversations = notificationManager.getConversations(
                 true).getList();
-        List<Map.Entry<Long, ShortcutInfo>> shortcutInfos = getSortedShortcutInfos(peopleManager,
-                conversations.stream().map(c -> c.getShortcutInfo()));
+        List<Map.Entry<Long, PeopleSpaceTile>> tiles = getSortedTiles(peopleManager,
+                conversations.stream().map(c ->
+                        new PeopleSpaceTile.Builder(c.getShortcutInfo(), launcherApps).build()));
         if (showAllConversations) {
             List<ConversationChannel> recentConversations =
                     peopleManager.getRecentConversations().getList();
-            List<Map.Entry<Long, ShortcutInfo>> recentShortcutInfos = getSortedShortcutInfos(
-                    peopleManager, recentConversations.stream().map(c -> c.getShortcutInfo()));
-            shortcutInfos.addAll(recentShortcutInfos);
+            List<Map.Entry<Long, PeopleSpaceTile>> recentTiles =
+                    getSortedTiles(peopleManager, recentConversations.stream().map(c ->
+                            new PeopleSpaceTile
+                                    .Builder(c.getShortcutInfo(), launcherApps)
+                                    .build()));
+            tiles.addAll(recentTiles);
         }
-        return shortcutInfos;
+        return tiles;
     }
 
     /** Returns a list sorted by ascending last interaction time from {@code stream}. */
-    private static List<Map.Entry<Long, ShortcutInfo>> getSortedShortcutInfos(
-            IPeopleManager peopleManager, Stream<ShortcutInfo> stream) {
+    private static List<Map.Entry<Long, PeopleSpaceTile>> getSortedTiles(
+            IPeopleManager peopleManager, Stream<PeopleSpaceTile> stream) {
         return stream
                 .filter(c -> shouldKeepConversation(c))
                 .map(c -> Map.entry(getLastInteraction(peopleManager, c), c))
@@ -82,13 +87,13 @@
                 .collect(Collectors.toList());
     }
 
-    /** Returns the last interaction time with the user specified by {@code shortcutInfo}. */
+    /** Returns the last interaction time with the user specified by {@code PeopleSpaceTile}. */
     private static Long getLastInteraction(IPeopleManager peopleManager,
-            ShortcutInfo shortcutInfo) {
+            PeopleSpaceTile tile) {
         try {
-            int userId = UserHandle.getUserHandleForUid(shortcutInfo.getUserId()).getIdentifier();
-            String pkg = shortcutInfo.getPackage();
-            return peopleManager.getLastInteraction(pkg, userId, shortcutInfo.getId());
+            int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier();
+            String pkg = tile.getPackageName();
+            return peopleManager.getLastInteraction(pkg, userId, tile.getId());
         } catch (Exception e) {
             Log.e(TAG, "Couldn't retrieve last interaction time", e);
             return 0L;
@@ -157,13 +162,13 @@
      *
      * <p>A valid {@code conversation} must:
      *     <ul>
-     *         <li>Have a non-null {@link ShortcutInfo}
-     *         <li>Have an associated label in the {@link ShortcutInfo}
+     *         <li>Have a non-null {@link PeopleSpaceTile}
+     *         <li>Have an associated label in the {@link PeopleSpaceTile}
      *     </ul>
      * </li>
      */
-    public static boolean shouldKeepConversation(ShortcutInfo shortcutInfo) {
-        return shortcutInfo != null && shortcutInfo.getLabel().length() != 0;
+    public static boolean shouldKeepConversation(PeopleSpaceTile tile) {
+        return tile != null && tile.getUserName().length() != 0;
     }
 
 }
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
index 44f173b..0b0308f 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
@@ -19,8 +19,8 @@
 import android.app.Activity;
 import android.content.Intent;
 import android.content.pm.LauncherApps;
-import android.content.pm.ShortcutInfo;
 import android.os.Bundle;
+import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.systemui.people.PeopleSpaceUtils;
@@ -36,18 +36,19 @@
         if (DEBUG) Log.d(TAG, "onCreate called");
 
         Intent intent = getIntent();
-        ShortcutInfo shortcutInfo = (ShortcutInfo) intent.getParcelableExtra(
-                PeopleSpaceWidgetProvider.EXTRA_SHORTCUT_INFO
-        );
-        if (shortcutInfo != null) {
+        String tileId = intent.getStringExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID);
+        String packageName = intent.getStringExtra(PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME);
+        int uid = intent.getIntExtra(PeopleSpaceWidgetProvider.EXTRA_UID, 0);
+
+        if (tileId != null && !tileId.isEmpty()) {
             if (DEBUG) {
-                Log.d(TAG, "Launching conversation with shortcutInfo id " + shortcutInfo.getId());
+                Log.d(TAG, "Launching conversation with shortcutInfo id " + tileId);
             }
             try {
                 LauncherApps launcherApps =
                         getApplicationContext().getSystemService(LauncherApps.class);
                 launcherApps.startShortcut(
-                        shortcutInfo, null, null);
+                        packageName, tileId, null, null, UserHandle.getUserHandleForUid(uid));
             } catch (Exception e) {
                 Log.e(TAG, "Exception starting shortcut:" + e);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
index aa98b61..9f84514 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
@@ -32,7 +32,9 @@
     private static final String TAG = "PeopleSpaceWidgetPvd";
     private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
 
-    public static final String EXTRA_SHORTCUT_INFO = "extra_shortcut_info";
+    public static final String EXTRA_TILE_ID = "extra_tile_id";
+    public static final String EXTRA_PACKAGE_NAME = "extra_package_name";
+    public static final String EXTRA_UID = "extra_uid";
 
     /** Called when widget updates. */
     public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
index a4527c3..1e6c213 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
@@ -18,11 +18,11 @@
 
 import android.app.INotificationManager;
 import android.app.people.IPeopleManager;
+import android.app.people.PeopleSpaceTile;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
-import android.content.pm.ShortcutInfo;
 import android.os.ServiceManager;
 import android.util.Log;
 import android.widget.RemoteViews;
@@ -45,7 +45,7 @@
     private INotificationManager mNotificationManager;
     private PackageManager mPackageManager;
     private LauncherApps mLauncherApps;
-    private List<Map.Entry<Long, ShortcutInfo>> mShortcutInfos = new ArrayList<>();
+    private List<Map.Entry<Long, PeopleSpaceTile>> mTiles = new ArrayList<>();
     private Context mContext;
 
     public PeopleSpaceWidgetRemoteViewsFactory(Context context, Intent intent) {
@@ -70,8 +70,8 @@
      */
     private void setTileViewsWithPriorityConversations() {
         try {
-            mShortcutInfos = PeopleSpaceUtils.getShortcutInfos(mContext, mNotificationManager,
-                    mPeopleManager);
+            mTiles = PeopleSpaceUtils.getTiles(mContext, mNotificationManager,
+                    mPeopleManager, mLauncherApps);
         } catch (Exception e) {
             Log.e(TAG, "Couldn't retrieve conversations", e);
         }
@@ -85,12 +85,12 @@
 
     @Override
     public void onDestroy() {
-        mShortcutInfos.clear();
+        mTiles.clear();
     }
 
     @Override
     public int getCount() {
-        return mShortcutInfos.size();
+        return mTiles.size();
     }
 
     @Override
@@ -100,30 +100,28 @@
         RemoteViews personView = new RemoteViews(mContext.getPackageName(),
                 R.layout.people_space_widget_item);
         try {
-            Map.Entry<Long, ShortcutInfo> entry = mShortcutInfos.get(i);
-            ShortcutInfo shortcutInfo = entry.getValue();
+            Map.Entry<Long, PeopleSpaceTile> entry = mTiles.get(i);
+            PeopleSpaceTile tile = entry.getValue();
             long lastInteraction = entry.getKey();
 
             String status = PeopleSpaceUtils.getLastInteractionString(mContext, lastInteraction);
 
             personView.setTextViewText(R.id.status, status);
-            personView.setTextViewText(R.id.name, shortcutInfo.getLabel().toString());
+            personView.setTextViewText(R.id.name, tile.getUserName().toString());
 
             personView.setImageViewBitmap(
                     R.id.package_icon,
                     PeopleSpaceUtils.convertDrawableToBitmap(
-                            mPackageManager.getApplicationIcon(shortcutInfo.getPackage())
+                            mPackageManager.getApplicationIcon(tile.getPackageName())
                     )
             );
-            personView.setImageViewBitmap(
-                    R.id.person_icon,
-                    PeopleSpaceUtils.convertDrawableToBitmap(
-                            mLauncherApps.getShortcutIconDrawable(shortcutInfo, 0)
-                    )
-            );
+            personView.setImageViewIcon(R.id.person_icon, tile.getUserIcon());
 
             Intent fillInIntent = new Intent();
-            fillInIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_SHORTCUT_INFO, shortcutInfo);
+            fillInIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, tile.getId());
+            fillInIntent.putExtra(
+                    PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, tile.getPackageName());
+            fillInIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_UID, tile.getUid());
             personView.setOnClickFillInIntent(R.id.item, fillInIntent);
         } catch (Exception e) {
             Log.e(TAG, "Couldn't retrieve shortcut information", e);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 4789239..5afe526 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -68,6 +68,7 @@
     private final View mRootView;
     private final TextView mFooterText;
     private final ImageView mFooterIcon;
+    private final ImageView mPrimaryFooterIcon;
     private final Context mContext;
     private final Callback mCallback = new Callback();
     private final SecurityController mSecurityController;
@@ -83,6 +84,7 @@
     private CharSequence mFooterTextContent = null;
     private int mFooterTextId;
     private int mFooterIconId;
+    private Drawable mPrimaryFooterIconDrawable;
 
     @Inject
     QSSecurityFooter(@Named(QS_SECURITY_FOOTER_VIEW) View rootView, Context context,
@@ -92,6 +94,7 @@
         mRootView.setOnClickListener(this);
         mFooterText = mRootView.findViewById(R.id.footer_text);
         mFooterIcon = mRootView.findViewById(R.id.footer_icon);
+        mPrimaryFooterIcon = mRootView.findViewById(R.id.primary_footer_icon);
         mFooterIconId = R.drawable.ic_info_outline;
         mContext = context;
         mMainHandler = mainHandler;
@@ -188,6 +191,18 @@
             mFooterIconId = footerIconId;
             mMainHandler.post(mUpdateIcon);
         }
+
+        // Update the primary icon
+        if (isParentalControlsEnabled) {
+            if (mPrimaryFooterIconDrawable == null) {
+                DeviceAdminInfo info = mSecurityController.getDeviceAdminInfo();
+                mPrimaryFooterIconDrawable = mSecurityController.getIcon(info);
+            }
+        } else {
+            mPrimaryFooterIconDrawable = null;
+        }
+        mMainHandler.post(mUpdatePrimaryIcon);
+
         mMainHandler.post(mUpdateDisplayState);
     }
 
@@ -532,6 +547,15 @@
         }
     };
 
+    private final Runnable mUpdatePrimaryIcon = new Runnable() {
+        @Override
+        public void run() {
+            mPrimaryFooterIcon.setVisibility(mPrimaryFooterIconDrawable != null
+                    ? View.VISIBLE : View.GONE);
+            mPrimaryFooterIcon.setImageDrawable(mPrimaryFooterIconDrawable);
+        }
+    };
+
     private final Runnable mUpdateDisplayState = new Runnable() {
         @Override
         public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 35874cd..1e91673 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -19,6 +19,8 @@
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
 
 import static java.util.Objects.requireNonNull;
 
@@ -52,7 +54,8 @@
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
 import android.view.View;
-import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -60,6 +63,7 @@
 
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.logging.UiEventLogger;
+import com.android.internal.policy.PhoneWindow;
 import com.android.systemui.R;
 import com.android.systemui.util.DeviceConfigProxy;
 
@@ -147,6 +151,8 @@
     private final MediaActionSound mCameraSound;
     private final ScrollCaptureClient mScrollCaptureClient;
     private final DeviceConfigProxy mConfigProxy;
+    private final PhoneWindow mWindow;
+    private final View mDecorView;
 
     private final Binder mWindowToken;
     private ScreenshotView mScreenshotView;
@@ -189,13 +195,12 @@
 
         final DisplayManager dm = requireNonNull(context.getSystemService(DisplayManager.class));
         mDisplay = dm.getDisplay(DEFAULT_DISPLAY);
-        mContext = context.createDisplayContext(mDisplay);
+        mContext = context.createWindowContext(TYPE_SCREENSHOT, null);
         mWindowManager = mContext.getSystemService(WindowManager.class);
 
         mAccessibilityManager = AccessibilityManager.getInstance(mContext);
         mConfigProxy = configProxy;
 
-        reloadAssets();
         Configuration config = mContext.getResources().getConfiguration();
         mInDarkMode = config.isNightModeActive();
         mDirectionLTR = config.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
@@ -204,9 +209,8 @@
         mScrollCaptureClient.setHostWindowToken(mWindowToken);
 
         // Setup the window that we are going to use
-        mWindowLayoutParams = new WindowManager.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
-                WindowManager.LayoutParams.TYPE_SCREENSHOT,
+        mWindowLayoutParams = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT, 0, 0,
+                TYPE_SCREENSHOT,
                 WindowManager.LayoutParams.FLAG_FULLSCREEN
                         | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
@@ -217,11 +221,19 @@
         mWindowLayoutParams.setTitle("ScreenshotAnimation");
         mWindowLayoutParams.layoutInDisplayCutoutMode =
                 WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        mWindowLayoutParams.setFitInsetsTypes(0 /* types */);
         mWindowLayoutParams.token = mWindowToken;
         // This is needed to let touches pass through outside the touchable areas
         mWindowLayoutParams.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
 
+        mWindow = new PhoneWindow(mContext);
+        mWindow.setWindowManager(mWindowManager, null, null);
+        mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+        mWindow.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
+        mWindow.setBackgroundDrawableResource(android.R.color.transparent);
+        mDecorView = mWindow.getDecorView();
+
+        reloadAssets();
+
         mDisplayMetrics = new DisplayMetrics();
         mDisplay.getRealMetrics(mDisplayMetrics);
 
@@ -355,11 +367,21 @@
      * button and the actions container background, since the buttons are re-inflated on demand.
      */
     private void reloadAssets() {
-        boolean wasAttached = mScreenshotView != null && mScreenshotView.isAttachedToWindow();
+        boolean wasAttached = mDecorView.isAttachedToWindow();
         if (wasAttached) {
-            mWindowManager.removeView(mScreenshotView);
+            mWindowManager.removeView(mDecorView);
         }
 
+        // respect the display cutout in landscape (since we'd otherwise overlap) but not portrait
+        mWindowLayoutParams.setFitInsetsTypes(
+                mOrientationPortrait ? 0 : WindowInsets.Type.displayCutout());
+
+        // ignore system bar insets for the purpose of window layout
+        mDecorView.setOnApplyWindowInsetsListener((v, insets) -> v.onApplyWindowInsets(
+                new WindowInsets.Builder(insets)
+                        .setInsets(WindowInsets.Type.all(), Insets.NONE)
+                        .build()));
+
         // Inflate the screenshot layout
         mScreenshotView = (ScreenshotView)
                 LayoutInflater.from(mContext).inflate(R.layout.global_screenshot, null);
@@ -382,9 +404,8 @@
             return false;
         });
 
-        if (wasAttached) {
-            mWindowManager.addView(mScreenshotView, mWindowLayoutParams);
-        }
+        // view is added to window manager in startAnimation
+        mWindow.setContentView(mScreenshotView, mWindowLayoutParams);
     }
 
     /**
@@ -510,10 +531,10 @@
         mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
         mScreenshotHandler.post(() -> {
             if (!mScreenshotView.isAttachedToWindow()) {
-                mWindowManager.addView(mScreenshotView, mWindowLayoutParams);
+                mWindowManager.addView(mWindow.getDecorView(), mWindowLayoutParams);
             }
 
-            mScreenshotView.prepareForAnimation(mScreenBitmap, screenRect, screenInsets);
+            mScreenshotView.prepareForAnimation(mScreenBitmap, screenInsets);
 
             mScreenshotHandler.post(() -> {
                 mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener(
@@ -541,7 +562,7 @@
 
     private void resetScreenshotView() {
         if (mScreenshotView.isAttachedToWindow()) {
-            mWindowManager.removeView(mScreenshotView);
+            mWindowManager.removeView(mDecorView);
         }
         mScreenshotView.reset();
         mOnCompleteRunnable.run();
@@ -636,8 +657,8 @@
         } else {
             mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
         }
-        if (mScreenshotView.isAttachedToWindow()) {
-            mWindowManager.updateViewLayout(mScreenshotView, mWindowLayoutParams);
+        if (mDecorView.isAttachedToWindow()) {
+            mWindowManager.updateViewLayout(mDecorView, mWindowLayoutParams);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index b020275..3814bd2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -240,8 +240,7 @@
 
         setOnApplyWindowInsetsListener((v, insets) -> {
             if (QuickStepContract.isGesturalMode(mNavMode)) {
-                Insets gestureInsets = insets.getInsets(
-                        WindowInsets.Type.systemGestures());
+                Insets gestureInsets = insets.getInsets(WindowInsets.Type.systemGestures());
                 mLeftInset = gestureInsets.left;
                 mRightInset = gestureInsets.right;
             } else {
@@ -272,7 +271,7 @@
         mScreenshotSelectorView.requestFocus();
     }
 
-    void prepareForAnimation(Bitmap bitmap, Rect screenRect, Insets screenInsets) {
+    void prepareForAnimation(Bitmap bitmap, Insets screenInsets) {
         mScreenshotPreview.setImageDrawable(createScreenDrawable(mResources, bitmap, screenInsets));
         // make static preview invisible (from gone) so we can query its location on screen
         mScreenshotPreview.setVisibility(View.INVISIBLE);
@@ -284,6 +283,8 @@
 
         Rect previewBounds = new Rect();
         mScreenshotPreview.getBoundsOnScreen(previewBounds);
+        int[] previewLocation = new int[2];
+        mScreenshotPreview.getLocationInWindow(previewLocation);
 
         float cornerScale =
                 mCornerSizeX / (mOrientationPortrait ? bounds.width() : bounds.height());
@@ -310,7 +311,8 @@
 
         // animate from the current location, to the static preview location
         final PointF startPos = new PointF(bounds.centerX(), bounds.centerY());
-        final PointF finalPos = new PointF(previewBounds.centerX(), previewBounds.centerY());
+        final PointF finalPos = new PointF(previewLocation[0] + previewBounds.width() / 2f,
+                previewLocation[1] + previewBounds.height() / 2f);
 
         ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1);
         toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
index 0e6bcc5..dbee0ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
@@ -129,7 +129,7 @@
      */
     public void updateChildrenAppearance() {
         List<ExpandableNotificationRow> notificationChildren = mRow.getAttachedChildren();
-        if (notificationChildren == null) {
+        if (notificationChildren == null || !mRow.isSummaryWithChildren()) {
             return;
         }
         // Initialize the processors
@@ -256,8 +256,8 @@
         }
 
         public void init() {
-            mParentView = mParentRow.getNotificationViewWrapper().getNotificationHeader()
-                    .findViewById(mId);
+            View header = mParentRow.getNotificationViewWrapper().getNotificationHeader();
+            mParentView = header == null ? null : header.findViewById(mId);
             mParentData = mExtractor == null ? null : mExtractor.extractData(mParentRow);
             mApply = !mComparator.isEmpty(mParentView);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
index 967524c..ecd0c41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
@@ -40,6 +40,14 @@
             View.TRANSLATION_X, R.id.x_animator_tag, R.id.x_animator_tag_start_value,
             R.id.x_animator_tag_end_value);
 
+    public static final AnimatableProperty SCALE_X = AnimatableProperty.from(
+            View.SCALE_X, R.id.scale_x_animator_tag, R.id.scale_x_animator_start_value_tag,
+            R.id.scale_x_animator_end_value_tag);
+
+    public static final AnimatableProperty SCALE_Y = AnimatableProperty.from(
+            View.SCALE_Y, R.id.scale_y_animator_tag, R.id.scale_y_animator_start_value_tag,
+            R.id.scale_y_animator_end_value_tag);
+
     /**
      * Similar to X, however this doesn't allow for any other modifications other than from this
      * property. When using X, it's possible that the view is laid out during the animation,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 7508dcb..1a2d1cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone;
 
 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
+import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInScale;
 import static com.android.systemui.statusbar.notification.NotificationUtils.interpolate;
 
 import android.content.res.Resources;
@@ -184,6 +185,7 @@
         result.stackScrollerPaddingExpanded = mBypassEnabled ? mUnlockedStackScrollerPadding
                 : getClockY(1.0f) + mKeyguardStatusHeight;
         result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount);
+        result.clockScale = interpolate(getBurnInScale(), 1.0f, 1.0f - mDarkAmount);
     }
 
     /**
@@ -304,6 +306,11 @@
         public float clockAlpha;
 
         /**
+         * Amount to scale the large clock (0.0 - 1.0)
+         */
+        public float clockScale;
+
+        /**
          * The top padding of the stack scroller, in pixels.
          */
         public int stackScrollerPadding;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index f7139aa..c67e8fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -68,6 +68,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.LatencyTracker;
+import com.android.keyguard.DisabledUdfpsController;
 import com.android.keyguard.KeyguardClockSwitchController;
 import com.android.keyguard.KeyguardStatusView;
 import com.android.keyguard.KeyguardStatusViewController;
@@ -256,7 +257,25 @@
                     mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
                     mDelayShowingKeyguardStatusBar = false;
                 }
-            };
+
+                @Override
+                public void onKeyguardVisibilityChanged(boolean showing) {
+                    if (mDisabledUdfpsController == null
+                            && mAuthController.getUdfpsRegion() != null
+                            && mAuthController.isUdfpsEnrolled(
+                                   KeyguardUpdateMonitor.getCurrentUser())) {
+                        LayoutInflater.from(mView.getContext())
+                                .inflate(R.layout.disabled_udfps_view, mView);
+                        mDisabledUdfpsController = new DisabledUdfpsController(
+                                mView.findViewById(R.id.disabled_udfps_view),
+                                mStatusBarStateController,
+                                mUpdateMonitor,
+                                mAuthController,
+                                mStatusBarKeyguardViewManager);
+                        mDisabledUdfpsController.init();
+                    }
+                }
+    };
 
     private final InjectionInflationController mInjectionInflationController;
     private final PowerManager mPowerManager;
@@ -284,6 +303,7 @@
     private QS mQs;
     private FrameLayout mQsFrame;
     private KeyguardStatusViewController mKeyguardStatusViewController;
+    private DisabledUdfpsController mDisabledUdfpsController;
     private View mQsNavbarScrim;
     private NotificationsQuickSettingsContainer mNotificationContainerParent;
     private boolean mAnimateNextPositionUpdate;
@@ -888,7 +908,8 @@
                     mUpdateMonitor.isUdfpsEnrolled());
             mClockPositionAlgorithm.run(mClockPositionResult);
             mKeyguardStatusViewController.updatePosition(
-                    mClockPositionResult.clockX, mClockPositionResult.clockY, animateClock);
+                    mClockPositionResult.clockX, mClockPositionResult.clockY,
+                    mClockPositionResult.clockScale, animateClock);
             updateNotificationTranslucency();
             updateClock();
             stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
@@ -3044,6 +3065,9 @@
         if (mKeyguardStatusBar != null) {
             mKeyguardStatusBar.dump(fd, pw, args);
         }
+        if (mDisabledUdfpsController != null) {
+            mDisabledUdfpsController.dump(fd, pw, args);
+        }
     }
 
     public boolean hasActiveClearableNotifications() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 2795857..bdf2b0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -71,7 +71,6 @@
             // Creating AudioRecordingDisclosureBar and just letting it run
             new AudioRecordingDisclosureBar(mContext);
         }
-
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationAdapter.java
new file mode 100644
index 0000000..3b1a4db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationAdapter.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tv.notifications;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.systemui.R;
+
+/**
+ * Adapter for the VerticalGridView of the TvNotificationsPanelView.
+ */
+public class TvNotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+    private static final String TAG = "TvNotificationAdapter";
+    private SparseArray<StatusBarNotification> mNotifications;
+
+    public TvNotificationAdapter() {
+        setHasStableIds(true);
+    }
+
+    @NonNull
+    @Override
+    public TvNotificationViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.tv_notification_item,
+                parent, false);
+        return new TvNotificationViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
+        if (mNotifications == null) {
+            Log.e(TAG, "Could not bind view holder because the notification is missing");
+            return;
+        }
+
+        TvNotificationViewHolder holder = (TvNotificationViewHolder) viewHolder;
+        Notification notification = mNotifications.valueAt(position).getNotification();
+        holder.mTitle.setText(notification.extras.getString(Notification.EXTRA_TITLE));
+        holder.mDetails.setText(notification.extras.getString(Notification.EXTRA_TEXT));
+        holder.mPendingIntent = notification.contentIntent;
+    }
+
+    @Override
+    public int getItemCount() {
+        return mNotifications == null ? 0 : mNotifications.size();
+    }
+
+    @Override
+    public long getItemId(int position) {
+        // the item id is the notification id
+        return mNotifications.keyAt(position);
+    }
+
+    /**
+     * Updates the notifications and calls notifyDataSetChanged().
+     */
+    public void setNotifications(SparseArray<StatusBarNotification> notifications) {
+        this.mNotifications = notifications;
+        notifyDataSetChanged();
+    }
+
+    private static class TvNotificationViewHolder extends RecyclerView.ViewHolder implements
+            View.OnClickListener {
+        final TextView mTitle;
+        final TextView mDetails;
+        PendingIntent mPendingIntent;
+
+        protected TvNotificationViewHolder(View itemView) {
+            super(itemView);
+            mTitle = itemView.findViewById(R.id.tv_notification_title);
+            mDetails = itemView.findViewById(R.id.tv_notification_details);
+            itemView.setOnClickListener(this);
+        }
+
+        @Override
+        public void onClick(View v) {
+            try {
+                if (mPendingIntent != null) {
+                    mPendingIntent.send();
+                }
+            } catch (PendingIntent.CanceledException e) {
+                Log.d(TAG, "Pending intent canceled for : " + mPendingIntent);
+            }
+        }
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
new file mode 100644
index 0000000..d985803
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tv.notifications;
+
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.content.Context;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.NotificationListener;
+
+import javax.inject.Inject;
+
+/**
+ * Keeps track of the notifications on TV.
+ */
+public class TvNotificationHandler extends SystemUI implements
+        NotificationListener.NotificationHandler {
+    private static final String TAG = "TvNotificationHandler";
+    private final NotificationListener mNotificationListener;
+    private final SparseArray<StatusBarNotification> mNotifications = new SparseArray<>();
+    @Nullable
+    private Listener mUpdateListener;
+
+    @Inject
+    public TvNotificationHandler(Context context, NotificationListener notificationListener) {
+        super(context);
+        mNotificationListener = notificationListener;
+    }
+
+    public SparseArray<StatusBarNotification> getCurrentNotifications() {
+        return mNotifications;
+    }
+
+    public void setTvNotificationListener(Listener listener) {
+        mUpdateListener = listener;
+    }
+
+    @Override
+    public void start() {
+        mNotificationListener.addNotificationHandler(this);
+        mNotificationListener.registerAsSystemService();
+    }
+
+    @Override
+    public void onNotificationPosted(StatusBarNotification sbn,
+            NotificationListenerService.RankingMap rankingMap) {
+        if (!new Notification.TvExtender(sbn.getNotification()).isAvailableOnTv()) {
+            Log.v(TAG, "Notification not added because it isn't relevant for tv");
+            return;
+        }
+
+        mNotifications.put(sbn.getId(), sbn);
+        if (mUpdateListener != null) {
+            mUpdateListener.notificationsUpdated(mNotifications);
+        }
+        Log.d(TAG, "Notification added");
+    }
+
+    @Override
+    public void onNotificationRemoved(StatusBarNotification sbn,
+            NotificationListenerService.RankingMap rankingMap) {
+
+        if (mNotifications.contains(sbn.getId())) {
+            mNotifications.remove(sbn.getId());
+            Log.d(TAG, "Notification removed");
+
+            if (mUpdateListener != null) {
+                mUpdateListener.notificationsUpdated(mNotifications);
+            }
+        }
+    }
+
+    @Override
+    public void onNotificationRemoved(StatusBarNotification sbn,
+            NotificationListenerService.RankingMap rankingMap, int reason) {
+        onNotificationRemoved(sbn, rankingMap);
+    }
+
+    @Override
+    public void onNotificationRankingUpdate(NotificationListenerService.RankingMap rankingMap) {
+        // noop
+    }
+
+    @Override
+    public void onNotificationsInitialized() {
+        // noop
+    }
+
+    /**
+     * Get notified when the notifications are updated.
+     */
+    interface Listener {
+        void notificationsUpdated(SparseArray<StatusBarNotification> sbns);
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
index 0bd3624..477424c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.tv;
+package com.android.systemui.statusbar.tv.notifications;
 
 import android.Manifest;
 import android.app.NotificationManager;
@@ -59,9 +59,8 @@
             startNotificationHandlerActivity(
                     new Intent(NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL));
         } else {
-            Log.w(TAG,
-                    "Not toggling notification panel: config_notificationHandlerPackage is "
-                            + "empty");
+            openInternalNotificationPanel(
+                    NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL);
         }
     }
 
@@ -71,9 +70,8 @@
             startNotificationHandlerActivity(
                     new Intent(NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL));
         } else {
-            Log.w(TAG,
-                    "Not expanding notification panel: config_notificationHandlerPackage is "
-                            + "empty");
+            openInternalNotificationPanel(
+                    NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL);
         }
     }
 
@@ -86,11 +84,17 @@
             closeNotificationIntent.setPackage(mNotificationHandlerPackage);
             mContext.sendBroadcastAsUser(closeNotificationIntent, UserHandle.CURRENT);
         } else {
-            Log.w(TAG,
-                    "Not closing notification panel: config_notificationHandlerPackage is empty");
+            openInternalNotificationPanel(
+                    NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL);
         }
     }
 
+    private void openInternalNotificationPanel(String action) {
+        Intent intent = new Intent(mContext, TvNotificationPanelActivity.class);
+        intent.setAction(action);
+        mContext.startActivityAsUser(intent, UserHandle.SYSTEM);
+    }
+
     /**
      * Starts the activity intent if all of the following are true
      * <ul>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanelActivity.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanelActivity.java
new file mode 100644
index 0000000..30f401b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanelActivity.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tv.notifications;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+import android.app.NotificationManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.service.notification.StatusBarNotification;
+import android.util.SparseArray;
+import android.view.View;
+
+import androidx.leanback.widget.VerticalGridView;
+
+import com.android.systemui.R;
+
+import javax.inject.Inject;
+
+/**
+ * This Activity shows a notification panel for tv. It is used if no other app (e.g. a launcher) can
+ * be found to show the notifications.
+ */
+public class TvNotificationPanelActivity extends Activity implements
+        TvNotificationHandler.Listener {
+    private final TvNotificationHandler mTvNotificationHandler;
+    private TvNotificationAdapter mTvNotificationAdapter;
+    private VerticalGridView mNotificationListView;
+    private View mNotificationPlaceholder;
+    private boolean mPanelAlreadyOpen = false;
+
+    @Inject
+    public TvNotificationPanelActivity(TvNotificationHandler tvNotificationHandler) {
+        super();
+        mTvNotificationHandler = tvNotificationHandler;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (maybeClosePanel(getIntent())) {
+            return;
+        }
+        mPanelAlreadyOpen = true;
+
+        setContentView(R.layout.tv_notification_panel);
+
+        mNotificationPlaceholder = findViewById(R.id.no_tv_notifications);
+        mTvNotificationAdapter = new TvNotificationAdapter();
+
+        mNotificationListView = findViewById(R.id.notifications_list);
+        mNotificationListView.setAdapter(mTvNotificationAdapter);
+        mNotificationListView.setColumnWidth(R.dimen.tv_notification_panel_width);
+
+        mTvNotificationHandler.setTvNotificationListener(this);
+        notificationsUpdated(mTvNotificationHandler.getCurrentNotifications());
+    }
+
+    @Override
+    public void notificationsUpdated(@NonNull SparseArray<StatusBarNotification> notificationList) {
+        mTvNotificationAdapter.setNotifications(notificationList);
+
+        boolean noNotifications = notificationList.size() == 0;
+        mNotificationListView.setVisibility(noNotifications ? View.GONE : View.VISIBLE);
+        mNotificationPlaceholder.setVisibility(noNotifications ? View.VISIBLE : View.GONE);
+    }
+
+    @Override
+    public void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        maybeClosePanel(intent);
+    }
+
+    /**
+     * Handles intents from onCreate and onNewIntent.
+     *
+     * @return true if the panel is being closed, false if it is being opened
+     */
+    private boolean maybeClosePanel(Intent intent) {
+        if (NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL.equals(intent.getAction())
+                || (mPanelAlreadyOpen
+                && NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL.equals(
+                intent.getAction()))) {
+            finish();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mTvNotificationHandler.setTvNotificationListener(null);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
index 2c3ea4f..353333f 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
@@ -16,14 +16,22 @@
 
 package com.android.systemui.tv;
 
+import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.GlobalRootComponent;
-import com.android.systemui.wmshell.TvPipModule;
+import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler;
 
 import dagger.Binds;
 import dagger.Module;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
 
 @Module
 interface TvSystemUIBinder {
     @Binds
     GlobalRootComponent bindGlobalRootComponent(TvGlobalRootComponent globalRootComponent);
+
+    @Binds
+    @IntoMap
+    @ClassKey(TvNotificationHandler.class)
+    SystemUI bindTvNotificationHandler(TvNotificationHandler systemui);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index 8ffc7cf..56a4c203 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -43,6 +43,7 @@
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsImplementation;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -62,6 +63,7 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler;
 
 import javax.inject.Named;
 
@@ -164,4 +166,11 @@
 
     @Binds
     abstract DozeHost provideDozeHost(DozeServiceHost dozeServiceHost);
+
+    @Provides
+    @SysUISingleton
+    static TvNotificationHandler provideTvNotificationHandler(Context context,
+            NotificationListener notificationListener) {
+        return new TvNotificationHandler(context, notificationListener);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 411fbc3..a879a1e 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -53,7 +53,6 @@
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.tracing.nano.SystemUiTraceProto;
 import com.android.wm.shell.ShellCommandHandler;
-import com.android.wm.shell.apppairs.AppPairs;
 import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
 import com.android.wm.shell.nano.WmShellTraceProto;
 import com.android.wm.shell.onehanded.OneHanded;
@@ -99,13 +98,11 @@
     private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional;
     private final ProtoTracer mProtoTracer;
     private final Optional<ShellCommandHandler> mShellCommandHandler;
-    private final Optional<AppPairs> mAppPairsOptional;
 
     private boolean mIsSysUiStateValid;
     private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
     private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
     private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
-    private KeyguardUpdateMonitorCallback mAppPairsKeyguardCallback;
 
     @Inject
     public WMShell(Context context, CommandQueue commandQueue,
@@ -119,8 +116,7 @@
             Optional<OneHanded> oneHandedOptional,
             Optional<HideDisplayCutout> hideDisplayCutoutOptional,
             ProtoTracer protoTracer,
-            Optional<ShellCommandHandler> shellCommandHandler,
-            Optional<AppPairs> appPairsOptional) {
+            Optional<ShellCommandHandler> shellCommandHandler) {
         super(context);
         mCommandQueue = commandQueue;
         mConfigurationController = configurationController;
@@ -135,7 +131,6 @@
         mProtoTracer = protoTracer;
         mProtoTracer.add(this);
         mShellCommandHandler = shellCommandHandler;
-        mAppPairsOptional = appPairsOptional;
     }
 
     @Override
@@ -145,7 +140,6 @@
         mSplitScreenOptional.ifPresent(this::initSplitScreen);
         mOneHandedOptional.ifPresent(this::initOneHanded);
         mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout);
-        mAppPairsOptional.ifPresent(this::initAppPairs);
     }
 
     @VisibleForTesting
@@ -294,16 +288,6 @@
         });
     }
 
-    void initAppPairs(AppPairs appPairs) {
-        mAppPairsKeyguardCallback = new KeyguardUpdateMonitorCallback() {
-            @Override
-            public void onKeyguardVisibilityChanged(boolean showing) {
-                appPairs.onKeyguardVisibilityChanged(showing);
-            }
-        };
-        mKeyguardUpdateMonitor.registerCallback(mAppPairsKeyguardCallback);
-    }
-
     @Override
     public void writeToProto(SystemUiTraceProto proto) {
         if (proto.wmShell == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 4505b2a..7a1c058 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -84,10 +84,8 @@
     @WMSingleton
     @Provides
     static AppPairs provideAppPairs(ShellTaskOrganizer shellTaskOrganizer,
-            SyncTransactionQueue syncQueue, DisplayController displayController,
-            TaskStackListenerImpl taskStackListener) {
-        return new AppPairsController(shellTaskOrganizer, syncQueue, displayController,
-                taskStackListener);
+            SyncTransactionQueue syncQueue, DisplayController displayController) {
+        return new AppPairsController(shellTaskOrganizer, syncQueue, displayController);
     }
 
     @WMSingleton
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index 6fa6f31..fd0715b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -15,6 +15,7 @@
 package com.android.systemui.qs;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
 
 import static org.junit.Assert.assertFalse;
 import static org.mockito.Matchers.any;
@@ -24,6 +25,8 @@
 import static org.mockito.Mockito.when;
 
 import android.content.pm.UserInfo;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.VectorDrawable;
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings;
@@ -75,6 +78,7 @@
     private ViewGroup mRootView;
     private TextView mFooterText;
     private TestableImageView mFooterIcon;
+    private TestableImageView mPrimaryFooterIcon;
     private QSSecurityFooter mFooter;
     @Mock
     private SecurityController mSecurityController;
@@ -95,6 +99,7 @@
                 mActivityStarter, mSecurityController, looper);
         mFooterText = mRootView.findViewById(R.id.footer_text);
         mFooterIcon = mRootView.findViewById(R.id.footer_icon);
+        mPrimaryFooterIcon = mRootView.findViewById(R.id.primary_footer_icon);
         mFooter.setHostEnvironment(null);
     }
 
@@ -119,6 +124,7 @@
                      mFooterText.getText());
         assertEquals(View.VISIBLE, mRootView.getVisibility());
         assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
         // -1 == never set.
         assertEquals(-1, mFooterIcon.getLastImageResource());
     }
@@ -136,6 +142,7 @@
                 mFooterText.getText());
         assertEquals(View.VISIBLE, mRootView.getVisibility());
         assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
         // -1 == never set.
         assertEquals(-1, mFooterIcon.getLastImageResource());
     }
@@ -165,6 +172,7 @@
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring),
                 mFooterText.getText());
         assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
         // -1 == never set.
         assertEquals(-1, mFooterIcon.getLastImageResource());
 
@@ -203,6 +211,7 @@
                                         VPN_PACKAGE),
                      mFooterText.getText());
         assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
         assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
 
         // Same situation, but with organization name set
@@ -229,6 +238,7 @@
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_vpns),
                      mFooterText.getText());
         assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
         assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
 
         // Same situation, but with organization name set
@@ -252,6 +262,7 @@
 
         TestableLooper.get(this).processAllMessages();
         assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
         assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring),
                 mFooterText.getText());
@@ -534,12 +545,27 @@
     @Test
     public void testParentalControls() {
         when(mSecurityController.isParentalControlsEnabled()).thenReturn(true);
+
+        Drawable testDrawable = new VectorDrawable();
+        when(mSecurityController.getIcon(any())).thenReturn(testDrawable);
+        assertNotNull(mSecurityController.getIcon(null));
+
         mFooter.refreshState();
 
         TestableLooper.get(this).processAllMessages();
 
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_parental_controls),
                 mFooterText.getText());
+        assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility());
+
+        assertEquals(testDrawable, mPrimaryFooterIcon.getDrawable());
+
+        // Ensure the primary icon is set to gone when parental controls is disabled.
+        when(mSecurityController.isParentalControlsEnabled()).thenReturn(false);
+        mFooter.refreshState();
+        TestableLooper.get(this).processAllMessages();
+
+        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 822a6f2..ef25b73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -34,7 +34,6 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.wm.shell.ShellCommandHandler;
-import com.android.wm.shell.apppairs.AppPairs;
 import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
 import com.android.wm.shell.onehanded.OneHanded;
 import com.android.wm.shell.onehanded.OneHandedGestureHandler;
@@ -69,7 +68,6 @@
     @Mock HideDisplayCutout mHideDisplayCutout;
     @Mock ProtoTracer mProtoTracer;
     @Mock ShellCommandHandler mShellCommandHandler;
-    @Mock AppPairs mAppPairs;
 
     @Before
     public void setUp() {
@@ -79,7 +77,7 @@
                 mKeyguardUpdateMonitor, mNavigationModeController,
                 mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mSplitScreen),
                 Optional.of(mOneHanded), Optional.of(mHideDisplayCutout), mProtoTracer,
-                Optional.of(mShellCommandHandler), Optional.of(mAppPairs));
+                Optional.of(mShellCommandHandler));
 
         when(mPip.getPipTouchHandler()).thenReturn(mPipTouchHandler);
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index be7643e..61de53a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -558,12 +558,13 @@
         if (mAms.getMagnificationMode(displayId)
                 == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
             magnificationGestureHandler = new WindowMagnificationGestureHandler(displayContext,
-                    mAms.getWindowMagnificationMgr(), mAms::onMagnificationScaleChanged,
-                    detectControlGestures, triggerable, displayId);
+                    mAms.getWindowMagnificationMgr(), mAms.getMagnificationController(),
+                    detectControlGestures, triggerable,
+                    displayId);
         } else {
             magnificationGestureHandler = new FullScreenMagnificationGestureHandler(displayContext,
-                    mAms.getFullScreenMagnificationController(),
-                    mAms::onMagnificationScaleChanged, detectControlGestures, triggerable,
+                    mAms.getFullScreenMagnificationController(), mAms.getMagnificationController(),
+                    detectControlGestures, triggerable,
                     new WindowMagnificationPromptController(displayContext, mUserId), displayId);
         }
         return magnificationGestureHandler;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 4c85490..be2f8f1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -119,7 +119,6 @@
 import com.android.server.SystemService;
 import com.android.server.accessibility.magnification.FullScreenMagnificationController;
 import com.android.server.accessibility.magnification.MagnificationController;
-import com.android.server.accessibility.magnification.MagnificationGestureHandler;
 import com.android.server.accessibility.magnification.WindowMagnificationManager;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
@@ -153,7 +152,6 @@
         AccessibilityUserState.ServiceInfoChangeListener,
         AccessibilityWindowManager.AccessibilityEventSender,
         AccessibilitySecurityPolicy.AccessibilityUserManager,
-        MagnificationGestureHandler.ScaleChangedListener,
         SystemActionPerformer.SystemActionsChangedListener {
 
     private static final boolean DEBUG = false;
@@ -1066,17 +1064,6 @@
         }
     }
 
-    @Override
-    public void onMagnificationScaleChanged(int displayId, int mode) {
-        synchronized (mLock) {
-            final int capabilities =
-                    getCurrentUserStateLocked().getMagnificationCapabilitiesLocked();
-            if (capabilities == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL) {
-                getWindowMagnificationMgr().showMagnificationButton(displayId, mode);
-            }
-        }
-    }
-
     /**
      * Called by AccessibilityInputFilter when it creates or destroys the motionEventInjector.
      * Not using a getter because the AccessibilityInputFilter isn't thread-safe
@@ -3000,6 +2987,17 @@
         }
     }
 
+    /**
+     * Getter of {@link MagnificationController}.
+     *
+     * @return MagnificationController
+     */
+    MagnificationController getMagnificationController() {
+        synchronized (mLock) {
+            return mMagnificationController;
+        }
+    }
+
     @Override
     public void associateEmbeddedHierarchy(@NonNull IBinder host, @NonNull IBinder embedded) {
         synchronized (mLock) {
@@ -3584,6 +3582,7 @@
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, userState.mUserId);
         if (capabilities != userState.getMagnificationCapabilitiesLocked()) {
             userState.setMagnificationCapabilitiesLocked(capabilities);
+            mMagnificationController.setMagnificationCapabilities(capabilities);
             return true;
         }
         return false;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index efb9d87..7483ff3 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -62,9 +62,6 @@
 import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.accessibility.gestures.GestureUtils;
 
-import java.util.ArrayDeque;
-import java.util.Queue;
-
 /**
  * This class handles full screen magnification in response to touch events.
  *
@@ -115,13 +112,10 @@
  */
 @SuppressWarnings("WeakerAccess")
 public class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler {
-    private static final String LOG_TAG = "FullScreenMagnificationGestureHandler";
 
-    private static final boolean DEBUG_ALL = false;
     private static final boolean DEBUG_STATE_TRANSITIONS = false | DEBUG_ALL;
     private static final boolean DEBUG_DETECTING = false | DEBUG_ALL;
     private static final boolean DEBUG_PANNING_SCALING = false | DEBUG_ALL;
-    private static final boolean DEBUG_EVENT_STREAM = false | DEBUG_ALL;
 
     // The MIN_SCALE is different from MagnificationController.MIN_SCALE due
     // to AccessibilityService.MagnificationController#setScale() has
@@ -139,41 +133,12 @@
     private final ScreenStateReceiver mScreenStateReceiver;
     private final WindowMagnificationPromptController mPromptController;
 
-    /**
-     * {@code true} if this detector should detect and respond to triple-tap
-     * gestures for engaging and disengaging magnification,
-     * {@code false} if it should ignore such gestures
-     */
-    final boolean mDetectTripleTap;
-
-    /**
-     * Whether {@link DetectingState#mShortcutTriggered shortcut} is enabled
-     */
-    final boolean mDetectShortcutTrigger;
-
     @VisibleForTesting State mCurrentState;
     @VisibleForTesting State mPreviousState;
 
     private PointerCoords[] mTempPointerCoords;
     private PointerProperties[] mTempPointerProperties;
 
-    private final int mDisplayId;
-
-    private final Queue<MotionEvent> mDebugInputEventHistory;
-    private final Queue<MotionEvent> mDebugOutputEventHistory;
-
-    /**
-     * @param context Context for resolving various magnification-related resources
-     * @param fullScreenMagnificationController the {@link FullScreenMagnificationController}
-     *
-     * @param detectTripleTap {@code true} if this detector should detect and respond to triple-tap
-     *                                gestures for engaging and disengaging magnification,
-     *                                {@code false} if it should ignore such gestures
-     * @param detectShortcutTrigger {@code true} if this detector should be "triggerable" by some
-     *                           external shortcut invoking {@link #notifyShortcutTriggered},
-     *                           {@code false} if it should ignore such triggers.
-     * @param displayId The logical display id.
-     */
     public FullScreenMagnificationGestureHandler(Context context,
             FullScreenMagnificationController fullScreenMagnificationController,
             ScaleChangedListener listener,
@@ -181,23 +146,20 @@
             boolean detectShortcutTrigger,
             @NonNull WindowMagnificationPromptController promptController,
             int displayId) {
-        super(listener);
+        super(displayId, detectTripleTap, detectShortcutTrigger, listener);
         if (DEBUG_ALL) {
-            Log.i(LOG_TAG,
+            Log.i(mLogTag,
                     "FullScreenMagnificationGestureHandler(detectTripleTap = " + detectTripleTap
                             + ", detectShortcutTrigger = " + detectShortcutTrigger + ")");
         }
         mFullScreenMagnificationController = fullScreenMagnificationController;
         mPromptController = promptController;
-        mDisplayId = displayId;
 
         mDelegatingState = new DelegatingState();
         mDetectingState = new DetectingState(context);
         mViewportDraggingState = new ViewportDraggingState();
         mPanningScalingState = new PanningScalingState(context);
 
-        mDetectTripleTap = detectTripleTap;
-        mDetectShortcutTrigger = detectShortcutTrigger;
         if (mDetectShortcutTrigger) {
             mScreenStateReceiver = new ScreenStateReceiver(context, this);
             mScreenStateReceiver.register();
@@ -205,36 +167,11 @@
             mScreenStateReceiver = null;
         }
 
-        mDebugInputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null;
-        mDebugOutputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null;
-
         transitionTo(mDetectingState);
     }
 
     @Override
-    public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
-        if (DEBUG_EVENT_STREAM) {
-            storeEventInto(mDebugInputEventHistory, event);
-            try {
-                onMotionEventInternal(event, rawEvent, policyFlags);
-            } catch (Exception e) {
-                throw new RuntimeException(
-                        "Exception following input events: " + mDebugInputEventHistory, e);
-            }
-        } else {
-            onMotionEventInternal(event, rawEvent, policyFlags);
-        }
-    }
-
-    private void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
-        if (DEBUG_ALL) Slog.i(LOG_TAG, "onMotionEvent(" + event + ")");
-
-        if ((!mDetectTripleTap && !mDetectShortcutTrigger)
-                || !event.isFromSource(SOURCE_TOUCHSCREEN)) {
-            dispatchTransformedEvent(event, rawEvent, policyFlags);
-            return;
-        }
-
+    void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
         handleEventWith(mCurrentState, event, rawEvent, policyFlags);
     }
 
@@ -259,7 +196,7 @@
     @Override
     public void onDestroy() {
         if (DEBUG_STATE_TRANSITIONS) {
-            Slog.i(LOG_TAG, "onDestroy(); delayed = "
+            Slog.i(mLogTag, "onDestroy(); delayed = "
                     + MotionEventInfo.toString(mDetectingState.mDelayedEventQueue));
         }
 
@@ -299,31 +236,6 @@
         mPanningScalingState.clear();
     }
 
-    private void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent,
-            int policyFlags) {
-        if (DEBUG_EVENT_STREAM) {
-            storeEventInto(mDebugOutputEventHistory, event);
-            try {
-                super.onMotionEvent(event, rawEvent, policyFlags);
-            } catch (Exception e) {
-                throw new RuntimeException(
-                        "Exception downstream following input events: " + mDebugInputEventHistory
-                                + "\nTransformed into output events: " + mDebugOutputEventHistory,
-                        e);
-            }
-        } else {
-            super.onMotionEvent(event, rawEvent, policyFlags);
-        }
-    }
-
-    private static void storeEventInto(Queue<MotionEvent> queue, MotionEvent event) {
-        queue.add(MotionEvent.obtain(event));
-        // Prune old events
-        while (!queue.isEmpty() && (event.getEventTime() - queue.peek().getEventTime() > 5000)) {
-            queue.remove().recycle();
-        }
-    }
-
     private PointerCoords[] getTempPointerCoordsWithMinSize(int size) {
         final int oldSize = (mTempPointerCoords != null) ? mTempPointerCoords.length : 0;
         if (oldSize < size) {
@@ -358,7 +270,7 @@
 
     private void transitionTo(State state) {
         if (DEBUG_STATE_TRANSITIONS) {
-            Slog.i(LOG_TAG,
+            Slog.i(mLogTag,
                     (State.nameOf(mCurrentState) + " -> " + State.nameOf(state)
                     + " at " + asList(copyOfRange(new RuntimeException().getStackTrace(), 1, 5)))
                     .replace(getClass().getName(), ""));
@@ -440,7 +352,7 @@
                 return true;
             }
             if (DEBUG_PANNING_SCALING) {
-                Slog.i(LOG_TAG, "Panned content by scrollX: " + distanceX
+                Slog.i(mLogTag, "Panned content by scrollX: " + distanceX
                         + " scrollY: " + distanceY);
             }
             mFullScreenMagnificationController.offsetMagnifiedRegion(mDisplayId, distanceX,
@@ -480,7 +392,7 @@
 
             final float pivotX = detector.getFocusX();
             final float pivotY = detector.getFocusY();
-            if (DEBUG_PANNING_SCALING) Slog.i(LOG_TAG, "Scaled content to: " + scale + "x");
+            if (DEBUG_PANNING_SCALING) Slog.i(mLogTag, "Scaled content to: " + scale + "x");
             mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false,
                     AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
             mListener.onMagnificationScaleChanged(mDisplayId, getMode());
@@ -945,7 +857,7 @@
 
         private void onTripleTap(MotionEvent up) {
             if (DEBUG_DETECTING) {
-                Slog.i(LOG_TAG, "onTripleTap(); delayed: "
+                Slog.i(mLogTag, "onTripleTap(); delayed: "
                         + MotionEventInfo.toString(mDelayedEventQueue));
             }
             clear();
@@ -965,7 +877,7 @@
 
         void transitionToViewportDraggingStateAndClear(MotionEvent down) {
 
-            if (DEBUG_DETECTING) Slog.i(LOG_TAG, "onTripleTapAndHold()");
+            if (DEBUG_DETECTING) Slog.i(mLogTag, "onTripleTapAndHold()");
             clear();
 
             mViewportDraggingState.mZoomedInBeforeDrag =
@@ -997,7 +909,7 @@
             if (mShortcutTriggered == state) {
                 return;
             }
-            if (DEBUG_DETECTING) Slog.i(LOG_TAG, "setShortcutTriggered(" + state + ")");
+            if (DEBUG_DETECTING) Slog.i(mLogTag, "setShortcutTriggered(" + state + ")");
 
             mShortcutTriggered = state;
             mFullScreenMagnificationController.setForceShowMagnifiableBounds(mDisplayId, state);
@@ -1030,7 +942,7 @@
     }
 
     private void zoomOn(float centerX, float centerY) {
-        if (DEBUG_DETECTING) Slog.i(LOG_TAG, "zoomOn(" + centerX + ", " + centerY + ")");
+        if (DEBUG_DETECTING) Slog.i(mLogTag, "zoomOn(" + centerX + ", " + centerY + ")");
 
         final float scale = MathUtils.constrain(
                 mFullScreenMagnificationController.getPersistedScale(),
@@ -1042,7 +954,7 @@
     }
 
     private void zoomOff() {
-        if (DEBUG_DETECTING) Slog.i(LOG_TAG, "zoomOff()");
+        if (DEBUG_DETECTING) Slog.i(mLogTag, "zoomOff()");
         mFullScreenMagnificationController.reset(mDisplayId, /* animate */ true);
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 6f81b5c..df88ceb 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -37,7 +37,8 @@
  * Handles all magnification controllers initialization, generic interactions
  * and magnification mode transition.
  */
-public class MagnificationController implements WindowMagnificationManager.Callback {
+public class MagnificationController implements WindowMagnificationManager.Callback,
+        MagnificationGestureHandler.ScaleChangedListener {
 
     private static final boolean DEBUG = false;
     private static final String TAG = "MagnificationController";
@@ -50,6 +51,7 @@
 
     private FullScreenMagnificationController mFullScreenMagnificationController;
     private WindowMagnificationManager mWindowMagnificationMgr;
+    private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
 
     /**
      * A callback to inform the magnification transition result.
@@ -82,10 +84,18 @@
     public void onPerformScaleAction(int displayId, float scale) {
         getWindowMagnificationMgr().setScale(displayId, scale);
         getWindowMagnificationMgr().persistScale(displayId);
-        mAms.onMagnificationScaleChanged(displayId,
+        onMagnificationScaleChanged(displayId,
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
     }
 
+    @Override
+    public void onMagnificationScaleChanged(int displayId, int mode) {
+        if (mMagnificationCapabilities != Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL) {
+            return;
+        }
+        getWindowMagnificationMgr().showMagnificationButton(displayId, mode);
+    }
+
     /**
      * Transitions to the target Magnification mode with current center of the magnification mode
      * if it is available.
@@ -182,6 +192,10 @@
         }
     }
 
+    public void setMagnificationCapabilities(int capabilities) {
+        mMagnificationCapabilities = capabilities;
+    }
+
     private DisableMagnificationCallback getDisableMagnificationEndRunnableLocked(
             int displayId) {
         return mMagnificationEndRunnableSparseArray.get(displayId);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
index d6f53d2..386d0bb 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
@@ -16,32 +16,127 @@
 
 package com.android.server.accessibility.magnification;
 
+import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
+
+import android.annotation.NonNull;
+import android.util.Log;
+import android.util.Slog;
+import android.view.MotionEvent;
+
 import com.android.server.accessibility.BaseEventStreamTransformation;
 
+import java.util.ArrayDeque;
+import java.util.Queue;
+
 /**
  * A base class that detects gestures and defines common methods for magnification.
  */
 public abstract class MagnificationGestureHandler extends BaseEventStreamTransformation {
 
-    protected final ScaleChangedListener mListener;
-
-    protected MagnificationGestureHandler(ScaleChangedListener listener) {
-        mListener = listener;
-    }
+    protected final String mLogTag = this.getClass().getSimpleName();
+    protected static final boolean DEBUG_ALL = Log.isLoggable("MagnificationGestureHandler",
+            Log.DEBUG);
+    protected static final boolean DEBUG_EVENT_STREAM = false | DEBUG_ALL;
+    private final Queue<MotionEvent> mDebugInputEventHistory;
+    private final Queue<MotionEvent> mDebugOutputEventHistory;
 
     /**
-     * Interface for listening to the magnification scaling gesture.
+     * The logical display id.
      */
+    protected final int mDisplayId;
+
+    /**
+     * {@code true} if this detector should be "triggerable" by some
+     * external shortcut invoking {@link #notifyShortcutTriggered},
+     * {@code false} if it should ignore such triggers.
+     */
+    protected final boolean mDetectShortcutTrigger;
+
+    /**
+     * {@code true} if this detector should detect and respond to triple-tap
+     * gestures for engaging and disengaging magnification,
+     * {@code false} if it should ignore such gestures
+     */
+    protected final boolean mDetectTripleTap;
+
+    /** Interface for listening to the magnification scaling gesture. */
     public interface ScaleChangedListener {
         /**
          * Called when the magnification scale is changed by users.
          *
          * @param displayId The logical display id
-         * @param mode  The magnification mode
+         * @param mode      The magnification mode
          */
         void onMagnificationScaleChanged(int displayId, int mode);
     }
 
+    protected final ScaleChangedListener mListener;
+
+    protected MagnificationGestureHandler(int displayId, boolean detectTripleTap,
+            boolean detectShortcutTrigger,
+            @NonNull ScaleChangedListener listener) {
+        mDisplayId = displayId;
+        mDetectTripleTap = detectTripleTap;
+        mDetectShortcutTrigger = detectShortcutTrigger;
+        mListener = listener;
+
+        mDebugInputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null;
+        mDebugOutputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null;
+    }
+
+    @Override
+    public final void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (DEBUG_ALL) {
+            Slog.i(mLogTag, "onMotionEvent(" + event + ")");
+        }
+        if (DEBUG_EVENT_STREAM) {
+            storeEventInto(mDebugInputEventHistory, event);
+        }
+        if (shouldDispatchTransformedEvent(event)) {
+            dispatchTransformedEvent(event, rawEvent, policyFlags);
+        } else {
+            onMotionEventInternal(event, rawEvent, policyFlags);
+        }
+    }
+
+    private boolean shouldDispatchTransformedEvent(MotionEvent event) {
+        if ((!mDetectTripleTap && !mDetectShortcutTrigger) || !event.isFromSource(
+                SOURCE_TOUCHSCREEN)) {
+            return true;
+        }
+        return false;
+    }
+
+    final void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent,
+            int policyFlags) {
+        if (DEBUG_EVENT_STREAM) {
+            storeEventInto(mDebugOutputEventHistory, event);
+            try {
+                super.onMotionEvent(event, rawEvent, policyFlags);
+                return;
+            } catch (Exception e) {
+                throw new RuntimeException(
+                        "Exception downstream following input events: " + mDebugInputEventHistory
+                                + "\nTransformed into output events: " + mDebugOutputEventHistory,
+                        e);
+            }
+        }
+        super.onMotionEvent(event, rawEvent, policyFlags);
+    }
+
+    private static void storeEventInto(Queue<MotionEvent> queue, MotionEvent event) {
+        queue.add(MotionEvent.obtain(event));
+        // Prune old events
+        while (!queue.isEmpty() && (event.getEventTime() - queue.peek().getEventTime() > 5000)) {
+            queue.remove().recycle();
+        }
+    }
+
+    /**
+     * Called when this MagnificationGestureHandler handles the motion event.
+     */
+    abstract void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags);
+
     /**
      * Called when the shortcut target is magnification.
      */
@@ -51,7 +146,6 @@
      * Indicates the magnification mode.
      *
      * @return the magnification mode of the handler
-     *
      * @see android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
      * @see android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
      */
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 7d6067c..7f26b27 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -27,7 +27,6 @@
 import android.content.Context;
 import android.graphics.Point;
 import android.provider.Settings;
-import android.util.Log;
 import android.util.MathUtils;
 import android.util.Slog;
 import android.view.Display;
@@ -38,9 +37,7 @@
 import com.android.server.accessibility.gestures.MultiTap;
 import com.android.server.accessibility.gestures.MultiTapAndHold;
 
-import java.util.ArrayDeque;
 import java.util.List;
-import java.util.Queue;
 
 /**
  * This class handles window magnification in response to touch events and shortcut.
@@ -64,12 +61,9 @@
  */
 @SuppressWarnings("WeakerAccess")
 public class WindowMagnificationGestureHandler extends MagnificationGestureHandler {
-    private static final String LOG_TAG = "WindowMagnificationGestureHandler";
 
-    private static final boolean DEBUG_ALL = Log.isLoggable(LOG_TAG, Log.DEBUG);
     private static final boolean DEBUG_STATE_TRANSITIONS = false | DEBUG_ALL;
     private static final boolean DEBUG_DETECTING = false | DEBUG_ALL;
-    private static final boolean DEBUG_EVENT_STREAM = false | DEBUG_ALL;
 
     //Ensure the range has consistency with FullScreenMagnificationGestureHandler.
     private static final float MIN_SCALE = 2.0f;
@@ -88,38 +82,26 @@
     @VisibleForTesting
     State mPreviousState;
 
-    final boolean mDetectShortcutTrigger;
-
     private MotionEventDispatcherDelegate mMotionEventDispatcherDelegate;
-    private final int mDisplayId;
     private final Context mContext;
     private final Point mTempPoint = new Point();
 
-    private final Queue<MotionEvent> mDebugOutputEventHistory;
-
-    /**
-     * @param context                Context for resolving various magnification-related resources
-     * @param windowMagnificationMgr The {@link WindowMagnificationManager}
-     * @param displayId              The logical display id.
-     */
     public WindowMagnificationGestureHandler(Context context,
             WindowMagnificationManager windowMagnificationMgr,
-            ScaleChangedListener listener, boolean detectTripleTap,
-            boolean detectShortcutTrigger, int displayId) {
-        super(listener);
+            ScaleChangedListener listener,
+            boolean detectTripleTap, boolean detectShortcutTrigger, int displayId) {
+        super(displayId, detectTripleTap, detectShortcutTrigger, listener);
         if (DEBUG_ALL) {
-            Slog.i(LOG_TAG,
+            Slog.i(mLogTag,
                     "WindowMagnificationGestureHandler() , displayId = " + displayId + ")");
         }
         mContext = context;
         mWindowMagnificationMgr = windowMagnificationMgr;
-        mDetectShortcutTrigger = detectShortcutTrigger;
-        mDisplayId = displayId;
         mMotionEventDispatcherDelegate = new MotionEventDispatcherDelegate(context,
-                (event, rawEvent, policyFlags) -> super.onMotionEvent(
-                        event, rawEvent, policyFlags));
+                (event, rawEvent, policyFlags) -> dispatchTransformedEvent(event, rawEvent,
+                        policyFlags));
         mDelegatingState = new DelegatingState(mMotionEventDispatcherDelegate);
-        mDetectingState = new DetectingState(context, detectTripleTap);
+        mDetectingState = new DetectingState(context, mDetectTripleTap);
         mObservePanningScalingState = new PanningScalingGestureState(
                 new PanningScalingHandler(context, MAX_SCALE, MIN_SCALE, true,
                         new PanningScalingHandler.MagnificationDelegate() {
@@ -142,24 +124,14 @@
                             }
                         }));
 
-        mDebugOutputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null;
-
         transitionTo(mDetectingState);
     }
 
     @Override
-    public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
-        if (DEBUG_ALL) {
-            Slog.i(LOG_TAG, "onMotionEvent(" + event + ")");
-        }
-        if (!event.isFromSource(SOURCE_TOUCHSCREEN)) {
-            dispatchTransformedEvent(event, rawEvent, policyFlags);
-            return;
-        } else {
-            // To keep InputEventConsistencyVerifiers within GestureDetectors happy.
-            mObservePanningScalingState.mPanningScalingHandler.onTouchEvent(event);
-            mCurrentState.onMotionEvent(event, rawEvent, policyFlags);
-        }
+    void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        // To keep InputEventConsistencyVerifiers within GestureDetectors happy.
+        mObservePanningScalingState.mPanningScalingHandler.onTouchEvent(event);
+        mCurrentState.onMotionEvent(event, rawEvent, policyFlags);
     }
 
     @Override
@@ -173,7 +145,7 @@
     @Override
     public void onDestroy() {
         if (DEBUG_ALL) {
-            Slog.i(LOG_TAG, "onDestroy(); delayed = "
+            Slog.i(mLogTag, "onDestroy(); delayed = "
                     + mDetectingState.toString());
         }
         mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true);
@@ -183,7 +155,7 @@
     @Override
     public void notifyShortcutTriggered() {
         if (DEBUG_ALL) {
-            Slog.i(LOG_TAG, "notifyShortcutTriggered():");
+            Slog.i(mLogTag, "notifyShortcutTriggered():");
         }
         if (!mDetectShortcutTrigger) {
             return;
@@ -205,7 +177,7 @@
 
     private void enableWindowMagnifier(float centerX, float centerY) {
         if (DEBUG_ALL) {
-            Slog.i(LOG_TAG, "enableWindowMagnifier :" + centerX + ", " + centerY);
+            Slog.i(mLogTag, "enableWindowMagnifier :" + centerX + ", " + centerY);
         }
 
         final float scale = MathUtils.constrain(
@@ -216,7 +188,7 @@
 
     private void disableWindowMagnifier() {
         if (DEBUG_ALL) {
-            Slog.i(LOG_TAG, "disableWindowMagnifier()");
+            Slog.i(mLogTag, "disableWindowMagnifier()");
         }
         mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, false);
     }
@@ -231,7 +203,7 @@
 
     private void onTripleTap(MotionEvent up) {
         if (DEBUG_DETECTING) {
-            Slog.i(LOG_TAG, "onTripleTap()");
+            Slog.i(mLogTag, "onTripleTap()");
         }
         toggleMagnification(up.getX(), up.getY());
     }
@@ -240,30 +212,6 @@
         transitionTo(mDetectingState);
     }
 
-    private void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent,
-            int policyFlags) {
-        if (DEBUG_EVENT_STREAM) {
-            storeEventInto(mDebugOutputEventHistory, event);
-            try {
-                super.onMotionEvent(event, rawEvent, policyFlags);
-            } catch (Exception e) {
-                throw new RuntimeException(
-                        "Exception downstream following input events: " + mDebugOutputEventHistory,
-                        e);
-            }
-        } else {
-            super.onMotionEvent(event, rawEvent, policyFlags);
-        }
-    }
-
-    private static void storeEventInto(Queue<MotionEvent> queue, MotionEvent event) {
-        queue.add(MotionEvent.obtain(event));
-        // Prune old events.
-        while (!queue.isEmpty() && (event.getEventTime() - queue.peek().getEventTime() > 5000)) {
-            queue.remove().recycle();
-        }
-    }
-
     /**
      * An interface to intercept the {@link MotionEvent} for gesture detection. The intercepted
      * events should be delivered to next {@link EventStreamTransformation} with {
@@ -293,7 +241,7 @@
 
     private void transitionTo(State state) {
         if (DEBUG_STATE_TRANSITIONS) {
-            Slog.i(LOG_TAG, "state transition: " + (State.nameOf(mCurrentState) + " -> "
+            Slog.i(mLogTag, "state transition: " + (State.nameOf(mCurrentState) + " -> "
                     + State.nameOf(state) + " at "
                     + asList(copyOfRange(new RuntimeException().getStackTrace(), 1, 5)))
                     .replace(getClass().getName(), ""));
@@ -451,10 +399,10 @@
                 List<MotionEventInfo> delayedEventQueue,
                 MotionEvent motionEvent) {
             if (DEBUG_DETECTING) {
-                Slog.d(LOG_TAG, "onGestureDetected : gesture = "
+                Slog.d(mLogTag, "onGestureDetected : gesture = "
                         + MagnificationGestureMatcher.gestureIdToString(
                         gestureId));
-                Slog.d(LOG_TAG,
+                Slog.d(mLogTag,
                         "onGestureDetected : delayedEventQueue = " + delayedEventQueue);
             }
             if (gestureId == MagnificationGestureMatcher.GESTURE_TWO_FINGER_DOWN
@@ -474,7 +422,7 @@
                 List<MotionEventInfo> delayedEventQueue,
                 MotionEvent motionEvent) {
             if (DEBUG_DETECTING) {
-                Slog.d(LOG_TAG,
+                Slog.d(mLogTag,
                         "onGestureCancelled : delayedEventQueue = " + delayedEventQueue);
             }
             mMotionEventDispatcherDelegate.sendDelayedMotionEvents(delayedEventQueue,
diff --git a/services/autofill/java/com/android/server/autofill/TEST_MAPPING b/services/autofill/java/com/android/server/autofill/TEST_MAPPING
new file mode 100644
index 0000000..cf058ad
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsAutoFillServiceTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index fec7ac0..614863d 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -188,6 +188,13 @@
     src: ":services.core.json.gz",
 }
 
+filegroup {
+    name: "services.core-sources-deviceconfig-interface",
+    srcs: [
+         "java/com/android/server/utils/DeviceConfigInterface.java"
+    ],
+}
+
 // TODO: Move connectivity service sources to independent directory.
 filegroup {
     name: "connectivity-service-srcs",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index e8ee18c..f0677a2 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -41,6 +41,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
@@ -6354,18 +6355,71 @@
         nai.declaredMetered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
     }
 
-    /** Propagates to |nc| the capabilities declared by the underlying networks of |nai|. */
-    private void mixInUnderlyingCapabilities(NetworkAgentInfo nai, NetworkCapabilities nc) {
-        Network[] underlyingNetworks = nai.declaredUnderlyingNetworks;
-        Network defaultNetwork = getNetwork(getDefaultNetwork());
+    /** Modifies |caps| based on the capabilities of the specified underlying networks. */
+    @VisibleForTesting
+    void applyUnderlyingCapabilities(@Nullable Network[] underlyingNetworks,
+            @NonNull NetworkCapabilities caps,  boolean declaredMetered) {
+        final Network defaultNetwork = getNetwork(getDefaultNetwork());
         if (underlyingNetworks == null && defaultNetwork != null) {
             // null underlying networks means to track the default.
             underlyingNetworks = new Network[] { defaultNetwork };
         }
+        int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN };
+        int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
+        int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
+        boolean metered = declaredMetered; // metered if any underlying is metered, or agentMetered
+        boolean roaming = false; // roaming if any underlying is roaming
+        boolean congested = false; // congested if any underlying is congested
+        boolean suspended = true; // suspended if all underlying are suspended
 
-        // TODO(b/124469351): Get capabilities directly from ConnectivityService instead.
-        final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
-        Vpn.applyUnderlyingCapabilities(cm, underlyingNetworks, nc, nai.declaredMetered);
+        boolean hadUnderlyingNetworks = false;
+        if (null != underlyingNetworks) {
+            for (Network underlyingNetwork : underlyingNetworks) {
+                final NetworkAgentInfo underlying =
+                        getNetworkAgentInfoForNetwork(underlyingNetwork);
+                if (underlying == null) continue;
+
+                final NetworkCapabilities underlyingCaps = underlying.networkCapabilities;
+                hadUnderlyingNetworks = true;
+                for (int underlyingType : underlyingCaps.getTransportTypes()) {
+                    transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType);
+                }
+
+                // Merge capabilities of this underlying network. For bandwidth, assume the
+                // worst case.
+                downKbps = NetworkCapabilities.minBandwidth(downKbps,
+                        underlyingCaps.getLinkDownstreamBandwidthKbps());
+                upKbps = NetworkCapabilities.minBandwidth(upKbps,
+                        underlyingCaps.getLinkUpstreamBandwidthKbps());
+                // If this underlying network is metered, the VPN is metered (it may cost money
+                // to send packets on this network).
+                metered |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_METERED);
+                // If this underlying network is roaming, the VPN is roaming (the billing structure
+                // is different than the usual, local one).
+                roaming |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+                // If this underlying network is congested, the VPN is congested (the current
+                // condition of the network affects the performance of this network).
+                congested |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_CONGESTED);
+                // If this network is not suspended, the VPN is not suspended (the VPN
+                // is able to transfer some data).
+                suspended &= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+            }
+        }
+        if (!hadUnderlyingNetworks) {
+            // No idea what the underlying networks are; assume reasonable defaults
+            metered = true;
+            roaming = false;
+            congested = false;
+            suspended = false;
+        }
+
+        caps.setTransportTypes(transportTypes);
+        caps.setLinkDownstreamBandwidthKbps(downKbps);
+        caps.setLinkUpstreamBandwidthKbps(upKbps);
+        caps.setCapability(NET_CAPABILITY_NOT_METERED, !metered);
+        caps.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming);
+        caps.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested);
+        caps.setCapability(NET_CAPABILITY_NOT_SUSPENDED, !suspended);
     }
 
     /**
@@ -6422,7 +6476,7 @@
         }
 
         if (nai.supportsUnderlyingNetworks()) {
-            mixInUnderlyingCapabilities(nai, newNc);
+            applyUnderlyingCapabilities(nai.declaredUnderlyingNetworks, newNc, nai.declaredMetered);
         }
 
         return newNc;
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index f3c5fd8..9bf63cb 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -1043,7 +1043,7 @@
                 TypedXmlSerializer out = Xml.resolveSerializer(stream);
                 out.startDocument(null, true);
                 out.startTag(null, TAG_PACKAGE_WATCHDOG);
-                out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
+                out.attributeInt(null, ATTR_VERSION, DB_VERSION);
                 for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
                     mAllObservers.valueAt(oIndex).writeLocked(out);
                 }
@@ -1107,7 +1107,7 @@
          * Does not persist any package failure thresholds.
          */
         @GuardedBy("mLock")
-        public boolean writeLocked(XmlSerializer out) {
+        public boolean writeLocked(TypedXmlSerializer out) {
             try {
                 out.startTag(null, TAG_OBSERVER);
                 out.attribute(null, ATTR_NAME, name);
@@ -1225,7 +1225,7 @@
          * #loadFromFile which in turn is only called on construction of the
          * singleton PackageWatchdog.
          **/
-        public static ObserverInternal read(XmlPullParser parser, PackageWatchdog watchdog) {
+        public static ObserverInternal read(TypedXmlPullParser parser, PackageWatchdog watchdog) {
             String observerName = null;
             if (TAG_OBSERVER.equals(parser.getName())) {
                 observerName = parser.getAttributeValue(null, ATTR_NAME);
@@ -1240,14 +1240,14 @@
                 while (XmlUtils.nextElementWithin(parser, innerDepth)) {
                     if (TAG_PACKAGE.equals(parser.getName())) {
                         try {
-                            String packageName = parser.getAttributeValue(null, ATTR_NAME);
-                            long duration = Long.parseLong(
-                                    parser.getAttributeValue(null, ATTR_DURATION));
-                            long healthCheckDuration = Long.parseLong(
-                                    parser.getAttributeValue(null,
-                                            ATTR_EXPLICIT_HEALTH_CHECK_DURATION));
-                            boolean hasPassedHealthCheck = Boolean.parseBoolean(
-                                    parser.getAttributeValue(null, ATTR_PASSED_HEALTH_CHECK));
+                            String packageName = parser.getAttributeValue(
+                                    null, ATTR_NAME);
+                            long duration = parser.getAttributeLong(
+                                    null, ATTR_DURATION);
+                            long healthCheckDuration = parser.getAttributeLong(
+                                    null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION);
+                            boolean hasPassedHealthCheck = parser.getAttributeBoolean(
+                                    null, ATTR_PASSED_HEALTH_CHECK, false);
                             MonitoredPackage pkg = watchdog.newMonitoredPackage(packageName,
                                     duration, healthCheckDuration, hasPassedHealthCheck);
                             if (pkg != null) {
@@ -1364,14 +1364,12 @@
 
         /** Writes the salient fields to disk using {@code out}. */
         @GuardedBy("mLock")
-        public void writeLocked(XmlSerializer out) throws IOException {
+        public void writeLocked(TypedXmlSerializer out) throws IOException {
             out.startTag(null, TAG_PACKAGE);
             out.attribute(null, ATTR_NAME, getName());
-            out.attribute(null, ATTR_DURATION, String.valueOf(mDurationMs));
-            out.attribute(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION,
-                    String.valueOf(mHealthCheckDurationMs));
-            out.attribute(null, ATTR_PASSED_HEALTH_CHECK,
-                    String.valueOf(mHasPassedHealthCheck));
+            out.attributeLong(null, ATTR_DURATION, mDurationMs);
+            out.attributeLong(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION, mHealthCheckDurationMs);
+            out.attributeBoolean(null, ATTR_PASSED_HEALTH_CHECK, mHasPassedHealthCheck);
             out.endTag(null, TAG_PACKAGE);
         }
 
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 2455e76..d30e9fb 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -107,7 +107,7 @@
                     TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream);
                     serializer.startDocument(null, true);
                     serializer.startTag(null, XML_TAG_SENSOR_PRIVACY);
-                    serializer.attribute(null, XML_ATTRIBUTE_ENABLED, String.valueOf(enable));
+                    serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, enable);
                     serializer.endTag(null, XML_TAG_SENSOR_PRIVACY);
                     serializer.endDocument();
                     mAtomicFile.finishWrite(outputStream);
@@ -180,7 +180,7 @@
                     TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream);
                     serializer.startDocument(null, true);
                     serializer.startTag(null, XML_TAG_SENSOR_PRIVACY);
-                    serializer.attribute(null, XML_ATTRIBUTE_ENABLED, String.valueOf(mEnabled));
+                    serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, mEnabled);
                     serializer.endTag(null, XML_TAG_SENSOR_PRIVACY);
                     serializer.endDocument();
                     mAtomicFile.finishWrite(outputStream);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 5a404f0..dbd27af4 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -39,11 +39,7 @@
 import static android.os.storage.OnObbStateChangeListener.MOUNTED;
 import static android.os.storage.OnObbStateChangeListener.UNMOUNTED;
 
-import static com.android.internal.util.XmlUtils.readIntAttribute;
-import static com.android.internal.util.XmlUtils.readLongAttribute;
 import static com.android.internal.util.XmlUtils.readStringAttribute;
-import static com.android.internal.util.XmlUtils.writeIntAttribute;
-import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -158,9 +154,7 @@
 import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
 
-import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -1318,12 +1312,13 @@
                     final int oldState = vol.state;
                     final int newState = state;
                     vol.state = newState;
+                    final VolumeInfo vInfo = new VolumeInfo(vol);
                     final SomeArgs args = SomeArgs.obtain();
-                    args.arg1 = vol;
+                    args.arg1 = vInfo;
                     args.arg2 = oldState;
                     args.arg3 = newState;
                     mHandler.obtainMessage(H_VOLUME_STATE_CHANGED, args).sendToTarget();
-                    onVolumeStateChangedLocked(vol, oldState, newState);
+                    onVolumeStateChangedLocked(vInfo, oldState, newState);
                 }
             }
         }
@@ -2000,7 +1995,7 @@
                 if (type == START_TAG) {
                     final String tag = in.getName();
                     if (TAG_VOLUMES.equals(tag)) {
-                        final int version = readIntAttribute(in, ATTR_VERSION, VERSION_INIT);
+                        final int version = in.getAttributeInt(null, ATTR_VERSION, VERSION_INIT);
                         final boolean primaryPhysical = SystemProperties.getBoolean(
                                 StorageManager.PROP_PRIMARY_PHYSICAL, false);
                         final boolean validAttr = (version >= VERSION_FIX_PRIMARY)
@@ -2035,7 +2030,7 @@
             TypedXmlSerializer out = Xml.resolveSerializer(fos);
             out.startDocument(null, true);
             out.startTag(null, TAG_VOLUMES);
-            writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY);
+            out.attributeInt(null, ATTR_VERSION, VERSION_FIX_PRIMARY);
             writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid);
             final int size = mRecords.size();
             for (int i = 0; i < size; i++) {
@@ -2053,31 +2048,33 @@
         }
     }
 
-    public static VolumeRecord readVolumeRecord(XmlPullParser in) throws IOException {
-        final int type = readIntAttribute(in, ATTR_TYPE);
+    public static VolumeRecord readVolumeRecord(TypedXmlPullParser in)
+            throws IOException, XmlPullParserException {
+        final int type = in.getAttributeInt(null, ATTR_TYPE);
         final String fsUuid = readStringAttribute(in, ATTR_FS_UUID);
         final VolumeRecord meta = new VolumeRecord(type, fsUuid);
         meta.partGuid = readStringAttribute(in, ATTR_PART_GUID);
         meta.nickname = readStringAttribute(in, ATTR_NICKNAME);
-        meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS);
-        meta.createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS, 0);
-        meta.lastSeenMillis = readLongAttribute(in, ATTR_LAST_SEEN_MILLIS, 0);
-        meta.lastTrimMillis = readLongAttribute(in, ATTR_LAST_TRIM_MILLIS, 0);
-        meta.lastBenchMillis = readLongAttribute(in, ATTR_LAST_BENCH_MILLIS, 0);
+        meta.userFlags = in.getAttributeInt(null, ATTR_USER_FLAGS);
+        meta.createdMillis = in.getAttributeLong(null, ATTR_CREATED_MILLIS, 0);
+        meta.lastSeenMillis = in.getAttributeLong(null, ATTR_LAST_SEEN_MILLIS, 0);
+        meta.lastTrimMillis = in.getAttributeLong(null, ATTR_LAST_TRIM_MILLIS, 0);
+        meta.lastBenchMillis = in.getAttributeLong(null, ATTR_LAST_BENCH_MILLIS, 0);
         return meta;
     }
 
-    public static void writeVolumeRecord(XmlSerializer out, VolumeRecord rec) throws IOException {
+    public static void writeVolumeRecord(TypedXmlSerializer out, VolumeRecord rec)
+            throws IOException {
         out.startTag(null, TAG_VOLUME);
-        writeIntAttribute(out, ATTR_TYPE, rec.type);
+        out.attributeInt(null, ATTR_TYPE, rec.type);
         writeStringAttribute(out, ATTR_FS_UUID, rec.fsUuid);
         writeStringAttribute(out, ATTR_PART_GUID, rec.partGuid);
         writeStringAttribute(out, ATTR_NICKNAME, rec.nickname);
-        writeIntAttribute(out, ATTR_USER_FLAGS, rec.userFlags);
-        writeLongAttribute(out, ATTR_CREATED_MILLIS, rec.createdMillis);
-        writeLongAttribute(out, ATTR_LAST_SEEN_MILLIS, rec.lastSeenMillis);
-        writeLongAttribute(out, ATTR_LAST_TRIM_MILLIS, rec.lastTrimMillis);
-        writeLongAttribute(out, ATTR_LAST_BENCH_MILLIS, rec.lastBenchMillis);
+        out.attributeInt(null, ATTR_USER_FLAGS, rec.userFlags);
+        out.attributeLong(null, ATTR_CREATED_MILLIS, rec.createdMillis);
+        out.attributeLong(null, ATTR_LAST_SEEN_MILLIS, rec.lastSeenMillis);
+        out.attributeLong(null, ATTR_LAST_TRIM_MILLIS, rec.lastTrimMillis);
+        out.attributeLong(null, ATTR_LAST_BENCH_MILLIS, rec.lastBenchMillis);
         out.endTag(null, TAG_VOLUME);
     }
 
diff --git a/services/core/java/com/android/server/SystemUpdateManagerService.java b/services/core/java/com/android/server/SystemUpdateManagerService.java
index 61a7d00..fcba9b5 100644
--- a/services/core/java/com/android/server/SystemUpdateManagerService.java
+++ b/services/core/java/com/android/server/SystemUpdateManagerService.java
@@ -201,7 +201,7 @@
 
     // Performs I/O work only, without validating the loaded info.
     @Nullable
-    private PersistableBundle readInfoFileLocked(XmlPullParser parser)
+    private PersistableBundle readInfoFileLocked(TypedXmlPullParser parser)
             throws XmlPullParserException, IOException {
         int type;
         while ((type = parser.next()) != END_DOCUMENT) {
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index d6bd5a1..a45466d 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -29,9 +29,9 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkProvider;
 import android.net.RouteInfo;
 import android.net.StringNetworkSpecifier;
 import android.net.TestNetworkInterface;
@@ -62,7 +62,8 @@
 /** @hide */
 class TestNetworkService extends ITestNetworkManager.Stub {
     @NonNull private static final String TAG = TestNetworkService.class.getSimpleName();
-    @NonNull private static final String TEST_NETWORK_TYPE = "TEST_NETWORK";
+    @NonNull private static final String TEST_NETWORK_LOGTAG = "TestNetworkAgent";
+    @NonNull private static final String TEST_NETWORK_PROVIDER_NAME = TAG;
     @NonNull private static final AtomicInteger sTestTunIndex = new AtomicInteger();
 
     @NonNull private final Context mContext;
@@ -72,6 +73,9 @@
     @NonNull private final HandlerThread mHandlerThread;
     @NonNull private final Handler mHandler;
 
+    @NonNull private final ConnectivityManager mCm;
+    @NonNull private final NetworkProvider mNetworkProvider;
+
     // Native method stubs
     private static native int jniCreateTunTap(boolean isTun, @NonNull String iface);
 
@@ -85,6 +89,10 @@
         mContext = Objects.requireNonNull(context, "missing Context");
         mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService");
         mNetd = Objects.requireNonNull(NetdService.getInstance(), "could not get netd instance");
+        mCm = mContext.getSystemService(ConnectivityManager.class);
+        mNetworkProvider = new NetworkProvider(mContext, mHandler.getLooper(),
+                TEST_NETWORK_PROVIDER_NAME);
+        mCm.registerNetworkProvider(mNetworkProvider);
     }
 
     /**
@@ -150,9 +158,6 @@
         private static final int NETWORK_SCORE = 1; // Use a low, non-zero score.
 
         private final int mUid;
-        @NonNull private final NetworkInfo mNi;
-        @NonNull private final NetworkCapabilities mNc;
-        @NonNull private final LinkProperties mLp;
 
         @GuardedBy("mBinderLock")
         @NonNull
@@ -161,20 +166,18 @@
         @NonNull private final Object mBinderLock = new Object();
 
         private TestNetworkAgent(
-                @NonNull Looper looper,
                 @NonNull Context context,
-                @NonNull NetworkInfo ni,
+                @NonNull Looper looper,
+                @NonNull NetworkAgentConfig config,
                 @NonNull NetworkCapabilities nc,
                 @NonNull LinkProperties lp,
                 int uid,
-                @NonNull IBinder binder)
+                @NonNull IBinder binder,
+                @NonNull NetworkProvider np)
                 throws RemoteException {
-            super(looper, context, TEST_NETWORK_TYPE, ni, nc, lp, NETWORK_SCORE);
+            super(context, looper, TEST_NETWORK_LOGTAG, nc, lp, NETWORK_SCORE, config, np);
 
             mUid = uid;
-            mNi = ni;
-            mNc = nc;
-            mLp = lp;
 
             synchronized (mBinderLock) {
                 mBinder = binder; // Binder null-checks in create()
@@ -203,9 +206,7 @@
         }
 
         private void teardown() {
-            mNi.setDetailedState(DetailedState.DISCONNECTED, null, null);
-            mNi.setIsAvailable(false);
-            sendNetworkInfo(mNi);
+            unregister();
 
             // Synchronize on mBinderLock to ensure that unlinkToDeath is never called more than
             // once (otherwise it could throw an exception)
@@ -238,11 +239,6 @@
         Objects.requireNonNull(context, "missing Context");
         // iface and binder validity checked by caller
 
-        // Build network info with special testing type
-        NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_TEST, 0, TEST_NETWORK_TYPE, "");
-        ni.setDetailedState(DetailedState.CONNECTED, null, null);
-        ni.setIsAvailable(true);
-
         // Build narrow set of NetworkCapabilities, useful only for testing
         NetworkCapabilities nc = new NetworkCapabilities();
         nc.clearAll(); // Remove default capabilities.
@@ -290,7 +286,12 @@
             lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null, iface));
         }
 
-        return new TestNetworkAgent(looper, context, ni, nc, lp, callingUid, binder);
+        final TestNetworkAgent agent = new TestNetworkAgent(context, looper,
+                new NetworkAgentConfig.Builder().build(), nc, lp, callingUid, binder,
+                mNetworkProvider);
+        agent.register();
+        agent.markConnected();
+        return agent;
     }
 
     /**
diff --git a/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java b/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java
index 1c77a7f..b379b5d 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java
@@ -23,25 +23,21 @@
 import android.annotation.NonNull;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.Signature;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.PackageUtils;
 import android.util.Pair;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
-import com.android.server.accounts.AccountsDb.DeDatabaseHelper;
 
-import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -163,7 +159,7 @@
                 }
                 try {
                     ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
-                    final XmlSerializer serializer = new FastXmlSerializer();
+                    final TypedXmlSerializer serializer = Xml.newFastSerializer();
                     serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
                     serializer.startDocument(null, true);
                     serializer.startTag(null, TAG_PERMISSIONS);
@@ -216,7 +212,7 @@
     public void restoreAccountAccessPermissions(byte[] data, int userId) {
         try {
             ByteArrayInputStream dataStream = new ByteArrayInputStream(data);
-            XmlPullParser parser = Xml.newPullParser();
+            TypedXmlPullParser parser = Xml.newFastPullParser();
             parser.setInput(dataStream, StandardCharsets.UTF_8.name());
             PackageManager packageManager = mAccountManagerService.mContext.getPackageManager();
 
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index f59af76..d99b195 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -1947,8 +1947,7 @@
                                 + tagName);
                         return keyMap;
                     }
-                    int keystoreVersion = Integer.parseInt(
-                            parser.getAttributeValue(null, XML_ATTRIBUTE_VERSION));
+                    int keystoreVersion = parser.getAttributeInt(null, XML_ATTRIBUTE_VERSION);
                     if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) {
                         Slog.e(TAG, "Keystore version=" + keystoreVersion
                                 + " not supported (max_supported="
@@ -2068,8 +2067,7 @@
                                 + tagName);
                         return trustedNetworks;
                     }
-                    int keystoreVersion = Integer.parseInt(
-                            parser.getAttributeValue(null, XML_ATTRIBUTE_VERSION));
+                    int keystoreVersion = parser.getAttributeInt(null, XML_ATTRIBUTE_VERSION);
                     if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) {
                         Slog.e(TAG, "Keystore version=" + keystoreVersion
                                 + " not supported (max_supported="
@@ -2148,7 +2146,7 @@
                 serializer.startDocument(null, true);
 
                 serializer.startTag(null, XML_KEYSTORE_START_TAG);
-                serializer.attribute(null, XML_ATTRIBUTE_VERSION, String.valueOf(KEYSTORE_VERSION));
+                serializer.attributeInt(null, XML_ATTRIBUTE_VERSION, KEYSTORE_VERSION);
                 for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) {
                     serializer.startTag(null, XML_TAG_ADB_KEY);
                     serializer.attribute(null, XML_ATTRIBUTE_KEY, keyEntry.getKey());
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 28afcbb..7299e81 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -88,6 +88,8 @@
         DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_RUNTIME_NATIVE,
         DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
+        DeviceConfig.NAMESPACE_STATSD_NATIVE,
+        DeviceConfig.NAMESPACE_STATSD_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
     };
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index fa9d100..d4e2d27 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -4265,10 +4265,7 @@
                         throw new IllegalStateException("no start tag found");
                     }
 
-                    final String versionString = parser.getAttributeValue(null, "v");
-                    if (versionString != null) {
-                        oldVersion = Integer.parseInt(versionString);
-                    }
+                    oldVersion = parser.getAttributeInt(null, "v", NO_VERSION);
 
                     int outerDepth = parser.getDepth();
                     while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -4368,9 +4365,9 @@
         scheduleFastWriteLocked();
     }
 
-    private void readUidOps(XmlPullParser parser) throws NumberFormatException,
+    private void readUidOps(TypedXmlPullParser parser) throws NumberFormatException,
             XmlPullParserException, IOException {
-        final int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
+        final int uid = parser.getAttributeInt(null, "n");
         int outerDepth = parser.getDepth();
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -4381,8 +4378,8 @@
 
             String tagName = parser.getName();
             if (tagName.equals("op")) {
-                final int code = Integer.parseInt(parser.getAttributeValue(null, "n"));
-                final int mode = Integer.parseInt(parser.getAttributeValue(null, "m"));
+                final int code = parser.getAttributeInt(null, "n");
+                final int mode = parser.getAttributeInt(null, "m");
                 setUidMode(code, uid, mode);
             } else {
                 Slog.w(TAG, "Unknown element under <uid-ops>: "
@@ -4392,7 +4389,7 @@
         }
     }
 
-    private void readPackage(XmlPullParser parser)
+    private void readPackage(TypedXmlPullParser parser)
             throws NumberFormatException, XmlPullParserException, IOException {
         String pkgName = parser.getAttributeValue(null, "n");
         int outerDepth = parser.getDepth();
@@ -4414,9 +4411,9 @@
         }
     }
 
-    private void readUid(XmlPullParser parser, String pkgName)
+    private void readUid(TypedXmlPullParser parser, String pkgName)
             throws NumberFormatException, XmlPullParserException, IOException {
-        int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
+        int uid = parser.getAttributeInt(null, "n");
         final UidState uidState = getUidStateLocked(uid, true);
         int outerDepth = parser.getDepth();
         int type;
@@ -4437,19 +4434,20 @@
         uidState.evalForegroundOps(mOpModeWatchers);
     }
 
-    private void readAttributionOp(XmlPullParser parser, @NonNull Op parent,
-            @Nullable String attribution) throws NumberFormatException, IOException {
+    private void readAttributionOp(TypedXmlPullParser parser, @NonNull Op parent,
+            @Nullable String attribution)
+            throws NumberFormatException, IOException, XmlPullParserException {
         final AttributedOp attributedOp = parent.getOrCreateAttribution(parent, attribution);
 
-        final long key = XmlUtils.readLongAttribute(parser, "n");
+        final long key = parser.getAttributeLong(null, "n");
         final int uidState = extractUidStateFromKey(key);
         final int opFlags = extractFlagsFromKey(key);
 
-        final long accessTime = XmlUtils.readLongAttribute(parser, "t", 0);
-        final long rejectTime = XmlUtils.readLongAttribute(parser, "r", 0);
-        final long accessDuration = XmlUtils.readLongAttribute(parser, "d", -1);
+        final long accessTime = parser.getAttributeLong(null, "t", 0);
+        final long rejectTime = parser.getAttributeLong(null, "r", 0);
+        final long accessDuration = parser.getAttributeLong(null, "d", -1);
         final String proxyPkg = XmlUtils.readStringAttribute(parser, "pp");
-        final int proxyUid = XmlUtils.readIntAttribute(parser, "pu", Process.INVALID_UID);
+        final int proxyUid = parser.getAttributeInt(null, "pu", Process.INVALID_UID);
         final String proxyAttributionTag = XmlUtils.readStringAttribute(parser, "pc");
 
         if (accessTime > 0) {
@@ -4461,14 +4459,13 @@
         }
     }
 
-    private void readOp(XmlPullParser parser, @NonNull UidState uidState, @NonNull String pkgName)
-            throws NumberFormatException,
-        XmlPullParserException, IOException {
-        int opCode = Integer.parseInt(parser.getAttributeValue(null, "n"));
+    private void readOp(TypedXmlPullParser parser,
+            @NonNull UidState uidState, @NonNull String pkgName)
+            throws NumberFormatException, XmlPullParserException, IOException {
+        int opCode = parser.getAttributeInt(null, "n");
         Op op = new Op(uidState, pkgName, opCode, uidState.uid);
 
-        final int mode = XmlUtils.readIntAttribute(parser, "m",
-                AppOpsManager.opToDefaultMode(op.op));
+        final int mode = parser.getAttributeInt(null, "m", AppOpsManager.opToDefaultMode(op.op));
         op.mode = mode;
 
         int outerDepth = parser.getDepth();
@@ -4515,7 +4512,7 @@
                 TypedXmlSerializer out = Xml.resolveSerializer(stream);
                 out.startDocument(null, true);
                 out.startTag(null, "app-ops");
-                out.attribute(null, "v", String.valueOf(CURRENT_VERSION));
+                out.attributeInt(null, "v", CURRENT_VERSION);
 
                 SparseArray<SparseIntArray> uidStatesClone;
                 synchronized (this) {
@@ -4545,15 +4542,14 @@
                     SparseIntArray opModes = uidStatesClone.valueAt(uidStateNum);
                     if (opModes != null && opModes.size() > 0) {
                         out.startTag(null, "uid");
-                        out.attribute(null, "n",
-                                Integer.toString(uidStatesClone.keyAt(uidStateNum)));
+                        out.attributeInt(null, "n", uidStatesClone.keyAt(uidStateNum));
                         final int opCount = opModes.size();
                         for (int opCountNum = 0; opCountNum < opCount; opCountNum++) {
                             final int op = opModes.keyAt(opCountNum);
                             final int mode = opModes.valueAt(opCountNum);
                             out.startTag(null, "op");
-                            out.attribute(null, "n", Integer.toString(op));
-                            out.attribute(null, "m", Integer.toString(mode));
+                            out.attributeInt(null, "n", op);
+                            out.attributeInt(null, "m", mode);
                             out.endTag(null, "op");
                         }
                         out.endTag(null, "uid");
@@ -4573,14 +4569,14 @@
                             out.attribute(null, "n", lastPkg);
                         }
                         out.startTag(null, "uid");
-                        out.attribute(null, "n", Integer.toString(pkg.getUid()));
+                        out.attributeInt(null, "n", pkg.getUid());
                         List<AppOpsManager.OpEntry> ops = pkg.getOps();
                         for (int j=0; j<ops.size(); j++) {
                             AppOpsManager.OpEntry op = ops.get(j);
                             out.startTag(null, "op");
-                            out.attribute(null, "n", Integer.toString(op.getOp()));
+                            out.attributeInt(null, "n", op.getOp());
                             if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) {
-                                out.attribute(null, "m", Integer.toString(op.getMode()));
+                                out.attributeInt(null, "m", op.getMode());
                             }
 
                             for (String attributionTag : op.getAttributedOpEntries().keySet()) {
@@ -4624,15 +4620,15 @@
                                     if (attributionTag != null) {
                                         out.attribute(null, "id", attributionTag);
                                     }
-                                    out.attribute(null, "n", Long.toString(key));
+                                    out.attributeLong(null, "n", key);
                                     if (accessTime > 0) {
-                                        out.attribute(null, "t", Long.toString(accessTime));
+                                        out.attributeLong(null, "t", accessTime);
                                     }
                                     if (rejectTime > 0) {
-                                        out.attribute(null, "r", Long.toString(rejectTime));
+                                        out.attributeLong(null, "r", rejectTime);
                                     }
                                     if (accessDuration > 0) {
-                                        out.attribute(null, "d", Long.toString(accessDuration));
+                                        out.attributeLong(null, "d", accessDuration);
                                     }
                                     if (proxyPkg != null) {
                                         out.attribute(null, "pp", proxyPkg);
@@ -4641,7 +4637,7 @@
                                         out.attribute(null, "pc", proxyAttributionTag);
                                     }
                                     if (proxyUid >= 0) {
-                                        out.attribute(null, "pu", Integer.toString(proxyUid));
+                                        out.attributeInt(null, "pu", proxyUid);
                                     }
                                     out.endTag(null, "st");
                                 }
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index f49b5dc..676fcd0 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -49,6 +49,8 @@
 import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.TimeUtils;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
@@ -1184,19 +1186,18 @@
             }
             List<HistoricalOps> allOps = null;
             try (FileInputStream stream = new FileInputStream(file)) {
-                final XmlPullParser parser = Xml.newPullParser();
-                parser.setInput(stream, StandardCharsets.UTF_8.name());
+                final TypedXmlPullParser parser = Xml.resolvePullParser(stream);
                 XmlUtils.beginDocument(parser, TAG_HISTORY);
 
                 // We haven't released version 1 and have more detailed
                 // accounting - just nuke the current state
-                final int version = XmlUtils.readIntAttribute(parser, ATTR_VERSION);
+                final int version = parser.getAttributeInt(null, ATTR_VERSION);
                 if (CURRENT_VERSION == 2 && version < CURRENT_VERSION) {
                     throw new IllegalStateException("Dropping unsupported history "
                             + "version 1 for file:" + file);
                 }
 
-                final long overflowMillis = XmlUtils.readLongAttribute(parser, ATTR_OVERFLOW, 0);
+                final long overflowMillis = parser.getAttributeLong(null, ATTR_OVERFLOW, 0);
                 final int depth = parser.getDepth();
                 while (XmlUtils.nextElementWithin(parser, depth)) {
                     if (TAG_OPS.equals(parser.getName())) {
@@ -1235,15 +1236,16 @@
         }
 
         private @Nullable HistoricalOps readeHistoricalOpsDLocked(
-                @NonNull XmlPullParser parser, int filterUid, @Nullable String filterPackageName,
+                @NonNull TypedXmlPullParser parser, int filterUid,
+                @Nullable String filterPackageName,
                 @Nullable String filterAttributionTag, @Nullable String[] filterOpNames,
                 @HistoricalOpsRequestFilter int filter, long filterBeginTimeMillis,
                 long filterEndTimeMillis, @OpFlags int filterFlags,
                 @Nullable long[] cumulativeOverflowMillis)
                 throws IOException, XmlPullParserException {
-            final long beginTimeMillis = XmlUtils.readLongAttribute(parser, ATTR_BEGIN_TIME, 0)
+            final long beginTimeMillis = parser.getAttributeLong(null, ATTR_BEGIN_TIME, 0)
                     + (cumulativeOverflowMillis != null ? cumulativeOverflowMillis[0] : 0);
-            final long endTimeMillis = XmlUtils.readLongAttribute(parser, ATTR_END_TIME, 0)
+            final long endTimeMillis = parser.getAttributeLong(null, ATTR_END_TIME, 0)
                     + (cumulativeOverflowMillis != null ? cumulativeOverflowMillis[0] : 0);
             // Keep reading as subsequent records may start matching
             if (filterEndTimeMillis < beginTimeMillis) {
@@ -1280,12 +1282,12 @@
         }
 
         private @Nullable HistoricalOps readHistoricalUidOpsDLocked(
-                @Nullable HistoricalOps ops, @NonNull XmlPullParser parser, int filterUid,
+                @Nullable HistoricalOps ops, @NonNull TypedXmlPullParser parser, int filterUid,
                 @Nullable String filterPackageName, @Nullable String filterAttributionTag,
                 @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
                 @OpFlags int filterFlags, double filterScale)
                 throws IOException, XmlPullParserException {
-            final int uid = XmlUtils.readIntAttribute(parser, ATTR_NAME);
+            final int uid = parser.getAttributeInt(null, ATTR_NAME);
             if ((filter & FILTER_BY_UID) != 0 && filterUid != uid) {
                 XmlUtils.skipCurrentTag(parser);
                 return null;
@@ -1305,7 +1307,7 @@
         }
 
         private @Nullable HistoricalOps readHistoricalPackageOpsDLocked(
-                @Nullable HistoricalOps ops, int uid, @NonNull XmlPullParser parser,
+                @Nullable HistoricalOps ops, int uid, @NonNull TypedXmlPullParser parser,
                 @Nullable String filterPackageName, @Nullable String filterAttributionTag,
                 @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
                 @OpFlags int filterFlags, double filterScale)
@@ -1331,7 +1333,7 @@
 
         private @Nullable HistoricalOps readHistoricalAttributionOpsDLocked(
                 @Nullable HistoricalOps ops, int uid, String packageName,
-                @NonNull XmlPullParser parser, @Nullable String filterAttributionTag,
+                @NonNull TypedXmlPullParser parser, @Nullable String filterAttributionTag,
                 @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
                 @OpFlags int filterFlags, double filterScale)
                 throws IOException, XmlPullParserException {
@@ -1357,11 +1359,11 @@
 
         private @Nullable HistoricalOps readHistoricalOpDLocked(@Nullable HistoricalOps ops,
                 int uid, @NonNull String packageName, @Nullable String attributionTag,
-                @NonNull XmlPullParser parser, @Nullable String[] filterOpNames,
+                @NonNull TypedXmlPullParser parser, @Nullable String[] filterOpNames,
                 @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags,
                 double filterScale)
                 throws IOException, XmlPullParserException {
-            final int op = XmlUtils.readIntAttribute(parser, ATTR_NAME);
+            final int op = parser.getAttributeInt(null, ATTR_NAME);
             if ((filter & FILTER_BY_OP_NAMES) != 0 && !ArrayUtils.contains(filterOpNames,
                     AppOpsManager.opToPublicName(op))) {
                 XmlUtils.skipCurrentTag(parser);
@@ -1382,15 +1384,15 @@
 
         private @Nullable HistoricalOps readStateDLocked(@Nullable HistoricalOps ops,
                 int uid, @NonNull String packageName, @Nullable String attributionTag, int op,
-                @NonNull XmlPullParser parser, @OpFlags int filterFlags, double filterScale)
-                throws IOException {
-            final long key = XmlUtils.readLongAttribute(parser, ATTR_NAME);
+                @NonNull TypedXmlPullParser parser, @OpFlags int filterFlags, double filterScale)
+                throws IOException, XmlPullParserException {
+            final long key = parser.getAttributeLong(null, ATTR_NAME);
             final int flags = AppOpsManager.extractFlagsFromKey(key) & filterFlags;
             if (flags == 0) {
                 return null;
             }
             final int uidState = AppOpsManager.extractUidStateFromKey(key);
-            long accessCount = XmlUtils.readLongAttribute(parser, ATTR_ACCESS_COUNT, 0);
+            long accessCount = parser.getAttributeLong(null, ATTR_ACCESS_COUNT, 0);
             if (accessCount > 0) {
                 if (!Double.isNaN(filterScale)) {
                     accessCount = (long) HistoricalOps.round(
@@ -1402,7 +1404,7 @@
                 ops.increaseAccessCount(op, uid, packageName, attributionTag, uidState, flags,
                         accessCount);
             }
-            long rejectCount = XmlUtils.readLongAttribute(parser, ATTR_REJECT_COUNT, 0);
+            long rejectCount = parser.getAttributeLong(null, ATTR_REJECT_COUNT, 0);
             if (rejectCount > 0) {
                 if (!Double.isNaN(filterScale)) {
                     rejectCount = (long) HistoricalOps.round(
@@ -1414,7 +1416,7 @@
                 ops.increaseRejectCount(op, uid, packageName, attributionTag, uidState, flags,
                         rejectCount);
             }
-            long accessDuration =  XmlUtils.readLongAttribute(parser, ATTR_ACCESS_DURATION, 0);
+            long accessDuration =  parser.getAttributeLong(null, ATTR_ACCESS_DURATION, 0);
             if (accessDuration > 0) {
                 if (!Double.isNaN(filterScale)) {
                     accessDuration = (long) HistoricalOps.round(
@@ -1433,16 +1435,14 @@
                 long intervalOverflowMillis, @NonNull File file) throws IOException {
             final FileOutputStream output = sHistoricalAppOpsDir.openWrite(file);
             try {
-                final XmlSerializer serializer = Xml.newSerializer();
-                serializer.setOutput(output, StandardCharsets.UTF_8.name());
+                final TypedXmlSerializer serializer = Xml.resolveSerializer(output);
                 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output",
                         true);
                 serializer.startDocument(null, true);
                 serializer.startTag(null, TAG_HISTORY);
-                serializer.attribute(null, ATTR_VERSION, String.valueOf(CURRENT_VERSION));
+                serializer.attributeInt(null, ATTR_VERSION, CURRENT_VERSION);
                 if (intervalOverflowMillis != 0) {
-                    serializer.attribute(null, ATTR_OVERFLOW,
-                            Long.toString(intervalOverflowMillis));
+                    serializer.attributeLong(null, ATTR_OVERFLOW, intervalOverflowMillis);
                 }
                 if (allOps != null) {
                     final int opsCount = allOps.size();
@@ -1461,10 +1461,10 @@
         }
 
         private void writeHistoricalOpDLocked(@NonNull HistoricalOps ops,
-                @NonNull XmlSerializer serializer) throws IOException {
+                @NonNull TypedXmlSerializer serializer) throws IOException {
             serializer.startTag(null, TAG_OPS);
-            serializer.attribute(null, ATTR_BEGIN_TIME, Long.toString(ops.getBeginTimeMillis()));
-            serializer.attribute(null, ATTR_END_TIME, Long.toString(ops.getEndTimeMillis()));
+            serializer.attributeLong(null, ATTR_BEGIN_TIME, ops.getBeginTimeMillis());
+            serializer.attributeLong(null, ATTR_END_TIME, ops.getEndTimeMillis());
             final int uidCount = ops.getUidCount();
             for (int i = 0; i < uidCount; i++) {
                 final HistoricalUidOps uidOp = ops.getUidOpsAt(i);
@@ -1474,9 +1474,9 @@
         }
 
         private void writeHistoricalUidOpsDLocked(@NonNull HistoricalUidOps uidOps,
-                @NonNull XmlSerializer serializer) throws IOException {
+                @NonNull TypedXmlSerializer serializer) throws IOException {
             serializer.startTag(null, TAG_UID);
-            serializer.attribute(null, ATTR_NAME, Integer.toString(uidOps.getUid()));
+            serializer.attributeInt(null, ATTR_NAME, uidOps.getUid());
             final int packageCount = uidOps.getPackageCount();
             for (int i = 0; i < packageCount; i++) {
                 final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(i);
@@ -1486,7 +1486,7 @@
         }
 
         private void writeHistoricalPackageOpsDLocked(@NonNull HistoricalPackageOps packageOps,
-                @NonNull XmlSerializer serializer) throws IOException {
+                @NonNull TypedXmlSerializer serializer) throws IOException {
             serializer.startTag(null, TAG_PACKAGE);
             serializer.attribute(null, ATTR_NAME, packageOps.getPackageName());
             final int numAttributions = packageOps.getAttributedOpsCount();
@@ -1499,7 +1499,7 @@
 
         private void writeHistoricalAttributionOpsDLocked(
                 @NonNull AppOpsManager.AttributedHistoricalOps attributionOps,
-                @NonNull XmlSerializer serializer) throws IOException {
+                @NonNull TypedXmlSerializer serializer) throws IOException {
             serializer.startTag(null, TAG_ATTRIBUTION);
             XmlUtils.writeStringAttribute(serializer, ATTR_NAME, attributionOps.getTag());
             final int opCount = attributionOps.getOpCount();
@@ -1511,13 +1511,13 @@
         }
 
         private void writeHistoricalOpDLocked(@NonNull HistoricalOp op,
-                @NonNull XmlSerializer serializer) throws IOException {
+                @NonNull TypedXmlSerializer serializer) throws IOException {
             final LongSparseArray keys = op.collectKeys();
             if (keys == null || keys.size() <= 0) {
                 return;
             }
             serializer.startTag(null, TAG_OP);
-            serializer.attribute(null, ATTR_NAME, Integer.toString(op.getOpCode()));
+            serializer.attributeInt(null, ATTR_NAME, op.getOpCode());
             final int keyCount = keys.size();
             for (int i = 0; i < keyCount; i++) {
                 writeStateOnLocked(op, keys.keyAt(i), serializer);
@@ -1526,7 +1526,7 @@
         }
 
         private void writeStateOnLocked(@NonNull HistoricalOp op, long key,
-                @NonNull XmlSerializer serializer) throws IOException {
+                @NonNull TypedXmlSerializer serializer) throws IOException {
             final int uidState = AppOpsManager.extractUidStateFromKey(key);
             final int flags = AppOpsManager.extractFlagsFromKey(key);
 
@@ -1539,15 +1539,15 @@
             }
 
             serializer.startTag(null, TAG_STATE);
-            serializer.attribute(null, ATTR_NAME, Long.toString(key));
+            serializer.attributeLong(null, ATTR_NAME, key);
             if (accessCount > 0) {
-                serializer.attribute(null, ATTR_ACCESS_COUNT, Long.toString(accessCount));
+                serializer.attributeLong(null, ATTR_ACCESS_COUNT, accessCount);
             }
             if (rejectCount > 0) {
-                serializer.attribute(null, ATTR_REJECT_COUNT, Long.toString(rejectCount));
+                serializer.attributeLong(null, ATTR_REJECT_COUNT, rejectCount);
             }
             if (accessDuration > 0) {
-                serializer.attribute(null, ATTR_ACCESS_DURATION, Long.toString(accessDuration));
+                serializer.attributeLong(null, ATTR_ACCESS_DURATION, accessDuration);
             }
             serializer.endTag(null, TAG_STATE);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
index a8250ac..d588b8d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
@@ -22,6 +22,7 @@
 import android.os.AsyncTask;
 import android.os.Environment;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
@@ -80,7 +81,7 @@
     /**
      * @return
      */
-    protected abstract void parseBiometricsLocked(XmlPullParser parser)
+    protected abstract void parseBiometricsLocked(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException;
 
 
@@ -176,8 +177,7 @@
             return;
         }
         try {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(in, null);
+            TypedXmlPullParser parser = Xml.resolvePullParser(in);
             parseStateLocked(parser);
 
         } catch (XmlPullParserException | IOException e) {
@@ -189,7 +189,7 @@
     }
 
     @GuardedBy("this")
-    private void parseStateLocked(XmlPullParser parser)
+    private void parseStateLocked(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         final int outerDepth = parser.getDepth();
         int type;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java
index d30c3c8..78e875b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java
@@ -20,6 +20,8 @@
 import android.hardware.face.Face;
 import android.util.AtomicFile;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
@@ -29,7 +31,6 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -87,8 +88,7 @@
         try {
             out = destination.startWrite();
 
-            XmlSerializer serializer = Xml.newSerializer();
-            serializer.setOutput(out, "utf-8");
+            TypedXmlSerializer serializer = Xml.resolveSerializer(out);
             serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
             serializer.startDocument(null, true);
             serializer.startTag(null, TAG_FACES);
@@ -97,9 +97,9 @@
             for (int i = 0; i < count; i++) {
                 Face f = faces.get(i);
                 serializer.startTag(null, TAG_FACE);
-                serializer.attribute(null, ATTR_FACE_ID, Integer.toString(f.getBiometricId()));
+                serializer.attributeInt(null, ATTR_FACE_ID, f.getBiometricId());
                 serializer.attribute(null, ATTR_NAME, f.getName().toString());
-                serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(f.getDeviceId()));
+                serializer.attributeLong(null, ATTR_DEVICE_ID, f.getDeviceId());
                 serializer.endTag(null, TAG_FACE);
             }
 
@@ -119,7 +119,7 @@
 
     @GuardedBy("this")
     @Override
-    protected void parseBiometricsLocked(XmlPullParser parser)
+    protected void parseBiometricsLocked(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         final int outerDepth = parser.getDepth();
         int type;
@@ -132,9 +132,9 @@
             String tagName = parser.getName();
             if (tagName.equals(TAG_FACE)) {
                 String name = parser.getAttributeValue(null, ATTR_NAME);
-                String faceId = parser.getAttributeValue(null, ATTR_FACE_ID);
-                String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID);
-                mBiometrics.add(new Face(name, Integer.parseInt(faceId), Integer.parseInt(deviceId)));
+                int faceId = parser.getAttributeInt(null, ATTR_FACE_ID);
+                int deviceId = parser.getAttributeInt(null, ATTR_DEVICE_ID);
+                mBiometrics.add(new Face(name, faceId, deviceId));
             }
         }
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java
index e56c8d5..671e08b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java
@@ -20,6 +20,8 @@
 import android.hardware.fingerprint.Fingerprint;
 import android.util.AtomicFile;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
@@ -29,7 +31,6 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -88,8 +89,7 @@
         try {
             out = destination.startWrite();
 
-            XmlSerializer serializer = Xml.newSerializer();
-            serializer.setOutput(out, "utf-8");
+            TypedXmlSerializer serializer = Xml.resolveSerializer(out);
             serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
             serializer.startDocument(null, true);
             serializer.startTag(null, TAG_FINGERPRINTS);
@@ -98,10 +98,10 @@
             for (int i = 0; i < count; i++) {
                 Fingerprint fp = fingerprints.get(i);
                 serializer.startTag(null, TAG_FINGERPRINT);
-                serializer.attribute(null, ATTR_FINGER_ID, Integer.toString(fp.getBiometricId()));
+                serializer.attributeInt(null, ATTR_FINGER_ID, fp.getBiometricId());
                 serializer.attribute(null, ATTR_NAME, fp.getName().toString());
-                serializer.attribute(null, ATTR_GROUP_ID, Integer.toString(fp.getGroupId()));
-                serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(fp.getDeviceId()));
+                serializer.attributeInt(null, ATTR_GROUP_ID, fp.getGroupId());
+                serializer.attributeLong(null, ATTR_DEVICE_ID, fp.getDeviceId());
                 serializer.endTag(null, TAG_FINGERPRINT);
             }
 
@@ -121,7 +121,7 @@
 
     @GuardedBy("this")
     @Override
-    protected void parseBiometricsLocked(XmlPullParser parser)
+    protected void parseBiometricsLocked(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
 
         final int outerDepth = parser.getDepth();
@@ -135,11 +135,10 @@
             String tagName = parser.getName();
             if (tagName.equals(TAG_FINGERPRINT)) {
                 String name = parser.getAttributeValue(null, ATTR_NAME);
-                String groupId = parser.getAttributeValue(null, ATTR_GROUP_ID);
-                String fingerId = parser.getAttributeValue(null, ATTR_FINGER_ID);
-                String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID);
-                mBiometrics.add(new Fingerprint(name, Integer.parseInt(groupId),
-                        Integer.parseInt(fingerId), Long.parseLong(deviceId)));
+                int groupId = parser.getAttributeInt(null, ATTR_GROUP_ID);
+                int fingerId = parser.getAttributeInt(null, ATTR_FINGER_ID);
+                long deviceId = parser.getAttributeLong(null, ATTR_DEVICE_ID);
+                mBiometrics.add(new Fingerprint(name, groupId, fingerId, deviceId));
             }
         }
     }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 234dcc9..3445275 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -18,10 +18,7 @@
 
 import static android.Manifest.permission.BIND_VPN_SERVICE;
 import static android.net.ConnectivityManager.NETID_UNSET;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
 import static android.net.RouteInfo.RTN_THROW;
 import static android.net.RouteInfo.RTN_UNREACHABLE;
 
@@ -111,7 +108,6 @@
 import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnInfo;
 import com.android.internal.net.VpnProfile;
-import com.android.internal.util.ArrayUtils;
 import com.android.server.DeviceIdleInternal;
 import com.android.server.LocalServices;
 import com.android.server.net.BaseNetworkObserver;
@@ -463,99 +459,6 @@
     }
 
     /**
-     * Updates {@link #mNetworkCapabilities} based on current underlying networks and returns a
-     * defensive copy.
-     *
-     * <p>Does not propagate updated capabilities to apps.
-     *
-     * @param defaultNetwork underlying network for VPNs following platform's default
-     */
-    public synchronized NetworkCapabilities updateCapabilities(@Nullable Network defaultNetwork) {
-        if (mConfig == null) {
-            // VPN is not running.
-            return null;
-        }
-
-        Network[] underlyingNetworks = mConfig.underlyingNetworks;
-        if (underlyingNetworks == null && defaultNetwork != null) {
-            // null underlying networks means to track the default.
-            underlyingNetworks = new Network[] { defaultNetwork };
-        }
-        // Only apps targeting Q and above can explicitly declare themselves as metered.
-        final boolean isAlwaysMetered = mIsPackageTargetingAtLeastQ && mConfig.isMetered;
-
-        applyUnderlyingCapabilities(
-                mConnectivityManager,
-                underlyingNetworks,
-                mNetworkCapabilities,
-                isAlwaysMetered);
-
-        return new NetworkCapabilities(mNetworkCapabilities);
-    }
-
-    @VisibleForTesting
-    public static void applyUnderlyingCapabilities(
-            @NonNull final ConnectivityManager cm,
-            @Nullable final Network[] underlyingNetworks,
-            @NonNull final NetworkCapabilities caps,
-            final boolean isAlwaysMetered) {
-        int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN };
-        int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
-        int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
-        boolean metered = isAlwaysMetered; // metered if any underlying is metered, or alwaysMetered
-        boolean roaming = false; // roaming if any underlying is roaming
-        boolean congested = false; // congested if any underlying is congested
-        boolean suspended = true; // suspended if all underlying are suspended
-
-        boolean hadUnderlyingNetworks = false;
-        if (null != underlyingNetworks) {
-            for (Network underlying : underlyingNetworks) {
-                // TODO(b/124469351): Get capabilities directly from ConnectivityService instead.
-                final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying);
-                if (underlyingCaps == null) continue;
-                hadUnderlyingNetworks = true;
-                for (int underlyingType : underlyingCaps.getTransportTypes()) {
-                    transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType);
-                }
-
-                // Merge capabilities of this underlying network. For bandwidth, assume the
-                // worst case.
-                downKbps = NetworkCapabilities.minBandwidth(downKbps,
-                        underlyingCaps.getLinkDownstreamBandwidthKbps());
-                upKbps = NetworkCapabilities.minBandwidth(upKbps,
-                        underlyingCaps.getLinkUpstreamBandwidthKbps());
-                // If this underlying network is metered, the VPN is metered (it may cost money
-                // to send packets on this network).
-                metered |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_METERED);
-                // If this underlying network is roaming, the VPN is roaming (the billing structure
-                // is different than the usual, local one).
-                roaming |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING);
-                // If this underlying network is congested, the VPN is congested (the current
-                // condition of the network affects the performance of this network).
-                congested |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_CONGESTED);
-                // If this network is not suspended, the VPN is not suspended (the VPN
-                // is able to transfer some data).
-                suspended &= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
-            }
-        }
-        if (!hadUnderlyingNetworks) {
-            // No idea what the underlying networks are; assume the safer defaults
-            metered = true;
-            roaming = false;
-            congested = false;
-            suspended = false;
-        }
-
-        caps.setTransportTypes(transportTypes);
-        caps.setLinkDownstreamBandwidthKbps(downKbps);
-        caps.setLinkUpstreamBandwidthKbps(upKbps);
-        caps.setCapability(NET_CAPABILITY_NOT_METERED, !metered);
-        caps.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming);
-        caps.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested);
-        caps.setCapability(NET_CAPABILITY_NOT_SUSPENDED, !suspended);
-    }
-
-    /**
      * Chooses whether to force all connections to go though VPN.
      *
      * Used to enable/disable legacy VPN lockdown.
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 9a1f1e5..d27cb16 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -58,12 +58,10 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IntPair;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -71,7 +69,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.HashMap;
@@ -1650,37 +1647,23 @@
 
             String tagName = parser.getName();
             if ("accounts".equals(tagName)) {
-                String listen = parser.getAttributeValue(null, XML_ATTR_LISTEN_FOR_TICKLES);
-                String versionString = parser.getAttributeValue(null, "version");
-                int version;
-                try {
-                    version = (versionString == null) ? 0 : Integer.parseInt(versionString);
-                } catch (NumberFormatException e) {
-                    version = 0;
-                }
+                boolean listen = parser.getAttributeBoolean(
+                        null, XML_ATTR_LISTEN_FOR_TICKLES, true);
+                int version = parser.getAttributeInt(null, "version", 0);
 
                 if (version < 3) {
                     mGrantSyncAdaptersAccountAccess = true;
                 }
 
-                String nextIdString = parser.getAttributeValue(null, XML_ATTR_NEXT_AUTHORITY_ID);
-                try {
-                    int id = (nextIdString == null) ? 0 : Integer.parseInt(nextIdString);
-                    mNextAuthorityId = Math.max(mNextAuthorityId, id);
-                } catch (NumberFormatException e) {
-                    // don't care
-                }
-                String offsetString = parser.getAttributeValue(null, XML_ATTR_SYNC_RANDOM_OFFSET);
-                try {
-                    mSyncRandomOffset = (offsetString == null) ? 0 : Integer.parseInt(offsetString);
-                } catch (NumberFormatException e) {
-                    mSyncRandomOffset = 0;
-                }
+                int nextId = parser.getAttributeInt(null, XML_ATTR_NEXT_AUTHORITY_ID, 0);
+                mNextAuthorityId = Math.max(mNextAuthorityId, nextId);
+
+                mSyncRandomOffset = parser.getAttributeInt(null, XML_ATTR_SYNC_RANDOM_OFFSET, 0);
                 if (mSyncRandomOffset == 0) {
                     Random random = new Random(System.currentTimeMillis());
                     mSyncRandomOffset = random.nextInt(86400);
                 }
-                mMasterSyncAutomatically.put(0, listen == null || Boolean.parseBoolean(listen));
+                mMasterSyncAutomatically.put(0, listen);
                 eventType = parser.next();
                 AuthorityInfo authority = null;
                 PeriodicSync periodicSync = null;
@@ -1804,27 +1787,23 @@
         return writeNeeded;
     }
 
-    private void parseListenForTickles(XmlPullParser parser) {
-        String user = parser.getAttributeValue(null, XML_ATTR_USER);
+    private void parseListenForTickles(TypedXmlPullParser parser) {
         int userId = 0;
         try {
-            userId = Integer.parseInt(user);
-        } catch (NumberFormatException e) {
+            parser.getAttributeInt(null, XML_ATTR_USER);
+        } catch (XmlPullParserException e) {
             Slog.e(TAG, "error parsing the user for listen-for-tickles", e);
-        } catch (NullPointerException e) {
-            Slog.e(TAG, "the user in listen-for-tickles is null", e);
         }
-        String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED);
-        boolean listen = enabled == null || Boolean.parseBoolean(enabled);
+        boolean listen = parser.getAttributeBoolean(null, XML_ATTR_ENABLED, true);
         mMasterSyncAutomatically.put(userId, listen);
     }
 
-    private AuthorityInfo parseAuthority(XmlPullParser parser, int version,
-            AccountAuthorityValidator validator) {
+    private AuthorityInfo parseAuthority(TypedXmlPullParser parser, int version,
+            AccountAuthorityValidator validator) throws XmlPullParserException {
         AuthorityInfo authority = null;
         int id = -1;
         try {
-            id = Integer.parseInt(parser.getAttributeValue(null, "id"));
+            id = parser.getAttributeInt(null, "id");
         } catch (NumberFormatException e) {
             Slog.e(TAG, "error parsing the id of the authority", e);
         } catch (NullPointerException e) {
@@ -1832,14 +1811,13 @@
         }
         if (id >= 0) {
             String authorityName = parser.getAttributeValue(null, "authority");
-            String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED);
+            boolean enabled = parser.getAttributeBoolean(null, XML_ATTR_ENABLED, true);
             String syncable = parser.getAttributeValue(null, "syncable");
             String accountName = parser.getAttributeValue(null, "account");
             String accountType = parser.getAttributeValue(null, "type");
-            String user = parser.getAttributeValue(null, XML_ATTR_USER);
+            int userId = parser.getAttributeInt(null, XML_ATTR_USER, 0);
             String packageName = parser.getAttributeValue(null, "package");
             String className = parser.getAttributeValue(null, "class");
-            int userId = user == null ? 0 : Integer.parseInt(user);
             if (accountType == null && packageName == null) {
                 accountType = "com.google";
                 syncable = String.valueOf(AuthorityInfo.NOT_INITIALIZED);
@@ -1884,7 +1862,7 @@
                 }
             }
             if (authority != null) {
-                authority.enabled = enabled == null || Boolean.parseBoolean(enabled);
+                authority.enabled = enabled;
                 try {
                     authority.syncable = (syncable == null) ?
                             AuthorityInfo.NOT_INITIALIZED : Integer.parseInt(syncable);
@@ -1912,32 +1890,22 @@
     /**
      * Parse a periodic sync from accounts.xml. Sets the bundle to be empty.
      */
-    private PeriodicSync parsePeriodicSync(XmlPullParser parser, AuthorityInfo authorityInfo) {
+    private PeriodicSync parsePeriodicSync(TypedXmlPullParser parser, AuthorityInfo authorityInfo) {
         Bundle extras = new Bundle(); // Gets filled in later.
-        String periodValue = parser.getAttributeValue(null, "period");
-        String flexValue = parser.getAttributeValue(null, "flex");
-        final long period;
+        long period;
         long flextime;
         try {
-            period = Long.parseLong(periodValue);
-        } catch (NumberFormatException e) {
+            period = parser.getAttributeLong(null, "period");
+        } catch (XmlPullParserException e) {
             Slog.e(TAG, "error parsing the period of a periodic sync", e);
             return null;
-        } catch (NullPointerException e) {
-            Slog.e(TAG, "the period of a periodic sync is null", e);
-            return null;
         }
         try {
-            flextime = Long.parseLong(flexValue);
-        } catch (NumberFormatException e) {
+            flextime = parser.getAttributeLong(null, "flex");
+        } catch (XmlPullParserException e) {
             flextime = calculateDefaultFlexTime(period);
-            Slog.e(TAG, "Error formatting value parsed for periodic sync flex: " + flexValue
-                    + ", using default: "
-                    + flextime);
-        } catch (NullPointerException expected) {
-            flextime = calculateDefaultFlexTime(period);
-            Slog.d(TAG, "No flex time specified for this sync, using a default. period: "
-                    + period + " flex: " + flextime);
+            Slog.e(TAG, "Error formatting value parsed for periodic sync flex, using default: "
+                    + flextime, e);
         }
         PeriodicSync periodicSync;
         periodicSync =
@@ -1949,7 +1917,7 @@
         return periodicSync;
     }
 
-    private void parseExtra(XmlPullParser parser, Bundle extras) {
+    private void parseExtra(TypedXmlPullParser parser, Bundle extras) {
         String name = parser.getAttributeValue(null, "name");
         String type = parser.getAttributeValue(null, "type");
         String value1 = parser.getAttributeValue(null, "value1");
@@ -1994,9 +1962,9 @@
             out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
 
             out.startTag(null, "accounts");
-            out.attribute(null, "version", Integer.toString(ACCOUNTS_VERSION));
-            out.attribute(null, XML_ATTR_NEXT_AUTHORITY_ID, Integer.toString(mNextAuthorityId));
-            out.attribute(null, XML_ATTR_SYNC_RANDOM_OFFSET, Integer.toString(mSyncRandomOffset));
+            out.attributeInt(null, "version", ACCOUNTS_VERSION);
+            out.attributeInt(null, XML_ATTR_NEXT_AUTHORITY_ID, mNextAuthorityId);
+            out.attributeInt(null, XML_ATTR_SYNC_RANDOM_OFFSET, mSyncRandomOffset);
 
             // Write the Sync Automatically flags for each user
             final int M = mMasterSyncAutomatically.size();
@@ -2004,8 +1972,8 @@
                 int userId = mMasterSyncAutomatically.keyAt(m);
                 Boolean listen = mMasterSyncAutomatically.valueAt(m);
                 out.startTag(null, XML_TAG_LISTEN_FOR_TICKLES);
-                out.attribute(null, XML_ATTR_USER, Integer.toString(userId));
-                out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(listen));
+                out.attributeInt(null, XML_ATTR_USER, userId);
+                out.attributeBoolean(null, XML_ATTR_ENABLED, listen);
                 out.endTag(null, XML_TAG_LISTEN_FOR_TICKLES);
             }
 
@@ -2014,13 +1982,13 @@
                 AuthorityInfo authority = mAuthorities.valueAt(i);
                 EndPoint info = authority.target;
                 out.startTag(null, "authority");
-                out.attribute(null, "id", Integer.toString(authority.ident));
-                out.attribute(null, XML_ATTR_USER, Integer.toString(info.userId));
-                out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(authority.enabled));
+                out.attributeInt(null, "id", authority.ident);
+                out.attributeInt(null, XML_ATTR_USER, info.userId);
+                out.attributeBoolean(null, XML_ATTR_ENABLED, authority.enabled);
                 out.attribute(null, "account", info.account.name);
                 out.attribute(null, "type", info.account.type);
                 out.attribute(null, "authority", info.provider);
-                out.attribute(null, "syncable", Integer.toString(authority.syncable));
+                out.attributeInt(null, "syncable", authority.syncable);
                 out.endTag(null, "authority");
             }
             out.endTag(null, "accounts");
diff --git a/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
index 928799b..0f1e666 100644
--- a/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
+++ b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
@@ -27,17 +27,14 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastXmlSerializer;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
 import java.time.LocalDate;
 import java.time.format.DateTimeParseException;
 import java.util.ArrayDeque;
@@ -179,7 +176,7 @@
                             entry.getKey());
                     if (userSerialNumber != -1 && userDayStats.getLocalDate().isAfter(cutOffDate)) {
                         out.startTag(null, TAG_AMBIENT_BRIGHTNESS_DAY_STATS);
-                        out.attribute(null, ATTR_USER, Integer.toString(userSerialNumber));
+                        out.attributeInt(null, ATTR_USER, userSerialNumber);
                         out.attribute(null, ATTR_LOCAL_DATE,
                                 userDayStats.getLocalDate().toString());
                         StringBuilder bucketBoundariesValues = new StringBuilder();
@@ -229,7 +226,7 @@
                     }
                     tag = parser.getName();
                     if (TAG_AMBIENT_BRIGHTNESS_DAY_STATS.equals(tag)) {
-                        String userSerialNumber = parser.getAttributeValue(null, ATTR_USER);
+                        int userSerialNumber = parser.getAttributeInt(null, ATTR_USER);
                         LocalDate localDate = LocalDate.parse(
                                 parser.getAttributeValue(null, ATTR_LOCAL_DATE));
                         String[] bucketBoundaries = parser.getAttributeValue(null,
@@ -246,8 +243,7 @@
                             parsedBucketBoundaries[i] = Float.parseFloat(bucketBoundaries[i]);
                             parsedBucketStats[i] = Float.parseFloat(bucketStats[i]);
                         }
-                        int userId = mInjector.getUserId(mUserManager,
-                                Integer.parseInt(userSerialNumber));
+                        int userId = mInjector.getUserId(mUserManager, userSerialNumber);
                         if (userId != -1 && localDate.isAfter(cutOffDate)) {
                             Deque<AmbientBrightnessDayStats> userStats = getOrCreateUserStats(
                                     parsedStats, userId);
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 3ae99ef..2a0e219 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -555,22 +555,22 @@
             if (userSerialNo != -1 && toWrite[i].timeStamp > timeCutOff) {
                 mEvents.append(toWrite[i]);
                 out.startTag(null, TAG_EVENT);
-                out.attribute(null, ATTR_NITS, Float.toString(toWrite[i].brightness));
-                out.attribute(null, ATTR_TIMESTAMP, Long.toString(toWrite[i].timeStamp));
+                out.attributeFloat(null, ATTR_NITS, toWrite[i].brightness);
+                out.attributeLong(null, ATTR_TIMESTAMP, toWrite[i].timeStamp);
                 out.attribute(null, ATTR_PACKAGE_NAME, toWrite[i].packageName);
-                out.attribute(null, ATTR_USER, Integer.toString(userSerialNo));
-                out.attribute(null, ATTR_BATTERY_LEVEL, Float.toString(toWrite[i].batteryLevel));
-                out.attribute(null, ATTR_NIGHT_MODE, Boolean.toString(toWrite[i].nightMode));
-                out.attribute(null, ATTR_COLOR_TEMPERATURE, Integer.toString(
-                        toWrite[i].colorTemperature));
-                out.attribute(null, ATTR_LAST_NITS,
-                        Float.toString(toWrite[i].lastBrightness));
-                out.attribute(null, ATTR_DEFAULT_CONFIG,
-                        Boolean.toString(toWrite[i].isDefaultBrightnessConfig));
-                out.attribute(null, ATTR_POWER_SAVE,
-                        Float.toString(toWrite[i].powerBrightnessFactor));
-                out.attribute(null, ATTR_USER_POINT,
-                        Boolean.toString(toWrite[i].isUserSetBrightness));
+                out.attributeInt(null, ATTR_USER, userSerialNo);
+                out.attributeFloat(null, ATTR_BATTERY_LEVEL, toWrite[i].batteryLevel);
+                out.attributeBoolean(null, ATTR_NIGHT_MODE, toWrite[i].nightMode);
+                out.attributeInt(null, ATTR_COLOR_TEMPERATURE,
+                        toWrite[i].colorTemperature);
+                out.attributeFloat(null, ATTR_LAST_NITS,
+                        toWrite[i].lastBrightness);
+                out.attributeBoolean(null, ATTR_DEFAULT_CONFIG,
+                        toWrite[i].isDefaultBrightnessConfig);
+                out.attributeFloat(null, ATTR_POWER_SAVE,
+                        toWrite[i].powerBrightnessFactor);
+                out.attributeBoolean(null, ATTR_USER_POINT,
+                        toWrite[i].isUserSetBrightness);
                 StringBuilder luxValues = new StringBuilder();
                 StringBuilder luxTimestamps = new StringBuilder();
                 for (int j = 0; j < toWrite[i].luxValues.length; ++j) {
@@ -585,8 +585,8 @@
                 out.attribute(null, ATTR_LUX_TIMESTAMPS, luxTimestamps.toString());
                 if (toWrite[i].colorValueBuckets != null
                         && toWrite[i].colorValueBuckets.length > 0) {
-                    out.attribute(null, ATTR_COLOR_SAMPLE_DURATION,
-                            Long.toString(toWrite[i].colorSampleDuration));
+                    out.attributeLong(null, ATTR_COLOR_SAMPLE_DURATION,
+                            toWrite[i].colorSampleDuration);
                     StringBuilder buckets = new StringBuilder();
                     for (int j = 0; j < toWrite[i].colorValueBuckets.length; ++j) {
                         if (j > 0) {
@@ -633,22 +633,16 @@
                 if (TAG_EVENT.equals(tag)) {
                     BrightnessChangeEvent.Builder builder = new BrightnessChangeEvent.Builder();
 
-                    String brightness = parser.getAttributeValue(null, ATTR_NITS);
-                    builder.setBrightness(Float.parseFloat(brightness));
-                    String timestamp = parser.getAttributeValue(null, ATTR_TIMESTAMP);
-                    builder.setTimeStamp(Long.parseLong(timestamp));
+                    builder.setBrightness(parser.getAttributeFloat(null, ATTR_NITS));
+                    builder.setTimeStamp(parser.getAttributeLong(null, ATTR_TIMESTAMP));
                     builder.setPackageName(parser.getAttributeValue(null, ATTR_PACKAGE_NAME));
-                    String user = parser.getAttributeValue(null, ATTR_USER);
-                    builder.setUserId(mInjector.getUserId(mUserManager, Integer.parseInt(user)));
-                    String batteryLevel = parser.getAttributeValue(null, ATTR_BATTERY_LEVEL);
-                    builder.setBatteryLevel(Float.parseFloat(batteryLevel));
-                    String nightMode = parser.getAttributeValue(null, ATTR_NIGHT_MODE);
-                    builder.setNightMode(Boolean.parseBoolean(nightMode));
-                    String colorTemperature =
-                            parser.getAttributeValue(null, ATTR_COLOR_TEMPERATURE);
-                    builder.setColorTemperature(Integer.parseInt(colorTemperature));
-                    String lastBrightness = parser.getAttributeValue(null, ATTR_LAST_NITS);
-                    builder.setLastBrightness(Float.parseFloat(lastBrightness));
+                    builder.setUserId(mInjector.getUserId(mUserManager,
+                            parser.getAttributeInt(null, ATTR_USER)));
+                    builder.setBatteryLevel(parser.getAttributeFloat(null, ATTR_BATTERY_LEVEL));
+                    builder.setNightMode(parser.getAttributeBoolean(null, ATTR_NIGHT_MODE));
+                    builder.setColorTemperature(
+                            parser.getAttributeInt(null, ATTR_COLOR_TEMPERATURE));
+                    builder.setLastBrightness(parser.getAttributeFloat(null, ATTR_LAST_NITS));
 
                     String luxValue = parser.getAttributeValue(null, ATTR_LUX);
                     String luxTimestamp = parser.getAttributeValue(null, ATTR_LUX_TIMESTAMPS);
@@ -667,20 +661,12 @@
                     builder.setLuxValues(luxValues);
                     builder.setLuxTimestamps(luxTimestamps);
 
-                    String defaultConfig = parser.getAttributeValue(null, ATTR_DEFAULT_CONFIG);
-                    if (defaultConfig != null) {
-                        builder.setIsDefaultBrightnessConfig(Boolean.parseBoolean(defaultConfig));
-                    }
-                    String powerSave = parser.getAttributeValue(null, ATTR_POWER_SAVE);
-                    if (powerSave != null) {
-                        builder.setPowerBrightnessFactor(Float.parseFloat(powerSave));
-                    } else {
-                        builder.setPowerBrightnessFactor(1.0f);
-                    }
-                    String userPoint = parser.getAttributeValue(null, ATTR_USER_POINT);
-                    if (userPoint != null) {
-                        builder.setUserBrightnessPoint(Boolean.parseBoolean(userPoint));
-                    }
+                    builder.setIsDefaultBrightnessConfig(
+                            parser.getAttributeBoolean(null, ATTR_DEFAULT_CONFIG, false));
+                    builder.setPowerBrightnessFactor(
+                            parser.getAttributeFloat(null, ATTR_POWER_SAVE, 1.0f));
+                    builder.setUserBrightnessPoint(
+                            parser.getAttributeBoolean(null, ATTR_USER_POINT, false));
 
                     String colorSampleDurationString =
                             parser.getAttributeValue(null, ATTR_COLOR_SAMPLE_DURATION);
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 1aced07..329081a 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -38,6 +38,7 @@
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -49,6 +50,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.server.display.utils.AmbientFilter;
 import com.android.server.display.utils.AmbientFilterFactory;
+import com.android.server.utils.DeviceConfigInterface;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -68,9 +70,11 @@
     private static final boolean DEBUG = false;
 
     private static final int MSG_REFRESH_RATE_RANGE_CHANGED = 1;
-    private static final int MSG_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
+    private static final int MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
     private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3;
-    private static final int MSG_REFRESH_RATE_IN_ZONE_CHANGED = 4;
+    private static final int MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED = 4;
+    private static final int MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED = 5;
+    private static final int MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED = 6;
 
     // Special ID used to indicate that given vote is to be applied globally, rather than to a
     // specific display.
@@ -83,6 +87,13 @@
     private final Context mContext;
 
     private final DisplayModeDirectorHandler mHandler;
+    private final Injector mInjector;
+
+    private final AppRequestObserver mAppRequestObserver;
+    private final SettingsObserver mSettingsObserver;
+    private final DisplayObserver mDisplayObserver;
+    private final DeviceConfigInterface mDeviceConfig;
+    private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
 
     // A map from the display ID to the collection of votes and their priority. The latter takes
     // the form of another map from the priority to the vote itself so that each priority is
@@ -93,12 +104,8 @@
     // A map from the display ID to the default mode of that display.
     private SparseArray<Display.Mode> mDefaultModeByDisplay;
 
-    private final AppRequestObserver mAppRequestObserver;
-    private final SettingsObserver mSettingsObserver;
-    private final DisplayObserver mDisplayObserver;
     private BrightnessObserver mBrightnessObserver;
 
-    private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
     private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener;
 
     private boolean mAlwaysRespectAppRequest;
@@ -127,8 +134,14 @@
     private int mModeSwitchingType = SWITCHING_TYPE_WITHIN_GROUPS;
 
     public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
+        this(context, handler, new RealInjector());
+    }
+
+    public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
+            @NonNull Injector injector) {
         mContext = context;
         mHandler = new DisplayModeDirectorHandler(handler.getLooper());
+        mInjector = injector;
         mVotesByDisplay = new SparseArray<>();
         mSupportedModesByDisplay = new SparseArray<>();
         mDefaultModeByDisplay = new SparseArray<>();
@@ -137,6 +150,7 @@
         mDisplayObserver = new DisplayObserver(context, handler);
         mBrightnessObserver = new BrightnessObserver(context, handler);
         mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
+        mDeviceConfig = injector.getDeviceConfig();
         mAlwaysRespectAppRequest = false;
     }
 
@@ -455,6 +469,23 @@
     }
 
     /**
+     * Retrieve the Vote for the given display and priority. Intended only for testing purposes.
+     *
+     * @param displayId the display to query for
+     * @param priority the priority of the vote to return
+     * @return the vote corresponding to the given {@code displayId} and {@code priority},
+     *         or {@code null} if there isn't one
+     */
+    @VisibleForTesting
+    @Nullable
+    Vote getVote(int displayId, int priority) {
+        synchronized (mLock) {
+            SparseArray<Vote> votes = getVotesLocked(displayId);
+            return votes.get(priority);
+        }
+    }
+
+    /**
      * Print the object's state and debug information into the given stream.
      *
      * @param pw The stream to dump information to.
@@ -586,6 +617,17 @@
     }
 
     @VisibleForTesting
+    BrightnessObserver getBrightnessObserver() {
+        return mBrightnessObserver;
+    }
+
+    @VisibleForTesting
+    SettingsObserver getSettingsObserver() {
+        return mSettingsObserver;
+    }
+
+
+    @VisibleForTesting
     DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
             float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
         synchronized (mLock) {
@@ -613,16 +655,35 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_BRIGHTNESS_THRESHOLDS_CHANGED:
+                case MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED: {
+                    Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
+                    mBrightnessObserver.onDeviceConfigLowBrightnessThresholdsChanged(
+                            thresholds.first, thresholds.second);
+                    break;
+                }
+
+                case MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED: {
+                    int refreshRateInZone = msg.arg1;
+                    mBrightnessObserver.onDeviceConfigRefreshRateInLowZoneChanged(
+                            refreshRateInZone);
+                    break;
+                }
+
+                case MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED: {
                     Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
 
-                    if (thresholds != null) {
-                        mBrightnessObserver.onDeviceConfigThresholdsChanged(
-                                thresholds.first, thresholds.second);
-                    } else {
-                        mBrightnessObserver.onDeviceConfigThresholdsChanged(null, null);
-                    }
+                    mBrightnessObserver.onDeviceConfigHighBrightnessThresholdsChanged(
+                            thresholds.first, thresholds.second);
+
                     break;
+                }
+
+                case MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED: {
+                    int refreshRateInZone = msg.arg1;
+                    mBrightnessObserver.onDeviceConfigRefreshRateInHighZoneChanged(
+                            refreshRateInZone);
+                    break;
+                }
 
                 case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED:
                     Float defaultPeakRefreshRate = (Float) msg.obj;
@@ -630,12 +691,6 @@
                             defaultPeakRefreshRate);
                     break;
 
-                case MSG_REFRESH_RATE_IN_ZONE_CHANGED:
-                    int refreshRateInZone = msg.arg1;
-                    mBrightnessObserver.onDeviceConfigRefreshRateInZoneChanged(
-                            refreshRateInZone);
-                    break;
-
                 case MSG_REFRESH_RATE_RANGE_CHANGED:
                     DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener =
                             (DesiredDisplayModeSpecsListener) msg.obj;
@@ -822,10 +877,11 @@
         // by all other considerations. It acts to set a default frame rate for a device.
         public static final int PRIORITY_DEFAULT_REFRESH_RATE = 0;
 
-        // LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null.
+        // FLICKER votes for a single refresh rate like [60,60], [90,90] or null.
         // If the higher voters result is a range, it will fix the rate to a single choice.
-        // It's used to avoid rate switch in certain conditions.
-        public static final int PRIORITY_LOW_BRIGHTNESS = 1;
+        // It's used to avoid refresh rate switches in certain conditions which may result in the
+        // user seeing the display flickering when the switches occur.
+        public static final int PRIORITY_FLICKER = 1;
 
         // SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
         // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
@@ -898,8 +954,8 @@
             switch (priority) {
                 case PRIORITY_DEFAULT_REFRESH_RATE:
                     return "PRIORITY_DEFAULT_REFRESH_RATE";
-                case PRIORITY_LOW_BRIGHTNESS:
-                    return "PRIORITY_LOW_BRIGHTNESS";
+                case PRIORITY_FLICKER:
+                    return "PRIORITY_FLICKER";
                 case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
                     return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
                 case PRIORITY_APP_REQUEST_REFRESH_RATE:
@@ -924,7 +980,8 @@
         }
     }
 
-    private final class SettingsObserver extends ContentObserver {
+    @VisibleForTesting
+    final class SettingsObserver extends ContentObserver {
         private final Uri mPeakRefreshRateSetting =
                 Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
         private final Uri mMinRefreshRateSetting =
@@ -949,8 +1006,7 @@
 
         public void observe() {
             final ContentResolver cr = mContext.getContentResolver();
-            cr.registerContentObserver(mPeakRefreshRateSetting, false /*notifyDescendants*/, this,
-                    UserHandle.USER_SYSTEM);
+            mInjector.registerPeakRefreshRateObserver(cr, this);
             cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this,
                     UserHandle.USER_SYSTEM);
             cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
@@ -971,6 +1027,13 @@
             }
         }
 
+        public void setDefaultRefreshRate(float refreshRate) {
+            synchronized (mLock) {
+                mDefaultRefreshRate = refreshRate;
+                updateRefreshRateSettingLocked();
+            }
+        }
+
         public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) {
             if (defaultPeakRefreshRate == null) {
                 defaultPeakRefreshRate = (float) mContext.getResources().getInteger(
@@ -1189,6 +1252,7 @@
         @Override
         public void onDisplayChanged(int displayId) {
             updateDisplayModes(displayId);
+            // TODO: Break the coupling between DisplayObserver and BrightnessObserver.
             mBrightnessObserver.onDisplayChanged(displayId);
         }
 
@@ -1227,15 +1291,16 @@
      */
     @VisibleForTesting
     public class BrightnessObserver extends ContentObserver {
-        // TODO: brightnessfloat: change this to the float setting
-        private final Uri mDisplayBrightnessSetting =
-                Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
         private final static int LIGHT_SENSOR_RATE_MS = 250;
-        private int[] mDisplayBrightnessThresholds;
-        private int[] mAmbientBrightnessThresholds;
+        private int[] mLowDisplayBrightnessThresholds;
+        private int[] mLowAmbientBrightnessThresholds;
+        private int[] mHighDisplayBrightnessThresholds;
+        private int[] mHighAmbientBrightnessThresholds;
         // valid threshold if any item from the array >= 0
-        private boolean mShouldObserveDisplayChange;
-        private boolean mShouldObserveAmbientChange;
+        private boolean mShouldObserveDisplayLowChange;
+        private boolean mShouldObserveAmbientLowChange;
+        private boolean mShouldObserveDisplayHighChange;
+        private boolean mShouldObserveAmbientHighChange;
 
         private SensorManager mSensorManager;
         private Sensor mLightSensor;
@@ -1243,46 +1308,122 @@
         // Take it as low brightness before valid sensor data comes
         private float mAmbientLux = -1.0f;
         private AmbientFilter mAmbientFilter;
+        private int mBrightness = -1;
 
         private final Context mContext;
 
-        // Enable light sensor only when mShouldObserveAmbientChange is true, screen is on, peak
-        // refresh rate changeable and low power mode off. After initialization, these states will
+        // Enable light sensor only when mShouldObserveAmbientLowChange is true or
+        // mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
+        // changeable and low power mode off. After initialization, these states will
         // be updated from the same handler thread.
-        private boolean mScreenOn = false;
+        private boolean mDefaultDisplayOn = false;
         private boolean mRefreshRateChangeable = false;
         private boolean mLowPowerModeEnabled = false;
 
-        private int mRefreshRateInZone;
+        private int mRefreshRateInLowZone;
+        private int mRefreshRateInHighZone;
 
         BrightnessObserver(Context context, Handler handler) {
             super(handler);
             mContext = context;
-            mDisplayBrightnessThresholds = context.getResources().getIntArray(
+            mLowDisplayBrightnessThresholds = context.getResources().getIntArray(
                     R.array.config_brightnessThresholdsOfPeakRefreshRate);
-            mAmbientBrightnessThresholds = context.getResources().getIntArray(
+            mLowAmbientBrightnessThresholds = context.getResources().getIntArray(
                     R.array.config_ambientThresholdsOfPeakRefreshRate);
 
-            if (mDisplayBrightnessThresholds.length != mAmbientBrightnessThresholds.length) {
-                throw new RuntimeException("display brightness threshold array and ambient "
-                        + "brightness threshold array have different length");
+            if (mLowDisplayBrightnessThresholds.length != mLowAmbientBrightnessThresholds.length) {
+                throw new RuntimeException("display low brightness threshold array and ambient "
+                        + "brightness threshold array have different length: "
+                        + "displayBrightnessThresholds="
+                        + Arrays.toString(mLowDisplayBrightnessThresholds)
+                        + ", ambientBrightnessThresholds="
+                        + Arrays.toString(mLowAmbientBrightnessThresholds));
             }
+
+            mHighDisplayBrightnessThresholds = context.getResources().getIntArray(
+                    R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
+            mHighAmbientBrightnessThresholds = context.getResources().getIntArray(
+                    R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
+            if (mHighDisplayBrightnessThresholds.length
+                    != mHighAmbientBrightnessThresholds.length) {
+                throw new RuntimeException("display high brightness threshold array and ambient "
+                        + "brightness threshold array have different length: "
+                        + "displayBrightnessThresholds="
+                        + Arrays.toString(mHighDisplayBrightnessThresholds)
+                        + ", ambientBrightnessThresholds="
+                        + Arrays.toString(mHighAmbientBrightnessThresholds));
+            }
+            mRefreshRateInHighZone = context.getResources().getInteger(
+                    R.integer.config_fixedRefreshRateInHighZone);
+        }
+
+        /**
+         * @return the refresh to lock to when in a low brightness zone
+         */
+        @VisibleForTesting
+        int getRefreshRateInLowZone() {
+            return mRefreshRateInLowZone;
+        }
+
+        /**
+         * @return the display brightness thresholds for the low brightness zones
+         */
+        @VisibleForTesting
+        int[] getLowDisplayBrightnessThresholds() {
+            return mLowDisplayBrightnessThresholds;
+        }
+
+        /**
+         * @return the ambient brightness thresholds for the low brightness zones
+         */
+        @VisibleForTesting
+        int[] getLowAmbientBrightnessThresholds() {
+            return mLowAmbientBrightnessThresholds;
+        }
+
+        public void registerLightSensor(SensorManager sensorManager, Sensor lightSensor) {
+            mSensorManager = sensorManager;
+            mLightSensor = lightSensor;
+
+            mSensorManager.registerListener(mLightSensorListener,
+                    mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
         }
 
         public void observe(SensorManager sensorManager) {
             mSensorManager = sensorManager;
+            final ContentResolver cr = mContext.getContentResolver();
+            mBrightness = Settings.System.getIntForUser(cr,
+                    Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
 
             // DeviceConfig is accessible after system ready.
-            int[] brightnessThresholds = mDeviceConfigDisplaySettings.getBrightnessThresholds();
-            int[] ambientThresholds = mDeviceConfigDisplaySettings.getAmbientThresholds();
+            int[] lowDisplayBrightnessThresholds =
+                    mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds();
+            int[] lowAmbientBrightnessThresholds =
+                    mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds();
 
-            if (brightnessThresholds != null && ambientThresholds != null
-                    && brightnessThresholds.length == ambientThresholds.length) {
-                mDisplayBrightnessThresholds = brightnessThresholds;
-                mAmbientBrightnessThresholds = ambientThresholds;
+            if (lowDisplayBrightnessThresholds != null && lowAmbientBrightnessThresholds != null
+                    && lowDisplayBrightnessThresholds.length
+                    == lowAmbientBrightnessThresholds.length) {
+                mLowDisplayBrightnessThresholds = lowDisplayBrightnessThresholds;
+                mLowAmbientBrightnessThresholds = lowAmbientBrightnessThresholds;
             }
 
-            mRefreshRateInZone = mDeviceConfigDisplaySettings.getRefreshRateInZone();
+
+            int[] highDisplayBrightnessThresholds =
+                    mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds();
+            int[] highAmbientBrightnessThresholds =
+                    mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds();
+
+            if (highDisplayBrightnessThresholds != null && highAmbientBrightnessThresholds != null
+                    && highDisplayBrightnessThresholds.length
+                    == highAmbientBrightnessThresholds.length) {
+                mHighDisplayBrightnessThresholds = highDisplayBrightnessThresholds;
+                mHighAmbientBrightnessThresholds = highAmbientBrightnessThresholds;
+            }
+
+            mRefreshRateInLowZone = mDeviceConfigDisplaySettings.getRefreshRateInLowZone();
+            mRefreshRateInHighZone = mDeviceConfigDisplaySettings.getRefreshRateInHighZone();
+
             restartObserver();
             mDeviceConfigDisplaySettings.startListening();
         }
@@ -1294,7 +1435,7 @@
                 updateSensorStatus();
                 if (!changeable) {
                     // Revoke previous vote from BrightnessObserver
-                    updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, null);
+                    updateVoteLocked(Vote.PRIORITY_FLICKER, null);
                 }
             }
         }
@@ -1306,25 +1447,48 @@
             }
         }
 
-        public void onDeviceConfigThresholdsChanged(int[] brightnessThresholds,
+        public void onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds,
                 int[] ambientThresholds) {
-            if (brightnessThresholds != null && ambientThresholds != null
-                    && brightnessThresholds.length == ambientThresholds.length) {
-                mDisplayBrightnessThresholds = brightnessThresholds;
-                mAmbientBrightnessThresholds = ambientThresholds;
+            if (displayThresholds != null && ambientThresholds != null
+                    && displayThresholds.length == ambientThresholds.length) {
+                mLowDisplayBrightnessThresholds = displayThresholds;
+                mLowAmbientBrightnessThresholds = ambientThresholds;
             } else {
                 // Invalid or empty. Use device default.
-                mDisplayBrightnessThresholds = mContext.getResources().getIntArray(
+                mLowDisplayBrightnessThresholds = mContext.getResources().getIntArray(
                         R.array.config_brightnessThresholdsOfPeakRefreshRate);
-                mAmbientBrightnessThresholds = mContext.getResources().getIntArray(
+                mLowAmbientBrightnessThresholds = mContext.getResources().getIntArray(
                         R.array.config_ambientThresholdsOfPeakRefreshRate);
             }
             restartObserver();
         }
 
-        public void onDeviceConfigRefreshRateInZoneChanged(int refreshRate) {
-            if (refreshRate != mRefreshRateInZone) {
-                mRefreshRateInZone = refreshRate;
+        public void onDeviceConfigRefreshRateInLowZoneChanged(int refreshRate) {
+            if (refreshRate != mRefreshRateInLowZone) {
+                mRefreshRateInLowZone = refreshRate;
+                restartObserver();
+            }
+        }
+
+        public void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds,
+                int[] ambientThresholds) {
+            if (displayThresholds != null && ambientThresholds != null
+                    && displayThresholds.length == ambientThresholds.length) {
+                mHighDisplayBrightnessThresholds = displayThresholds;
+                mHighAmbientBrightnessThresholds = ambientThresholds;
+            } else {
+                // Invalid or empty. Use device default.
+                mHighDisplayBrightnessThresholds = mContext.getResources().getIntArray(
+                        R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
+                mHighAmbientBrightnessThresholds = mContext.getResources().getIntArray(
+                        R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
+            }
+            restartObserver();
+        }
+
+        public void onDeviceConfigRefreshRateInHighZoneChanged(int refreshRate) {
+            if (refreshRate != mRefreshRateInHighZone) {
+                mRefreshRateInHighZone = refreshRate;
                 restartObserver();
             }
         }
@@ -1332,48 +1496,95 @@
         public void dumpLocked(PrintWriter pw) {
             pw.println("  BrightnessObserver");
             pw.println("    mAmbientLux: " + mAmbientLux);
-            pw.println("    mRefreshRateInZone: " + mRefreshRateInZone);
+            pw.println("    mBrightness: " + mBrightness);
+            pw.println("    mDefaultDisplayOn: " + mDefaultDisplayOn);
+            pw.println("    mLowPowerModeEnabled: " + mLowPowerModeEnabled);
+            pw.println("    mRefreshRateChangeable: " + mRefreshRateChangeable);
+            pw.println("    mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange);
+            pw.println("    mShouldObserveAmbientLowChange: " + mShouldObserveAmbientLowChange);
+            pw.println("    mRefreshRateInLowZone: " + mRefreshRateInLowZone);
 
-            for (int d: mDisplayBrightnessThresholds) {
-                pw.println("    mDisplayBrightnessThreshold: " + d);
+            for (int d : mLowDisplayBrightnessThresholds) {
+                pw.println("    mDisplayLowBrightnessThreshold: " + d);
             }
 
-            for (int d: mAmbientBrightnessThresholds) {
-                pw.println("    mAmbientBrightnessThreshold: " + d);
+            for (int d : mLowAmbientBrightnessThresholds) {
+                pw.println("    mAmbientLowBrightnessThreshold: " + d);
+            }
+
+            pw.println("    mShouldObserveDisplayHighChange: " + mShouldObserveDisplayHighChange);
+            pw.println("    mShouldObserveAmbientHighChange: " + mShouldObserveAmbientHighChange);
+            pw.println("    mRefreshRateInHighZone: " + mRefreshRateInHighZone);
+
+            for (int d : mHighDisplayBrightnessThresholds) {
+                pw.println("    mDisplayHighBrightnessThresholds: " + d);
+            }
+
+            for (int d : mHighAmbientBrightnessThresholds) {
+                pw.println("    mAmbientHighBrightnessThresholds: " + d);
             }
 
             mLightSensorListener.dumpLocked(pw);
+
+            if (mAmbientFilter != null) {
+                IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+                ipw.setIndent("    ");
+                mAmbientFilter.dump(ipw);
+            }
         }
 
         public void onDisplayChanged(int displayId) {
             if (displayId == Display.DEFAULT_DISPLAY) {
-                onScreenOn(isDefaultDisplayOn());
+                updateDefaultDisplayState();
             }
         }
 
         @Override
         public void onChange(boolean selfChange, Uri uri, int userId) {
             synchronized (mLock) {
-                onBrightnessChangedLocked();
+                final ContentResolver cr = mContext.getContentResolver();
+                int brightness = Settings.System.getIntForUser(cr,
+                        Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
+                if (brightness != mBrightness) {
+                    mBrightness = brightness;
+                    onBrightnessChangedLocked();
+                }
             }
         }
 
         private void restartObserver() {
-            mShouldObserveDisplayChange = checkShouldObserve(mDisplayBrightnessThresholds);
-            mShouldObserveAmbientChange = checkShouldObserve(mAmbientBrightnessThresholds);
-
             final ContentResolver cr = mContext.getContentResolver();
-            if (mShouldObserveDisplayChange) {
-                // Content Service does not check if an listener has already been registered.
-                // To ensure only one listener is registered, force an unregistration first.
-                cr.unregisterContentObserver(this);
-                cr.registerContentObserver(mDisplayBrightnessSetting,
-                        false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM);
+
+            if (mRefreshRateInLowZone > 0) {
+                mShouldObserveDisplayLowChange = hasValidThreshold(
+                        mLowDisplayBrightnessThresholds);
+                mShouldObserveAmbientLowChange = hasValidThreshold(
+                        mLowAmbientBrightnessThresholds);
             } else {
-                cr.unregisterContentObserver(this);
+                mShouldObserveDisplayLowChange = false;
+                mShouldObserveAmbientLowChange = false;
             }
 
-            if (mShouldObserveAmbientChange) {
+            if (mRefreshRateInHighZone > 0) {
+                mShouldObserveDisplayHighChange = hasValidThreshold(
+                        mHighDisplayBrightnessThresholds);
+                mShouldObserveAmbientHighChange = hasValidThreshold(
+                        mHighAmbientBrightnessThresholds);
+            } else {
+                mShouldObserveDisplayHighChange = false;
+                mShouldObserveAmbientHighChange = false;
+            }
+
+            if (mShouldObserveDisplayLowChange || mShouldObserveDisplayHighChange) {
+                // Content Service does not check if an listener has already been registered.
+                // To ensure only one listener is registered, force an unregistration first.
+                mInjector.unregisterBrightnessObserver(cr, this);
+                mInjector.registerBrightnessObserver(cr, this);
+            } else {
+                mInjector.unregisterBrightnessObserver(cr, this);
+            }
+
+            if (mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) {
                 Resources resources = mContext.getResources();
                 String lightSensorType = resources.getString(
                         com.android.internal.R.string.config_displayLightSensorType);
@@ -1399,8 +1610,6 @@
 
                     mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res);
                     mLightSensor = lightSensor;
-
-                    onScreenOn(isDefaultDisplayOn());
                 }
             } else {
                 mAmbientFilter = null;
@@ -1419,11 +1628,7 @@
          * Checks to see if at least one value is positive, in which case it is necessary to listen
          * to value changes.
          */
-        private boolean checkShouldObserve(int[] a) {
-            if (mRefreshRateInZone <= 0) {
-                return false;
-            }
-
+        private boolean hasValidThreshold(int[] a) {
             for (int d: a) {
                 if (d >= 0) {
                     return true;
@@ -1433,13 +1638,13 @@
             return false;
         }
 
-        private boolean isInsideZone(int brightness, float lux) {
-            for (int i = 0; i < mDisplayBrightnessThresholds.length; i++) {
-                int disp = mDisplayBrightnessThresholds[i];
-                int ambi = mAmbientBrightnessThresholds[i];
+        private boolean isInsideLowZone(int brightness, float lux) {
+            for (int i = 0; i < mLowDisplayBrightnessThresholds.length; i++) {
+                int disp = mLowDisplayBrightnessThresholds[i];
+                int ambi = mLowAmbientBrightnessThresholds[i];
 
                 if (disp >= 0 && ambi >= 0) {
-                    if (brightness <= disp && mAmbientLux <= ambi) {
+                    if (brightness <= disp && lux <= ambi) {
                         return true;
                     }
                 } else if (disp >= 0) {
@@ -1447,7 +1652,7 @@
                         return true;
                     }
                 } else if (ambi >= 0) {
-                    if (mAmbientLux <= ambi) {
+                    if (lux <= ambi) {
                         return true;
                     }
                 }
@@ -1455,28 +1660,77 @@
 
             return false;
         }
-        // TODO: brightnessfloat: make it use float not int
-        private void onBrightnessChangedLocked() {
-            final ContentResolver cr = mContext.getContentResolver();
-            int brightness = Settings.System.getIntForUser(cr,
-                    Settings.System.SCREEN_BRIGHTNESS, -1, cr.getUserId());
 
+        private boolean isInsideHighZone(int brightness, float lux) {
+            for (int i = 0; i < mHighDisplayBrightnessThresholds.length; i++) {
+                int disp = mHighDisplayBrightnessThresholds[i];
+                int ambi = mHighAmbientBrightnessThresholds[i];
+
+                if (disp >= 0 && ambi >= 0) {
+                    if (brightness >= disp && lux >= ambi) {
+                        return true;
+                    }
+                } else if (disp >= 0) {
+                    if (brightness >= disp) {
+                        return true;
+                    }
+                } else if (ambi >= 0) {
+                    if (lux >= ambi) {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+        private void onBrightnessChangedLocked() {
             Vote vote = null;
-            boolean insideZone = isInsideZone(brightness, mAmbientLux);
-            if (insideZone) {
-                vote = Vote.forRefreshRates(mRefreshRateInZone, mRefreshRateInZone);
+
+            if (mBrightness < 0) {
+                // Either the setting isn't available or we shouldn't be observing yet anyways.
+                // Either way, just bail out since there's nothing we can do here.
+                return;
+            }
+
+            boolean insideLowZone = hasValidLowZone() && isInsideLowZone(mBrightness, mAmbientLux);
+            if (insideLowZone) {
+                vote = Vote.forRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone);
+            }
+
+            boolean insideHighZone = hasValidHighZone()
+                    && isInsideHighZone(mBrightness, mAmbientLux);
+            if (insideHighZone) {
+                vote = Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone);
             }
 
             if (DEBUG) {
-                Slog.d(TAG, "Display brightness " + brightness + ", ambient lux " +  mAmbientLux +
-                        ", Vote " + vote);
+                Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " +  mAmbientLux
+                        + ", Vote " + vote);
             }
-            updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote);
+            updateVoteLocked(Vote.PRIORITY_FLICKER, vote);
         }
 
-        private void onScreenOn(boolean on) {
-            if (mScreenOn != on) {
-                mScreenOn = on;
+        private boolean hasValidLowZone() {
+            return mRefreshRateInLowZone > 0
+                    && (mShouldObserveDisplayLowChange || mShouldObserveAmbientLowChange);
+        }
+
+        private boolean hasValidHighZone() {
+            return mRefreshRateInHighZone > 0
+                    && (mShouldObserveDisplayHighChange || mShouldObserveAmbientHighChange);
+        }
+
+        private void updateDefaultDisplayState() {
+            Display display = mContext.getSystemService(DisplayManager.class)
+                    .getDisplay(Display.DEFAULT_DISPLAY);
+            boolean defaultDisplayOn = display != null && display.getState() != Display.STATE_OFF;
+            setDefaultDisplayState(defaultDisplayOn);
+        }
+
+        @VisibleForTesting
+        public void setDefaultDisplayState(boolean on) {
+            if (mDefaultDisplayOn != on) {
+                mDefaultDisplayOn = on;
                 updateSensorStatus();
             }
         }
@@ -1486,8 +1740,8 @@
                 return;
             }
 
-            if (mShouldObserveAmbientChange && mScreenOn && !mLowPowerModeEnabled
-                    && mRefreshRateChangeable) {
+            if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange)
+                     && isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) {
                 mSensorManager.registerListener(mLightSensorListener,
                         mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
             } else {
@@ -1496,11 +1750,8 @@
             }
         }
 
-        private boolean isDefaultDisplayOn() {
-            final Display display = mContext.getSystemService(DisplayManager.class)
-                    .getDisplay(Display.DEFAULT_DISPLAY);
-            return display.getState() != Display.STATE_OFF
-                    && mContext.getSystemService(PowerManager.class).isInteractive();
+        private boolean isDeviceActive() {
+            return mDefaultDisplayOn && mInjector.isDeviceInteractive(mContext);
         }
 
         private final class LightSensorEventListener implements SensorEventListener {
@@ -1518,23 +1769,33 @@
                     Slog.d(TAG, "On sensor changed: " + mLastSensorData);
                 }
 
-                boolean zoneChanged = isDifferentZone(mLastSensorData, mAmbientLux);
-                if (zoneChanged && mLastSensorData < mAmbientLux) {
-                    // Easier to see flicker at lower brightness environment. Forget the history to
-                    // get immediate response.
-                    mAmbientFilter.clear();
+                boolean lowZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
+                        mLowAmbientBrightnessThresholds);
+                boolean highZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
+                        mHighAmbientBrightnessThresholds);
+                if ((lowZoneChanged && mLastSensorData < mAmbientLux)
+                        || (highZoneChanged && mLastSensorData > mAmbientLux)) {
+                    // Easier to see flicker at lower brightness environment or high brightness
+                    // environment. Forget the history to get immediate response.
+                    if (mAmbientFilter != null) {
+                        mAmbientFilter.clear();
+                    }
                 }
 
                 long now = SystemClock.uptimeMillis();
-                mAmbientFilter.addValue(now, mLastSensorData);
+                if (mAmbientFilter != null) {
+                    mAmbientFilter.addValue(now, mLastSensorData);
+                }
 
                 mHandler.removeCallbacks(mInjectSensorEventRunnable);
                 processSensorData(now);
 
-                if (zoneChanged && mLastSensorData > mAmbientLux) {
+                if ((lowZoneChanged && mLastSensorData > mAmbientLux)
+                        || (highZoneChanged && mLastSensorData < mAmbientLux)) {
                     // Sensor may not report new event if there is no brightness change.
                     // Need to keep querying the temporal filter for the latest estimation,
-                    // until enter in higher lux zone or is interrupted by a new sensor event.
+                    // until sensor readout and filter estimation are in the same zone or
+                    // is interrupted by a new sensor event.
                     mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
                 }
             }
@@ -1549,17 +1810,19 @@
             }
 
             private void processSensorData(long now) {
-                mAmbientLux = mAmbientFilter.getEstimate(now);
+                if (mAmbientFilter != null) {
+                    mAmbientLux = mAmbientFilter.getEstimate(now);
+                } else {
+                    mAmbientLux = mLastSensorData;
+                }
 
                 synchronized (mLock) {
                     onBrightnessChangedLocked();
                 }
             }
 
-            private boolean isDifferentZone(float lux1, float lux2) {
-                for (int z = 0; z < mAmbientBrightnessThresholds.length; z++) {
-                    final float boundary = mAmbientBrightnessThresholds[z];
-
+            private boolean isDifferentZone(float lux1, float lux2, int[] luxThresholds) {
+                for (final float boundary : luxThresholds) {
                     // Test each boundary. See if the current value and the new value are at
                     // different sides.
                     if ((lux1 <= boundary && lux2 > boundary)
@@ -1579,7 +1842,10 @@
                     processSensorData(now);
 
                     // Inject next event if there is a possible zone change.
-                    if (isDifferentZone(mLastSensorData, mAmbientLux)) {
+                    if (isDifferentZone(mLastSensorData, mAmbientLux,
+                            mLowAmbientBrightnessThresholds)
+                            || isDifferentZone(mLastSensorData, mAmbientLux,
+                            mHighAmbientBrightnessThresholds)) {
                         mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
                     }
                 }
@@ -1592,33 +1858,75 @@
         }
 
         public void startListening() {
-            DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+            mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
                     BackgroundThread.getExecutor(), this);
         }
 
         /*
          * Return null if no such property or wrong format (not comma separated integers).
          */
-        public int[] getBrightnessThresholds() {
+        public int[] getLowDisplayBrightnessThresholds() {
             return getIntArrayProperty(
                     DisplayManager.DeviceConfig.
-                            KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS);
+                            KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS);
         }
 
         /*
          * Return null if no such property or wrong format (not comma separated integers).
          */
-        public int[] getAmbientThresholds() {
+        public int[] getLowAmbientBrightnessThresholds() {
             return getIntArrayProperty(
                     DisplayManager.DeviceConfig.
-                            KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS);
+                            KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS);
+        }
+
+        public int getRefreshRateInLowZone() {
+            int defaultRefreshRateInZone = mContext.getResources().getInteger(
+                    R.integer.config_defaultRefreshRateInZone);
+
+            int refreshRate = mDeviceConfig.getInt(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE,
+                    defaultRefreshRateInZone);
+
+            return refreshRate;
+        }
+
+        /*
+         * Return null if no such property or wrong format (not comma separated integers).
+         */
+        public int[] getHighDisplayBrightnessThresholds() {
+            return getIntArrayProperty(
+                    DisplayManager.DeviceConfig
+                            .KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS);
+        }
+
+        /*
+         * Return null if no such property or wrong format (not comma separated integers).
+         */
+        public int[] getHighAmbientBrightnessThresholds() {
+            return getIntArrayProperty(
+                    DisplayManager.DeviceConfig
+                            .KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS);
+        }
+
+        public int getRefreshRateInHighZone() {
+            int defaultRefreshRateInZone = mContext.getResources().getInteger(
+                    R.integer.config_fixedRefreshRateInHighZone);
+
+            int refreshRate = mDeviceConfig.getInt(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE,
+                    defaultRefreshRateInZone);
+
+            return refreshRate;
         }
 
         /*
          * Return null if no such property
          */
         public Float getDefaultPeakRefreshRate() {
-            float defaultPeakRefreshRate = DeviceConfig.getFloat(
+            float defaultPeakRefreshRate = mDeviceConfig.getFloat(
                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
                     DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
 
@@ -1628,36 +1936,35 @@
             return defaultPeakRefreshRate;
         }
 
-        public int getRefreshRateInZone() {
-            int defaultRefreshRateInZone = mContext.getResources().getInteger(
-                    R.integer.config_defaultRefreshRateInZone);
-
-            int refreshRate = DeviceConfig.getInt(
-                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
-                    DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE,
-                    defaultRefreshRateInZone);
-
-            return refreshRate;
-        }
-
         @Override
         public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
-            int[] brightnessThresholds = getBrightnessThresholds();
-            int[] ambientThresholds = getAmbientThresholds();
             Float defaultPeakRefreshRate = getDefaultPeakRefreshRate();
-            int refreshRateInZone = getRefreshRateInZone();
-
-            mHandler.obtainMessage(MSG_BRIGHTNESS_THRESHOLDS_CHANGED,
-                    new Pair<int[], int[]>(brightnessThresholds, ambientThresholds))
-                    .sendToTarget();
             mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
                     defaultPeakRefreshRate).sendToTarget();
-            mHandler.obtainMessage(MSG_REFRESH_RATE_IN_ZONE_CHANGED, refreshRateInZone,
-                    0).sendToTarget();
+
+            int[] lowDisplayBrightnessThresholds = getLowDisplayBrightnessThresholds();
+            int[] lowAmbientBrightnessThresholds = getLowAmbientBrightnessThresholds();
+            int refreshRateInLowZone = getRefreshRateInLowZone();
+
+            mHandler.obtainMessage(MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED,
+                    new Pair<>(lowDisplayBrightnessThresholds, lowAmbientBrightnessThresholds))
+                    .sendToTarget();
+            mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone, 0)
+                    .sendToTarget();
+
+            int[] highDisplayBrightnessThresholds = getHighDisplayBrightnessThresholds();
+            int[] highAmbientBrightnessThresholds = getHighAmbientBrightnessThresholds();
+            int refreshRateInHighZone = getRefreshRateInHighZone();
+
+            mHandler.obtainMessage(MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED,
+                    new Pair<>(highDisplayBrightnessThresholds, highAmbientBrightnessThresholds))
+                    .sendToTarget();
+            mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone, 0)
+                    .sendToTarget();
         }
 
         private int[] getIntArrayProperty(String prop) {
-            String strArray = DeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
+            String strArray = mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
                     null);
 
             if (strArray != null) {
@@ -1684,4 +1991,59 @@
         }
     }
 
+    interface Injector {
+        // TODO: brightnessfloat: change this to the float setting
+        Uri DISPLAY_BRIGHTNESS_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
+        Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
+
+        @NonNull
+        DeviceConfigInterface getDeviceConfig();
+
+        void registerBrightnessObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer);
+
+        void unregisterBrightnessObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer);
+
+        void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer);
+
+        boolean isDeviceInteractive(@NonNull Context context);
+    }
+
+    @VisibleForTesting
+    static class RealInjector implements Injector {
+
+        @Override
+        @NonNull
+        public DeviceConfigInterface getDeviceConfig() {
+            return DeviceConfigInterface.REAL;
+        }
+
+        @Override
+        public void registerBrightnessObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            cr.registerContentObserver(DISPLAY_BRIGHTNESS_URI, false /*notifyDescendants*/,
+                    observer, UserHandle.USER_SYSTEM);
+        }
+
+        @Override
+        public void unregisterBrightnessObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            cr.unregisterContentObserver(observer);
+        }
+
+        @Override
+        public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
+                    observer, UserHandle.USER_SYSTEM);
+        }
+
+        @Override
+        public boolean isDeviceInteractive(@NonNull Context ctx) {
+            return ctx.getSystemService(PowerManager.class).isInteractive();
+        }
+    }
+
 }
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 494dd39..b0820e8 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -25,6 +25,7 @@
 import android.util.SparseArray;
 import android.util.SparseLongArray;
 import android.util.TimeUtils;
+import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
 import android.view.Display;
@@ -322,7 +323,7 @@
             return;
         }
 
-        XmlPullParser parser;
+        TypedXmlPullParser parser;
         try {
             parser = Xml.resolvePullParser(is);
             loadFromXml(parser);
@@ -355,7 +356,7 @@
         }
     }
 
-    private void loadFromXml(XmlPullParser parser)
+    private void loadFromXml(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         XmlUtils.beginDocument(parser, TAG_DISPLAY_MANAGER_STATE);
         final int outerDepth = parser.getDepth();
@@ -375,7 +376,7 @@
         }
     }
 
-    private void loadRememberedWifiDisplaysFromXml(XmlPullParser parser)
+    private void loadRememberedWifiDisplaysFromXml(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         final int outerDepth = parser.getDepth();
         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
@@ -399,7 +400,7 @@
         }
     }
 
-    private void loadDisplaysFromXml(XmlPullParser parser)
+    private void loadDisplaysFromXml(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         final int outerDepth = parser.getDepth();
         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
@@ -420,7 +421,7 @@
         }
     }
 
-    private void saveToXml(XmlSerializer serializer) throws IOException {
+    private void saveToXml(TypedXmlSerializer serializer) throws IOException {
         serializer.startDocument(null, true);
         serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
         serializer.startTag(null, TAG_DISPLAY_MANAGER_STATE);
@@ -491,7 +492,7 @@
             return mColorMode;
         }
 
-        public void loadFromXml(XmlPullParser parser)
+        public void loadFromXml(TypedXmlPullParser parser)
                 throws IOException, XmlPullParserException {
             final int outerDepth = parser.getDepth();
 
@@ -503,7 +504,7 @@
             }
         }
 
-        public void saveToXml(XmlSerializer serializer) throws IOException {
+        public void saveToXml(TypedXmlSerializer serializer) throws IOException {
             serializer.startTag(null, TAG_COLOR_MODE);
             serializer.text(Integer.toString(mColorMode));
             serializer.endTag(null, TAG_COLOR_MODE);
@@ -531,7 +532,8 @@
             return false;
         }
 
-        public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
+        public void loadFromXml(TypedXmlPullParser parser)
+                throws IOException, XmlPullParserException {
             final int outerDepth = parser.getDepth();
             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
                 switch (parser.getName()) {
@@ -545,7 +547,7 @@
             }
         }
 
-        private static int loadIntValue(XmlPullParser parser)
+        private static int loadIntValue(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
             try {
                 String value = parser.nextText();
@@ -555,7 +557,7 @@
             }
         }
 
-        public void saveToXml(XmlSerializer serializer) throws IOException {
+        public void saveToXml(TypedXmlSerializer serializer) throws IOException {
             if (mWidth > 0 && mHeight > 0) {
                 serializer.startTag(null, TAG_STABLE_DISPLAY_WIDTH);
                 serializer.text(Integer.toString(mWidth));
@@ -612,14 +614,14 @@
             return mConfigurations.get(userSerial);
         }
 
-        public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
+        public void loadFromXml(TypedXmlPullParser parser)
+                throws IOException, XmlPullParserException {
             final int outerDepth = parser.getDepth();
             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
                 if (TAG_BRIGHTNESS_CONFIGURATION.equals(parser.getName())) {
                     int userSerial;
                     try {
-                        userSerial = Integer.parseInt(
-                                parser.getAttributeValue(null, ATTR_USER_SERIAL));
+                        userSerial = parser.getAttributeInt(null, ATTR_USER_SERIAL);
                     } catch (NumberFormatException nfe) {
                         userSerial = -1;
                         Slog.e(TAG, "Failed to read in brightness configuration", nfe);
@@ -655,20 +657,20 @@
             }
         }
 
-        public void saveToXml(XmlSerializer serializer) throws IOException {
+        public void saveToXml(TypedXmlSerializer serializer) throws IOException {
             for (int i = 0; i < mConfigurations.size(); i++) {
                 final int userSerial = mConfigurations.keyAt(i);
                 final BrightnessConfiguration config = mConfigurations.valueAt(i);
 
                 serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATION);
-                serializer.attribute(null, ATTR_USER_SERIAL, Integer.toString(userSerial));
+                serializer.attributeInt(null, ATTR_USER_SERIAL, userSerial);
                 String packageName = mPackageNames.get(userSerial);
                 if (packageName != null) {
                     serializer.attribute(null, ATTR_PACKAGE_NAME, packageName);
                 }
                 long timestamp = mTimeStamps.get(userSerial, -1);
                 if (timestamp != -1) {
-                    serializer.attribute(null, ATTR_TIME_STAMP, Long.toString(timestamp));
+                    serializer.attributeLong(null, ATTR_TIME_STAMP, timestamp);
                 }
                 config.saveToXml(serializer);
                 serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATION);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 11e18c5..ad3773e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1186,10 +1186,11 @@
         // When the device is not unplugged but reawaken from standby, we check if the System
         // Audio Control Feature is enabled or not then decide if turning SAM on/off accordingly.
         if (getAvrDeviceInfo() != null && portId == getAvrDeviceInfo().getPortId()) {
+            HdmiLogger.debug("Port ID:%d, 5v=%b", portId, connected);
             if (!connected) {
                 setSystemAudioMode(false);
-            } else if (mSystemAudioControlFeatureEnabled != mService.isSystemAudioActivated()){
-                setSystemAudioMode(mSystemAudioControlFeatureEnabled);
+            } else {
+                onNewAvrAdded(getAvrDeviceInfo());
             }
         }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index e47b544..52a804a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -25,6 +25,7 @@
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
 import android.util.Xml;
 
 import com.android.internal.util.HexDump;
@@ -571,14 +572,13 @@
         // return a list of devices config
         public static List<DeviceConfig> parse(InputStream in)
                 throws XmlPullParserException, IOException {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
-            parser.setInput(in, null);
+            TypedXmlPullParser parser = Xml.resolvePullParser(in);
             parser.nextTag();
             return readDevices(parser);
         }
 
-        private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
+        private static void skip(TypedXmlPullParser parser)
+                throws XmlPullParserException, IOException {
             if (parser.getEventType() != XmlPullParser.START_TAG) {
                 throw new IllegalStateException();
             }
@@ -595,7 +595,7 @@
             }
         }
 
-        private static List<DeviceConfig> readDevices(XmlPullParser parser)
+        private static List<DeviceConfig> readDevices(TypedXmlPullParser parser)
                 throws XmlPullParserException, IOException {
             List<DeviceConfig> devices = new ArrayList<>();
 
@@ -624,7 +624,7 @@
 
         // Processes device tags in the config.
         @Nullable
-        private static DeviceConfig readDeviceConfig(XmlPullParser parser, String deviceType)
+        private static DeviceConfig readDeviceConfig(TypedXmlPullParser parser, String deviceType)
                 throws XmlPullParserException, IOException {
             List<CodecSad> codecSads = new ArrayList<>();
             int format;
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index 6633789..db93ad0 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -102,12 +102,12 @@
     }
 
     private void setArcStatus(boolean enabled) {
-        boolean wasEnabled = tv().setArcStatus(enabled);
-        Slog.i(TAG, "Change arc status [old:" + wasEnabled + ", new:" + enabled + "]");
+        tv().setArcStatus(enabled);
+        Slog.i(TAG, "Change arc status to " + enabled);
 
         // If enabled before and set to "disabled" and send <Report Arc Terminated> to
         // av reciever.
-        if (!enabled && wasEnabled) {
+        if (!enabled) {
             sendCommand(HdmiCecMessageBuilder.buildReportArcTerminated(getSourceAddress(),
                     mAvrAddress));
         }
diff --git a/services/core/java/com/android/server/input/ConfigurationProcessor.java b/services/core/java/com/android/server/input/ConfigurationProcessor.java
index 3888b1b..0563806 100644
--- a/services/core/java/com/android/server/input/ConfigurationProcessor.java
+++ b/services/core/java/com/android/server/input/ConfigurationProcessor.java
@@ -18,15 +18,13 @@
 
 import android.text.TextUtils;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.XmlUtils;
 
-import org.xmlpull.v1.XmlPullParser;
-
 import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -38,9 +36,8 @@
 
     static List<String> processExcludedDeviceNames(InputStream xml) throws Exception {
         List<String> names = new ArrayList<>();
-        try (InputStreamReader confReader = new InputStreamReader(xml)) {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(confReader);
+        {
+            TypedXmlPullParser parser = Xml.resolvePullParser(xml);
             XmlUtils.beginDocument(parser, "devices");
             while (true) {
                 XmlUtils.nextElement(parser);
@@ -90,9 +87,8 @@
     static Map<String, Integer> processInputPortAssociations(InputStream xml)
             throws Exception {
         Map<String, Integer> associations = new HashMap<String, Integer>();
-        try (InputStreamReader confReader = new InputStreamReader(xml)) {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(confReader);
+        {
+            TypedXmlPullParser parser = Xml.resolvePullParser(xml);
             XmlUtils.beginDocument(parser, "ports");
 
             while (true) {
diff --git a/services/core/java/com/android/server/input/PersistentDataStore.java b/services/core/java/com/android/server/input/PersistentDataStore.java
index f61662d..a735a8f 100644
--- a/services/core/java/com/android/server/input/PersistentDataStore.java
+++ b/services/core/java/com/android/server/input/PersistentDataStore.java
@@ -253,7 +253,7 @@
         }
     }
 
-    private void loadFromXml(XmlPullParser parser)
+    private void loadFromXml(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         XmlUtils.beginDocument(parser, "input-manager-state");
         final int outerDepth = parser.getDepth();
@@ -264,7 +264,7 @@
         }
     }
 
-    private void loadInputDevicesFromXml(XmlPullParser parser)
+    private void loadInputDevicesFromXml(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         final int outerDepth = parser.getDepth();
         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
@@ -285,7 +285,7 @@
         }
     }
 
-    private void saveToXml(XmlSerializer serializer) throws IOException {
+    private void saveToXml(TypedXmlSerializer serializer) throws IOException {
         serializer.startDocument(null, true);
         serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
         serializer.startTag(null, "input-manager-state");
@@ -422,7 +422,7 @@
             return changed;
         }
 
-        public void loadFromXml(XmlPullParser parser)
+        public void loadFromXml(TypedXmlPullParser parser)
                 throws IOException, XmlPullParserException {
             final int outerDepth = parser.getDepth();
             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
@@ -505,7 +505,7 @@
             }
         }
 
-        public void saveToXml(XmlSerializer serializer) throws IOException {
+        public void saveToXml(TypedXmlSerializer serializer) throws IOException {
             for (String layout : mKeyboardLayouts) {
                 serializer.startTag(null, "keyboard-layout");
                 serializer.attribute(null, "descriptor", layout);
diff --git a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
index a077b04..24b8e34 100644
--- a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
+++ b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
@@ -31,17 +31,13 @@
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
 
-import com.android.internal.util.FastXmlSerializer;
-
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -158,20 +154,17 @@
                     final InputMethodSubtype subtype = subtypesList.get(i);
                     out.startTag(null, NODE_SUBTYPE);
                     if (subtype.hasSubtypeId()) {
-                        out.attribute(null, ATTR_IME_SUBTYPE_ID,
-                                String.valueOf(subtype.getSubtypeId()));
+                        out.attributeInt(null, ATTR_IME_SUBTYPE_ID, subtype.getSubtypeId());
                     }
-                    out.attribute(null, ATTR_ICON, String.valueOf(subtype.getIconResId()));
-                    out.attribute(null, ATTR_LABEL, String.valueOf(subtype.getNameResId()));
+                    out.attributeInt(null, ATTR_ICON, subtype.getIconResId());
+                    out.attributeInt(null, ATTR_LABEL, subtype.getNameResId());
                     out.attribute(null, ATTR_IME_SUBTYPE_LOCALE, subtype.getLocale());
                     out.attribute(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG,
                             subtype.getLanguageTag());
                     out.attribute(null, ATTR_IME_SUBTYPE_MODE, subtype.getMode());
                     out.attribute(null, ATTR_IME_SUBTYPE_EXTRA_VALUE, subtype.getExtraValue());
-                    out.attribute(null, ATTR_IS_AUXILIARY,
-                            String.valueOf(subtype.isAuxiliary() ? 1 : 0));
-                    out.attribute(null, ATTR_IS_ASCII_CAPABLE,
-                            String.valueOf(subtype.isAsciiCapable() ? 1 : 0));
+                    out.attributeInt(null, ATTR_IS_AUXILIARY, subtype.isAuxiliary() ? 1 : 0);
+                    out.attributeInt(null, ATTR_IS_ASCII_CAPABLE, subtype.isAsciiCapable() ? 1 : 0);
                     out.endTag(null, NODE_SUBTYPE);
                 }
                 out.endTag(null, NODE_IMI);
@@ -243,10 +236,8 @@
                         Slog.w(TAG, "IME uninstalled or not valid.: " + currentImiId);
                         continue;
                     }
-                    final int icon = Integer.parseInt(
-                            parser.getAttributeValue(null, ATTR_ICON));
-                    final int label = Integer.parseInt(
-                            parser.getAttributeValue(null, ATTR_LABEL));
+                    final int icon = parser.getAttributeInt(null, ATTR_ICON);
+                    final int label = parser.getAttributeInt(null, ATTR_LABEL);
                     final String imeSubtypeLocale =
                             parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LOCALE);
                     final String languageTag =
@@ -269,10 +260,10 @@
                             .setSubtypeExtraValue(imeSubtypeExtraValue)
                             .setIsAuxiliary(isAuxiliary)
                             .setIsAsciiCapable(isAsciiCapable);
-                    final String subtypeIdString =
-                            parser.getAttributeValue(null, ATTR_IME_SUBTYPE_ID);
-                    if (subtypeIdString != null) {
-                        builder.setSubtypeId(Integer.parseInt(subtypeIdString));
+                    final int subtypeId = parser.getAttributeInt(null, ATTR_IME_SUBTYPE_ID,
+                            InputMethodSubtype.SUBTYPE_ID_NONE);
+                    if (subtypeId != InputMethodSubtype.SUBTYPE_ID_NONE) {
+                        builder.setSubtypeId(subtypeId);
                     }
                     tempSubtypesArray.add(builder.build());
                 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 0ceaf77..6395094 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -158,6 +158,8 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.content.PackageMonitor;
+import com.android.internal.inputmethod.CallbackUtils;
+import com.android.internal.inputmethod.IInputBindResultResultCallback;
 import com.android.internal.inputmethod.IInputContentUriToken;
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.InputMethodDebug;
@@ -208,6 +210,7 @@
 import java.util.WeakHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
 
 /**
  * This class provides a system service that manages input methods.
@@ -1534,12 +1537,14 @@
 
         @Override
         public void sessionCreated(IInputMethodSession session) {
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.sessionCreated");
             final long ident = Binder.clearCallingIdentity();
             try {
                 mParentIMMS.onSessionCreated(mMethod, session, mChannel);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
@@ -2284,6 +2289,8 @@
                 }
 
                 if (mCurClient == cs) {
+                    hideCurrentInputLocked(
+                            mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_REMOVE_CLIENT);
                     if (mBoundToMethod) {
                         mBoundToMethod = false;
                         if (mCurMethod != null) {
@@ -2610,6 +2617,7 @@
 
     @Override
     public void onServiceConnected(ComponentName name, IBinder service) {
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.onServiceConnected");
         synchronized (mMethodMap) {
             if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
                 mCurMethod = IInputMethod.Stub.asInterface(service);
@@ -2625,6 +2633,7 @@
                 if (mCurToken == null) {
                     Slog.w(TAG, "Service connected without a token!");
                     unbindCurrentMethodLocked();
+                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                     return;
                 }
                 if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
@@ -2638,6 +2647,7 @@
                 }
             }
         }
+        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
     void onSessionCreated(IInputMethod method, IInputMethodSession session,
@@ -3345,63 +3355,68 @@
 
     @NonNull
     @Override
-    public InputBindResult startInputOrWindowGainedFocus(
+    public void startInputOrWindowGainedFocus(
             @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
             @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
             int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
-            @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) {
-        if (windowToken == null) {
-            Slog.e(TAG, "windowToken cannot be null.");
-            return InputBindResult.NULL;
-        }
-        try {
-            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
-                    "IMMS.startInputOrWindowGainedFocus");
-            ImeTracing.getInstance().triggerManagerServiceDump(
-                    "InputMethodManagerService#startInputOrWindowGainedFocus");
-            final int callingUserId = UserHandle.getCallingUserId();
-            final int userId;
-            if (attribute != null && attribute.targetInputMethodUser != null
-                    && attribute.targetInputMethodUser.getIdentifier() != callingUserId) {
-                mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                        "Using EditorInfo.targetInputMethodUser requires"
-                                + " INTERACT_ACROSS_USERS_FULL.");
-                userId = attribute.targetInputMethodUser.getIdentifier();
-                if (!mUserManagerInternal.isUserRunning(userId)) {
-                    // There is a chance that we hit here because of race condition.  Let's just
-                    // return an error code instead of crashing the caller process, which at least
-                    // has INTERACT_ACROSS_USERS_FULL permission thus is likely to be an important
-                    // process.
-                    Slog.e(TAG, "User #" + userId + " is not running.");
-                    return InputBindResult.INVALID_USER;
-                }
-            } else {
-                userId = callingUserId;
-            }
-            final InputBindResult result;
-            synchronized (mMethodMap) {
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    result = startInputOrWindowGainedFocusInternalLocked(startInputReason, client,
-                            windowToken, startInputFlags, softInputMode, windowFlags, attribute,
-                            inputContext, missingMethods, unverifiedTargetSdkVersion, userId);
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
-            }
-            if (result == null) {
-                // This must never happen, but just in case.
-                Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason="
-                        + InputMethodDebug.startInputReasonToString(startInputReason)
-                        + " windowFlags=#" + Integer.toHexString(windowFlags)
-                        + " editorInfo=" + attribute);
+            @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion,
+            IInputBindResultResultCallback resultCallback) {
+        CallbackUtils.onResult(resultCallback, (Supplier<InputBindResult>) () -> {
+            if (windowToken == null) {
+                Slog.e(TAG, "windowToken cannot be null.");
                 return InputBindResult.NULL;
             }
+            try {
+                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
+                        "IMMS.startInputOrWindowGainedFocus");
+                ImeTracing.getInstance().triggerManagerServiceDump(
+                        "InputMethodManagerService#startInputOrWindowGainedFocus");
+                final int callingUserId = UserHandle.getCallingUserId();
+                final int userId;
+                if (attribute != null && attribute.targetInputMethodUser != null
+                        && attribute.targetInputMethodUser.getIdentifier() != callingUserId) {
+                    mContext.enforceCallingPermission(
+                            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                            "Using EditorInfo.targetInputMethodUser requires"
+                                    + " INTERACT_ACROSS_USERS_FULL.");
+                    userId = attribute.targetInputMethodUser.getIdentifier();
+                    if (!mUserManagerInternal.isUserRunning(userId)) {
+                        // There is a chance that we hit here because of race condition. Let's just
+                        // return an error code instead of crashing the caller process, which at
+                        // least has INTERACT_ACROSS_USERS_FULL permission thus is likely to be an
+                        // important process.
+                        Slog.e(TAG, "User #" + userId + " is not running.");
+                        return InputBindResult.INVALID_USER;
+                    }
+                } else {
+                    userId = callingUserId;
+                }
+                final InputBindResult result;
+                synchronized (mMethodMap) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
+                                client, windowToken, startInputFlags, softInputMode, windowFlags,
+                                attribute, inputContext, missingMethods, unverifiedTargetSdkVersion,
+                                userId);
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                }
+                if (result == null) {
+                    // This must never happen, but just in case.
+                    Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason="
+                            + InputMethodDebug.startInputReasonToString(startInputReason)
+                            + " windowFlags=#" + Integer.toHexString(windowFlags)
+                            + " editorInfo=" + attribute);
+                    return InputBindResult.NULL;
+                }
 
-            return result;
-        } finally {
-            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-        }
+                return result;
+            } finally {
+                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+            }
+        });
     }
 
     @NonNull
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 62d817c..6bdae63 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -72,6 +72,8 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.CallbackUtils;
+import com.android.internal.inputmethod.IInputBindResultResultCallback;
 import com.android.internal.inputmethod.IMultiClientInputMethod;
 import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.IMultiClientInputMethodSession;
@@ -104,6 +106,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.WeakHashMap;
+import java.util.function.Supplier;
 
 /**
  * Actual implementation of multi-client InputMethodManagerService.
@@ -1588,7 +1591,26 @@
 
         @BinderThread
         @Override
-        public InputBindResult startInputOrWindowGainedFocus(
+        public void startInputOrWindowGainedFocus(
+                @StartInputReason int startInputReason,
+                @Nullable IInputMethodClient client,
+                @Nullable IBinder windowToken,
+                @StartInputFlags int startInputFlags,
+                @SoftInputModeFlags int softInputMode,
+                int windowFlags,
+                @Nullable EditorInfo editorInfo,
+                @Nullable IInputContext inputContext,
+                @MissingMethodFlags int missingMethods,
+                int unverifiedTargetSdkVersion,
+                IInputBindResultResultCallback resultCallback) {
+            CallbackUtils.onResult(resultCallback, (Supplier<InputBindResult>) () ->
+                    startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken,
+                            startInputFlags, softInputMode, windowFlags, editorInfo, inputContext,
+                            missingMethods, unverifiedTargetSdkVersion));
+        }
+
+        @BinderThread
+        private InputBindResult startInputOrWindowGainedFocusInternal(
                 @StartInputReason int startInputReason,
                 @Nullable IInputMethodClient client,
                 @Nullable IBinder windowToken,
@@ -1676,8 +1698,7 @@
                                 clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus(
                                         inputContext, missingMethods, editorInfo, startInputFlags,
                                         softInputMode, windowHandle);
-                            } catch (RemoteException e) {
-                            }
+                            } catch (RemoteException ignored) { }
                             break;
                     }
                     return InputBindResult.NULL_EDITOR_INFO;
@@ -1708,8 +1729,7 @@
                             clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus(
                                     inputContext, missingMethods, editorInfo, startInputFlags,
                                     softInputMode, windowHandle);
-                        } catch (RemoteException e) {
-                        }
+                        } catch (RemoteException ignored) { }
                         clientInfo.mState = InputMethodClientState.ALREADY_SENT_BIND_RESULT;
                         return new InputBindResult(
                                 InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
diff --git a/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java b/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java
index 28d2e69..ab91290 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java
@@ -17,6 +17,7 @@
 package com.android.server.integrity.parser;
 
 import android.annotation.Nullable;
+import android.util.TypedXmlPullParser;
 import android.util.Xml;
 
 import com.android.server.integrity.model.RuleMetadata;
@@ -26,7 +27,6 @@
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
 
 /** Helper class for parsing rule metadata. */
 public class RuleMetadataParser {
@@ -42,8 +42,7 @@
         String ruleProvider = "";
         String version = "";
 
-        XmlPullParser xmlPullParser = Xml.newPullParser();
-        xmlPullParser.setInput(inputStream, StandardCharsets.UTF_8.name());
+        TypedXmlPullParser xmlPullParser = Xml.resolvePullParser(inputStream);
 
         int eventType;
         while ((eventType = xmlPullParser.next()) != XmlPullParser.END_DOCUMENT) {
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java
index 5c51f31..7aed352 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java
@@ -19,6 +19,7 @@
 import static com.android.server.integrity.parser.RuleMetadataParser.RULE_PROVIDER_TAG;
 import static com.android.server.integrity.parser.RuleMetadataParser.VERSION_TAG;
 
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.server.integrity.model.RuleMetadata;
@@ -34,8 +35,7 @@
     /** Serialize the rule metadata to an output stream. */
     public static void serialize(RuleMetadata ruleMetadata, OutputStream outputStream)
             throws IOException {
-        XmlSerializer xmlSerializer = Xml.newSerializer();
-        xmlSerializer.setOutput(outputStream, StandardCharsets.UTF_8.name());
+        TypedXmlSerializer xmlSerializer = Xml.resolveSerializer(outputStream);
 
         serializeTaggedValue(xmlSerializer, RULE_PROVIDER_TAG, ruleMetadata.getRuleProvider());
         serializeTaggedValue(xmlSerializer, VERSION_TAG, ruleMetadata.getVersion());
@@ -43,8 +43,8 @@
         xmlSerializer.endDocument();
     }
 
-    private static void serializeTaggedValue(XmlSerializer xmlSerializer, String tag, String value)
-            throws IOException {
+    private static void serializeTaggedValue(TypedXmlSerializer xmlSerializer, String tag,
+            String value) throws IOException {
         xmlSerializer.startTag(/* namespace= */ null, tag);
         xmlSerializer.text(value);
         xmlSerializer.endTag(/* namespace= */ null, tag);
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
index 8a19d62..0c209c5 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
@@ -18,7 +18,6 @@
 
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.CERTIFICATE_FACTORY_TYPE;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.NAMESPACE;
-import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.OUTPUT_ENCODING;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALGORITHM;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY;
@@ -46,6 +45,7 @@
 import android.security.keystore.recovery.KeyDerivationParams;
 import android.security.keystore.recovery.WrappedApplicationKey;
 import android.util.Base64;
+import android.util.TypedXmlPullParser;
 import android.util.Xml;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -84,8 +84,7 @@
 
     private static KeyChainSnapshot deserializeInternal(InputStream inputStream) throws IOException,
             XmlPullParserException, KeyChainSnapshotParserException {
-        XmlPullParser parser = Xml.newPullParser();
-        parser.setInput(inputStream, OUTPUT_ENCODING);
+        TypedXmlPullParser parser = Xml.resolvePullParser(inputStream);
 
         parser.nextTag();
         parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_SNAPSHOT);
@@ -156,7 +155,7 @@
         }
     }
 
-    private static List<WrappedApplicationKey> readWrappedApplicationKeys(XmlPullParser parser)
+    private static List<WrappedApplicationKey> readWrappedApplicationKeys(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
         parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_APPLICATION_KEYS);
         ArrayList<WrappedApplicationKey> keys = new ArrayList<>();
@@ -170,7 +169,7 @@
         return keys;
     }
 
-    private static WrappedApplicationKey readWrappedApplicationKey(XmlPullParser parser)
+    private static WrappedApplicationKey readWrappedApplicationKey(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
         parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_APPLICATION_KEY);
         WrappedApplicationKey.Builder builder = new WrappedApplicationKey.Builder();
@@ -209,7 +208,7 @@
     }
 
     private static List<KeyChainProtectionParams> readKeyChainProtectionParamsList(
-            XmlPullParser parser) throws IOException, XmlPullParserException,
+            TypedXmlPullParser parser) throws IOException, XmlPullParserException,
             KeyChainSnapshotParserException {
         parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST);
 
@@ -225,7 +224,7 @@
         return keyChainProtectionParamsList;
     }
 
-    private static KeyChainProtectionParams readKeyChainProtectionParams(XmlPullParser parser)
+    private static KeyChainProtectionParams readKeyChainProtectionParams(TypedXmlPullParser parser)
         throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
         parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS);
 
@@ -269,7 +268,7 @@
         }
     }
 
-    private static KeyDerivationParams readKeyDerivationParams(XmlPullParser parser)
+    private static KeyDerivationParams readKeyDerivationParams(TypedXmlPullParser parser)
             throws XmlPullParserException, IOException, KeyChainSnapshotParserException {
         parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_DERIVATION_PARAMS);
 
@@ -331,7 +330,7 @@
         return keyDerivationParams;
     }
 
-    private static int readIntTag(XmlPullParser parser, String tagName)
+    private static int readIntTag(TypedXmlPullParser parser, String tagName)
             throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
         parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
         String text = readText(parser);
@@ -345,7 +344,7 @@
         }
     }
 
-    private static long readLongTag(XmlPullParser parser, String tagName)
+    private static long readLongTag(TypedXmlPullParser parser, String tagName)
             throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
         parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
         String text = readText(parser);
@@ -359,7 +358,7 @@
         }
     }
 
-    private static String readStringTag(XmlPullParser parser, String tagName)
+    private static String readStringTag(TypedXmlPullParser parser, String tagName)
             throws IOException, XmlPullParserException {
         parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
         String text = readText(parser);
@@ -367,7 +366,7 @@
         return text;
     }
 
-    private static byte[] readBlobTag(XmlPullParser parser, String tagName)
+    private static byte[] readBlobTag(TypedXmlPullParser parser, String tagName)
             throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
         parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
         String text = readText(parser);
@@ -384,7 +383,7 @@
         }
     }
 
-    private static CertPath readCertPathTag(XmlPullParser parser, String tagName)
+    private static CertPath readCertPathTag(TypedXmlPullParser parser, String tagName)
             throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
         byte[] bytes = readBlobTag(parser, tagName);
         try {
@@ -396,7 +395,7 @@
         }
     }
 
-    private static String readText(XmlPullParser parser)
+    private static String readText(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         String result = "";
         if (parser.next() == XmlPullParser.TEXT) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
index 8f85a27..6475d9e 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
@@ -22,8 +22,6 @@
 class KeyChainSnapshotSchema {
     static final String NAMESPACE = null;
 
-    static final String OUTPUT_ENCODING = "UTF-8";
-
     static final String CERTIFICATE_FACTORY_TYPE = "X.509";
     static final String CERT_PATH_ENCODING = "PkiPath";
 
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
index 527e879..eb34e98 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
@@ -19,7 +19,6 @@
 
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.CERT_PATH_ENCODING;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.NAMESPACE;
-import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.OUTPUT_ENCODING;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALGORITHM;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY;
@@ -47,10 +46,9 @@
 import android.security.keystore.recovery.KeyDerivationParams;
 import android.security.keystore.recovery.WrappedApplicationKey;
 import android.util.Base64;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
-import org.xmlpull.v1.XmlSerializer;
-
 import java.io.IOException;
 import java.io.OutputStream;
 import java.security.cert.CertPath;
@@ -71,8 +69,7 @@
      */
     public static void serialize(KeyChainSnapshot keyChainSnapshot, OutputStream outputStream)
             throws IOException, CertificateEncodingException {
-        XmlSerializer xmlSerializer = Xml.newSerializer();
-        xmlSerializer.setOutput(outputStream, OUTPUT_ENCODING);
+        TypedXmlSerializer xmlSerializer = Xml.resolveSerializer(outputStream);
         xmlSerializer.startDocument(
                 /*encoding=*/ null,
                 /*standalone=*/ null);
@@ -87,7 +84,7 @@
     }
 
     private static void writeApplicationKeys(
-            XmlSerializer xmlSerializer, List<WrappedApplicationKey> wrappedApplicationKeys)
+            TypedXmlSerializer xmlSerializer, List<WrappedApplicationKey> wrappedApplicationKeys)
             throws IOException {
         xmlSerializer.startTag(NAMESPACE, TAG_APPLICATION_KEYS);
         for (WrappedApplicationKey key : wrappedApplicationKeys) {
@@ -98,15 +95,15 @@
         xmlSerializer.endTag(NAMESPACE, TAG_APPLICATION_KEYS);
     }
 
-    private static void writeApplicationKeyProperties(
-            XmlSerializer xmlSerializer, WrappedApplicationKey applicationKey) throws IOException {
+    private static void writeApplicationKeyProperties(TypedXmlSerializer xmlSerializer,
+            WrappedApplicationKey applicationKey) throws IOException {
         writePropertyTag(xmlSerializer, TAG_ALIAS, applicationKey.getAlias());
         writePropertyTag(xmlSerializer, TAG_KEY_MATERIAL, applicationKey.getEncryptedKeyMaterial());
         writePropertyTag(xmlSerializer, TAG_KEY_METADATA, applicationKey.getMetadata());
     }
 
     private static void writeKeyChainProtectionParams(
-            XmlSerializer xmlSerializer,
+            TypedXmlSerializer xmlSerializer,
             List<KeyChainProtectionParams> keyChainProtectionParamsList) throws IOException {
         xmlSerializer.startTag(NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST);
         for (KeyChainProtectionParams keyChainProtectionParams : keyChainProtectionParamsList) {
@@ -118,7 +115,7 @@
     }
 
     private static void writeKeyChainProtectionParamsProperties(
-            XmlSerializer xmlSerializer, KeyChainProtectionParams keyChainProtectionParams)
+            TypedXmlSerializer xmlSerializer, KeyChainProtectionParams keyChainProtectionParams)
             throws IOException {
         writePropertyTag(xmlSerializer, TAG_USER_SECRET_TYPE,
                 keyChainProtectionParams.getUserSecretType());
@@ -132,7 +129,7 @@
     }
 
     private static void writeKeyDerivationParams(
-            XmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams)
+            TypedXmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams)
             throws IOException {
         xmlSerializer.startTag(NAMESPACE, TAG_KEY_DERIVATION_PARAMS);
         writeKeyDerivationParamsProperties(
@@ -141,7 +138,7 @@
     }
 
     private static void writeKeyDerivationParamsProperties(
-            XmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams)
+            TypedXmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams)
             throws IOException {
         writePropertyTag(xmlSerializer, TAG_ALGORITHM, keyDerivationParams.getAlgorithm());
         writePropertyTag(xmlSerializer, TAG_SALT, keyDerivationParams.getSalt());
@@ -150,7 +147,7 @@
     }
 
     private static void writeKeyChainSnapshotProperties(
-            XmlSerializer xmlSerializer, KeyChainSnapshot keyChainSnapshot)
+            TypedXmlSerializer xmlSerializer, KeyChainSnapshot keyChainSnapshot)
             throws IOException, CertificateEncodingException {
 
         writePropertyTag(xmlSerializer, TAG_SNAPSHOT_VERSION,
@@ -165,7 +162,7 @@
     }
 
     private static void writePropertyTag(
-            XmlSerializer xmlSerializer, String propertyName, long propertyValue)
+            TypedXmlSerializer xmlSerializer, String propertyName, long propertyValue)
             throws IOException {
         xmlSerializer.startTag(NAMESPACE, propertyName);
         xmlSerializer.text(Long.toString(propertyValue));
@@ -173,7 +170,7 @@
     }
 
     private static void writePropertyTag(
-            XmlSerializer xmlSerializer, String propertyName, String propertyValue)
+            TypedXmlSerializer xmlSerializer, String propertyName, String propertyValue)
             throws IOException {
         xmlSerializer.startTag(NAMESPACE, propertyName);
         xmlSerializer.text(propertyValue);
@@ -181,7 +178,7 @@
     }
 
     private static void writePropertyTag(
-            XmlSerializer xmlSerializer, String propertyName, @Nullable byte[] propertyValue)
+            TypedXmlSerializer xmlSerializer, String propertyName, @Nullable byte[] propertyValue)
             throws IOException {
         if (propertyValue == null) {
             return;
@@ -192,7 +189,7 @@
     }
 
     private static void writePropertyTag(
-            XmlSerializer xmlSerializer, String propertyName, CertPath certPath)
+            TypedXmlSerializer xmlSerializer, String propertyName, CertPath certPath)
             throws IOException, CertificateEncodingException {
         writePropertyTag(xmlSerializer, propertyName, certPath.getEncoded(CERT_PATH_ENCODING));
     }
diff --git a/services/core/java/com/android/server/media/MediaKeyDispatcher.java b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
index 0dbc839..63618ee 100644
--- a/services/core/java/com/android/server/media/MediaKeyDispatcher.java
+++ b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
+import android.content.Context;
 import android.media.session.ISessionManager;
 import android.media.session.MediaSession;
 import android.os.Binder;
@@ -60,7 +61,7 @@
 
     private Map<Integer, Integer> mOverriddenKeyEvents;
 
-    public MediaKeyDispatcher() {
+    public MediaKeyDispatcher(Context context) {
         // Constructor used for reflection
         mOverriddenKeyEvents = new HashMap<>();
         mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_PLAY, 0);
diff --git a/services/core/java/com/android/server/media/MediaServerUtils.java b/services/core/java/com/android/server/media/MediaServerUtils.java
index 5fa2b1c..a4f11b2 100644
--- a/services/core/java/com/android/server/media/MediaServerUtils.java
+++ b/services/core/java/com/android/server/media/MediaServerUtils.java
@@ -18,6 +18,9 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.AudioPlaybackConfiguration;
 import android.os.Binder;
 
 import java.io.PrintWriter;
@@ -29,7 +32,7 @@
     /**
      * Verify that caller holds {@link android.Manifest.permission#DUMP}.
      */
-    public static boolean checkDumpPermission(Context context, String tag, PrintWriter pw) {
+    static boolean checkDumpPermission(Context context, String tag, PrintWriter pw) {
         if (context.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
             pw.println("Permission Denial: can't dump " + tag + " from from pid="
@@ -40,4 +43,18 @@
             return true;
         }
     }
+
+    /**
+     * Whether the given stream is currently active or not.
+     */
+    static boolean isStreamActive(AudioManager audioManager, int stream) {
+        for (AudioPlaybackConfiguration configuration
+                : audioManager.getActivePlaybackConfigurations()) {
+            AudioAttributes attributes = configuration.getAudioAttributes();
+            if (attributes != null && attributes.getVolumeControlStream() == stream) {
+                return configuration.isActive();
+            }
+        }
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index f997352..ea6e7d7 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -23,7 +23,6 @@
 import android.content.pm.ParceledListSlice;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
-import android.media.AudioSystem;
 import android.media.MediaMetadata;
 import android.media.Rating;
 import android.media.VolumeProvider;
@@ -513,7 +512,7 @@
             public void run() {
                 try {
                     if (useSuggested) {
-                        if (AudioSystem.isStreamActive(stream, 0)) {
+                        if (MediaServerUtils.isStreamActive(mAudioManager, stream)) {
                             mAudioManager.adjustSuggestedStreamVolumeForUid(stream,
                                     direction, flags, opPackageName, uid, pid,
                                     mContext.getApplicationInfo().targetSdkVersion);
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index c23bfc4..11dbef2 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -43,7 +43,6 @@
 import android.content.pm.UserInfo;
 import android.media.AudioManager;
 import android.media.AudioPlaybackConfiguration;
-import android.media.AudioSystem;
 import android.media.IRemoteVolumeControllerCallback;
 import android.media.Session2Token;
 import android.media.session.IActiveSessionsListener;
@@ -94,7 +93,6 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 /**
  * System implementation of MediaSessionManager
@@ -149,7 +147,6 @@
 
     private SessionPolicyProvider mCustomSessionPolicyProvider;
     private MediaKeyDispatcher mCustomMediaKeyDispatcher;
-    private Map<Integer, Integer> mOverriddenKeyEventsMap;
 
     public MediaSessionService(Context context) {
         super(context);
@@ -780,7 +777,6 @@
     private void instantiateCustomDispatcher(String nameFromTesting) {
         synchronized (mLock) {
             mCustomMediaKeyDispatcher = null;
-            mOverriddenKeyEventsMap = null;
 
             String customDispatcherClassName = (nameFromTesting == null)
                     ? mContext.getResources().getString(R.string.config_customMediaKeyDispatcher)
@@ -788,9 +784,10 @@
             try {
                 if (!TextUtils.isEmpty(customDispatcherClassName)) {
                     Class customDispatcherClass = Class.forName(customDispatcherClassName);
-                    Constructor constructor = customDispatcherClass.getDeclaredConstructor();
-                    mCustomMediaKeyDispatcher = (MediaKeyDispatcher) constructor.newInstance();
-                    mOverriddenKeyEventsMap = mCustomMediaKeyDispatcher.getOverriddenKeyEvents();
+                    Constructor constructor =
+                            customDispatcherClass.getDeclaredConstructor(Context.class);
+                    mCustomMediaKeyDispatcher =
+                            (MediaKeyDispatcher) constructor.newInstance(mContext);
                 }
             } catch (ClassNotFoundException | InstantiationException | InvocationTargetException
                     | IllegalAccessException | NoSuchMethodException e) {
@@ -810,9 +807,10 @@
             try {
                 if (!TextUtils.isEmpty(customProviderClassName)) {
                     Class customProviderClass = Class.forName(customProviderClassName);
-                    Constructor constructor = customProviderClass.getDeclaredConstructor();
+                    Constructor constructor =
+                            customProviderClass.getDeclaredConstructor(Context.class);
                     mCustomSessionPolicyProvider =
-                            (SessionPolicyProvider) constructor.newInstance();
+                            (SessionPolicyProvider) constructor.newInstance(mContext);
                 }
             } catch (ClassNotFoundException | InstantiationException | InvocationTargetException
                     | IllegalAccessException | NoSuchMethodException e) {
@@ -2019,7 +2017,7 @@
 
             boolean preferSuggestedStream = false;
             if (isValidLocalStreamType(suggestedStream)
-                    && AudioSystem.isStreamActive(suggestedStream, 0)) {
+                    && MediaServerUtils.isStreamActive(mAudioManager, suggestedStream)) {
                 preferSuggestedStream = true;
             }
             if (session == null || preferSuggestedStream) {
@@ -2028,7 +2026,8 @@
                             + ". flags=" + flags + ", preferSuggestedStream="
                             + preferSuggestedStream + ", session=" + session);
                 }
-                if (musicOnly && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
+                if (musicOnly && !MediaServerUtils.isStreamActive(mAudioManager,
+                        AudioManager.STREAM_MUSIC)) {
                     if (DEBUG_KEY_EVENT) {
                         Log.d(TAG, "Nothing is playing on the music stream. Skipping volume event,"
                                 + " flags=" + flags);
@@ -2382,9 +2381,12 @@
                     return;
                 }
 
-                int overriddenKeyEvents = (mCustomMediaKeyDispatcher == null) ? 0
-                        : mCustomMediaKeyDispatcher.getOverriddenKeyEvents()
-                                .get(keyEvent.getKeyCode());
+                int overriddenKeyEvents = 0;
+                if (mCustomMediaKeyDispatcher != null
+                        && mCustomMediaKeyDispatcher.getOverriddenKeyEvents() != null) {
+                    overriddenKeyEvents = mCustomMediaKeyDispatcher.getOverriddenKeyEvents()
+                            .get(keyEvent.getKeyCode());
+                }
                 cancelTrackingIfNeeded(packageName, pid, uid, asSystemService, keyEvent,
                         needWakeLock, opPackageName, stream, musicOnly, overriddenKeyEvents);
                 if (!needTracking(keyEvent, overriddenKeyEvents)) {
diff --git a/services/core/java/com/android/server/media/SessionPolicyProvider.java b/services/core/java/com/android/server/media/SessionPolicyProvider.java
index 5f02a07..332c85a 100644
--- a/services/core/java/com/android/server/media/SessionPolicyProvider.java
+++ b/services/core/java/com/android/server/media/SessionPolicyProvider.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.content.Context;
 import android.media.session.MediaSession;
 
 import java.lang.annotation.Retention;
@@ -54,7 +55,7 @@
      */
     static final int SESSION_POLICY_IGNORE_BUTTON_SESSION = 1 << 1;
 
-    public SessionPolicyProvider() {
+    public SessionPolicyProvider(Context context) {
         // Constructor used for reflection
     }
 
diff --git a/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java
index adb9869..9c68349 100644
--- a/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java
+++ b/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java
@@ -19,13 +19,17 @@
 import android.content.Context;
 import android.media.metrics.IPlaybackMetricsManager;
 import android.media.metrics.PlaybackMetrics;
+import android.util.Base64;
 
 import com.android.server.SystemService;
 
+import java.security.SecureRandom;
+
 /**
  * System service manages playback metrics.
  */
 public final class PlaybackMetricsManagerService extends SystemService {
+    private final SecureRandom mSecureRandom;
 
     /**
      * Initializes the playback metrics manager service.
@@ -34,6 +38,7 @@
      */
     public PlaybackMetricsManagerService(Context context) {
         super(context);
+        mSecureRandom = new SecureRandom();
     }
 
     @Override
@@ -44,8 +49,16 @@
 
     private final class BinderService extends IPlaybackMetricsManager.Stub {
         @Override
-        public void reportPlaybackMetrics(PlaybackMetrics metrics, int userId) {
+        public void reportPlaybackMetrics(String sessionId, PlaybackMetrics metrics, int userId) {
             // TODO: log it to statsd
         }
+
+        @Override
+        public String getSessionId(int userId) {
+            byte[] byteId = new byte[16]; // 128 bits
+            mSecureRandom.nextBytes(byteId);
+            String id = Base64.encodeToString(byteId, Base64.DEFAULT);
+            return id;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index bdf0fb9..8200ca0 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -34,6 +34,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
+import android.util.TypedXmlSerializer;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -110,7 +111,7 @@
     }
 
     @Override
-    void writeDefaults(XmlSerializer out) throws IOException {
+    void writeDefaults(TypedXmlSerializer out) throws IOException {
         synchronized (mDefaultsLock) {
             String defaults = String.join(ENABLED_SERVICES_SEPARATOR, mDefaultPackages);
             out.attribute(null, ATT_DEFAULTS, defaults);
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index f2e3708..a7ee272 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -60,6 +60,8 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
@@ -443,7 +445,7 @@
         }
     }
 
-    void writeDefaults(XmlSerializer out) throws IOException {
+    void writeDefaults(TypedXmlSerializer out) throws IOException {
         synchronized (mDefaultsLock) {
             List<String> componentStrings = new ArrayList<>(mDefaultComponents.size());
             for (int i = 0; i < mDefaultComponents.size(); i++) {
@@ -454,10 +456,10 @@
         }
     }
 
-    public void writeXml(XmlSerializer out, boolean forBackup, int userId) throws IOException {
+    public void writeXml(TypedXmlSerializer out, boolean forBackup, int userId) throws IOException {
         out.startTag(null, getConfig().xmlTag);
 
-        out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION));
+        out.attributeInt(null, ATT_VERSION, DB_VERSION);
 
         writeDefaults(out);
 
@@ -485,8 +487,8 @@
                                     : String.join(ENABLED_SERVICES_SEPARATOR, approved);
                             out.startTag(null, TAG_MANAGED_SERVICES);
                             out.attribute(null, ATT_APPROVED_LIST, allowedItems);
-                            out.attribute(null, ATT_USER_ID, Integer.toString(approvedUserId));
-                            out.attribute(null, ATT_IS_PRIMARY, Boolean.toString(isPrimary));
+                            out.attributeInt(null, ATT_USER_ID, approvedUserId);
+                            out.attributeBoolean(null, ATT_IS_PRIMARY, isPrimary);
                             if (userSet != null) {
                                 String userSetItems =
                                         String.join(ENABLED_SERVICES_SEPARATOR, userSet);
@@ -516,23 +518,23 @@
     /**
      * Writes extra xml attributes to {@link #TAG_MANAGED_SERVICES} tag.
      */
-    protected void writeExtraAttributes(XmlSerializer out, int userId) throws IOException {}
+    protected void writeExtraAttributes(TypedXmlSerializer out, int userId) throws IOException {}
 
     /**
      * Writes extra xml tags within the parent tag specified in {@link Config#xmlTag}.
      */
-    protected void writeExtraXmlTags(XmlSerializer out) throws IOException {}
+    protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException {}
 
     /**
      * This is called to process tags other than {@link #TAG_MANAGED_SERVICES}.
      */
-    protected void readExtraTag(String tag, XmlPullParser parser) throws IOException {}
+    protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException {}
 
     protected void migrateToXml() {
         loadAllowedComponentsFromSettings();
     }
 
-    void readDefaults(XmlPullParser parser) {
+    void readDefaults(TypedXmlPullParser parser) {
         String defaultComponents = XmlUtils.readStringAttribute(parser, ATT_DEFAULTS);
 
         if (!TextUtils.isEmpty(defaultComponents)) {
@@ -554,7 +556,7 @@
     }
 
     public void readXml(
-            XmlPullParser parser,
+            TypedXmlPullParser parser,
             TriPredicate<String, Integer, String> allowedManagedServicePackages,
             boolean forRestore,
             int userId)
@@ -577,9 +579,9 @@
                     final String approved = XmlUtils.readStringAttribute(parser, ATT_APPROVED_LIST);
                     // Ignore parser's user id for restore.
                     final int resolvedUserId = forRestore
-                            ? userId : XmlUtils.readIntAttribute(parser, ATT_USER_ID, 0);
+                            ? userId : parser.getAttributeInt(null, ATT_USER_ID, 0);
                     final boolean isPrimary =
-                            XmlUtils.readBooleanAttribute(parser, ATT_IS_PRIMARY, true);
+                            parser.getAttributeBoolean(null, ATT_IS_PRIMARY, true);
                     final String userSet = XmlUtils.readStringAttribute(parser, ATT_USER_SET);
                     readExtraAttributes(tag, parser, resolvedUserId);
                     if (allowedManagedServicePackages == null || allowedManagedServicePackages.test(
@@ -631,7 +633,7 @@
     /**
      * Read extra attributes in the {@link #TAG_MANAGED_SERVICES} tag.
      */
-    protected void readExtraAttributes(String tag, XmlPullParser parser, int userId)
+    protected void readExtraAttributes(String tag, TypedXmlPullParser parser, int userId)
             throws IOException {}
 
     protected abstract String getRequiredPermission();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b1289dd..76b9c86 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -231,6 +231,8 @@
 import android.util.SparseArray;
 import android.util.StatsEvent;
 import android.util.Xml;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.proto.ProtoOutputStream;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -743,8 +745,13 @@
 
     void readPolicyXml(InputStream stream, boolean forRestore, int userId)
             throws XmlPullParserException, NumberFormatException, IOException {
-        final XmlPullParser parser = Xml.newPullParser();
-        parser.setInput(stream, StandardCharsets.UTF_8.name());
+        final TypedXmlPullParser parser;
+        if (forRestore) {
+            parser = Xml.newFastPullParser();
+            parser.setInput(stream, StandardCharsets.UTF_8.name());
+        } else {
+            parser = Xml.resolvePullParser(stream);
+        }
         XmlUtils.beginDocument(parser, TAG_NOTIFICATION_POLICY);
         boolean migratedManagedServices = false;
         boolean ineligibleForManagedServices = forRestore && mUm.isManagedProfile(userId);
@@ -781,9 +788,8 @@
                 if (forRestore && userId != UserHandle.USER_SYSTEM) {
                     continue;
                 }
-                mLockScreenAllowSecureNotifications =
-                        safeBoolean(parser.getAttributeValue(null,
-                                        LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE), true);
+                mLockScreenAllowSecureNotifications = parser.getAttributeBoolean(null,
+                        LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE, true);
             }
         }
 
@@ -856,11 +862,16 @@
 
     private void writePolicyXml(OutputStream stream, boolean forBackup, int userId)
             throws IOException {
-        final XmlSerializer out = new FastXmlSerializer();
-        out.setOutput(stream, StandardCharsets.UTF_8.name());
+        final TypedXmlSerializer out;
+        if (forBackup) {
+            out = Xml.newFastSerializer();
+            out.setOutput(stream, StandardCharsets.UTF_8.name());
+        } else {
+            out = Xml.resolveSerializer(stream);
+        }
         out.startDocument(null, true);
         out.startTag(null, TAG_NOTIFICATION_POLICY);
-        out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
+        out.attributeInt(null, ATTR_VERSION, DB_VERSION);
         mZenModeHelper.writeXml(out, forBackup, null, userId);
         mPreferencesHelper.writeXml(out, forBackup, userId);
         mListeners.writeXml(out, forBackup, userId);
@@ -8975,7 +8986,7 @@
         }
 
         @Override
-        protected void writeExtraXmlTags(XmlSerializer out) throws IOException {
+        protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException {
             synchronized (mLock) {
                 out.startTag(null, TAG_ALLOWED_ADJUSTMENT_TYPES);
                 out.attribute(null, ATT_TYPES, TextUtils.join(",", mAllowedAdjustments));
@@ -8984,7 +8995,7 @@
         }
 
         @Override
-        protected void readExtraTag(String tag, XmlPullParser parser) throws IOException {
+        protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException {
             if (TAG_ALLOWED_ADJUSTMENT_TYPES.equals(tag)) {
                 final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES);
                 synchronized (mLock) {
@@ -9104,9 +9115,9 @@
         }
 
         @Override
-        protected void readExtraAttributes(String tag, XmlPullParser parser, int userId)
+        protected void readExtraAttributes(String tag, TypedXmlPullParser parser, int userId)
                 throws IOException {
-            boolean userSet = XmlUtils.readBooleanAttribute(parser, ATT_USER_SET, false);
+            boolean userSet = parser.getAttributeBoolean(null, ATT_USER_SET, false);
             setUserSet(userId, userSet);
         }
 
@@ -10106,18 +10117,13 @@
         }
     }
 
-    private void writeSecureNotificationsPolicy(XmlSerializer out) throws IOException {
+    private void writeSecureNotificationsPolicy(TypedXmlSerializer out) throws IOException {
         out.startTag(null, LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG);
-        out.attribute(null, LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE,
-                Boolean.toString(mLockScreenAllowSecureNotifications));
+        out.attributeBoolean(null, LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE,
+                mLockScreenAllowSecureNotifications);
         out.endTag(null, LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG);
     }
 
-    private static boolean safeBoolean(String val, boolean defValue) {
-        if (TextUtils.isEmpty(val)) return defValue;
-        return Boolean.parseBoolean(val);
-    }
-
     /**
      * Shows a warning on logcat. Shows the toast only once per package. This is to avoid being too
      * aggressive and annoying the user.
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 2f990c6..1c0349d 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -56,6 +56,8 @@
 import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.util.StatsEvent;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.R;
@@ -195,21 +197,15 @@
         syncChannelsBypassingDnd(mContext.getUserId());
     }
 
-    public void readXml(XmlPullParser parser, boolean forRestore, int userId)
+    public void readXml(TypedXmlPullParser parser, boolean forRestore, int userId)
             throws XmlPullParserException, IOException {
         int type = parser.getEventType();
         if (type != XmlPullParser.START_TAG) return;
         String tag = parser.getName();
         if (!TAG_RANKING.equals(tag)) return;
 
-        boolean upgradeForBubbles = false;
-        if (parser.getAttributeCount() > 0) {
-            String attribute = parser.getAttributeName(0);
-            if (ATT_VERSION.equals(attribute)) {
-                int xmlVersion = Integer.parseInt(parser.getAttributeValue(0));
-                upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE;
-            }
-        }
+        final int xmlVersion = parser.getAttributeInt(null, ATT_VERSION, -1);
+        boolean upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE;
         synchronized (mPackagePreferences) {
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
                 tag = parser.getName();
@@ -221,10 +217,10 @@
                         if (forRestore && userId != UserHandle.USER_SYSTEM) {
                             continue;
                         }
-                        mHideSilentStatusBarIcons = XmlUtils.readBooleanAttribute(
-                                parser, ATT_HIDE_SILENT, DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS);
+                        mHideSilentStatusBarIcons = parser.getAttributeBoolean(null,
+                                ATT_HIDE_SILENT, DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS);
                     } else if (TAG_PACKAGE.equals(tag)) {
-                        int uid = XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
+                        int uid = parser.getAttributeInt(null, ATT_UID, UNKNOWN_UID);
                         String name = parser.getAttributeValue(null, ATT_NAME);
                         if (!TextUtils.isEmpty(name)) {
                             if (forRestore) {
@@ -243,36 +239,36 @@
                             }
                             int bubblePref = hasSAWPermission
                                     ? BUBBLE_PREFERENCE_ALL
-                                    : XmlUtils.readIntAttribute(parser, ATT_ALLOW_BUBBLE,
-                                            DEFAULT_BUBBLE_PREFERENCE);
+                                    : parser.getAttributeInt(
+                                            null, ATT_ALLOW_BUBBLE, DEFAULT_BUBBLE_PREFERENCE);
 
                             PackagePreferences r = getOrCreatePackagePreferencesLocked(
                                     name, userId, uid,
-                                    XmlUtils.readIntAttribute(
-                                            parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
-                                    XmlUtils.readIntAttribute(parser, ATT_PRIORITY,
-                                            DEFAULT_PRIORITY),
-                                    XmlUtils.readIntAttribute(
-                                            parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
-                                    XmlUtils.readBooleanAttribute(
-                                            parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
+                                    parser.getAttributeInt(
+                                            null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
+                                    parser.getAttributeInt(
+                                            null, ATT_PRIORITY, DEFAULT_PRIORITY),
+                                    parser.getAttributeInt(
+                                            null, ATT_VISIBILITY, DEFAULT_VISIBILITY),
+                                    parser.getAttributeBoolean(
+                                            null, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
                                     bubblePref);
-                            r.importance = XmlUtils.readIntAttribute(
-                                    parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
-                            r.priority = XmlUtils.readIntAttribute(
-                                    parser, ATT_PRIORITY, DEFAULT_PRIORITY);
-                            r.visibility = XmlUtils.readIntAttribute(
-                                    parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
-                            r.showBadge = XmlUtils.readBooleanAttribute(
-                                    parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
-                            r.lockedAppFields = XmlUtils.readIntAttribute(parser,
-                                    ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS);
-                            r.hasSentInvalidMessage = XmlUtils.readBooleanAttribute(
-                                    parser, ATT_SENT_INVALID_MESSAGE, false);
-                            r.hasSentValidMessage = XmlUtils.readBooleanAttribute(
-                                    parser, ATT_SENT_VALID_MESSAGE, false);
-                            r.userDemotedMsgApp = XmlUtils.readBooleanAttribute(
-                                    parser, ATT_USER_DEMOTED_INVALID_MSG_APP, false);
+                            r.importance = parser.getAttributeInt(
+                                    null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
+                            r.priority = parser.getAttributeInt(
+                                    null, ATT_PRIORITY, DEFAULT_PRIORITY);
+                            r.visibility = parser.getAttributeInt(
+                                    null, ATT_VISIBILITY, DEFAULT_VISIBILITY);
+                            r.showBadge = parser.getAttributeBoolean(
+                                    null, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
+                            r.lockedAppFields = parser.getAttributeInt(
+                                    null, ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS);
+                            r.hasSentInvalidMessage = parser.getAttributeBoolean(
+                                    null, ATT_SENT_INVALID_MESSAGE, false);
+                            r.hasSentValidMessage = parser.getAttributeBoolean(
+                                    null, ATT_SENT_VALID_MESSAGE, false);
+                            r.userDemotedMsgApp = parser.getAttributeBoolean(
+                                    null, ATT_USER_DEMOTED_INVALID_MSG_APP, false);
 
                             final int innerDepth = parser.getDepth();
                             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -307,8 +303,8 @@
                                     }
                                     String id = parser.getAttributeValue(null, ATT_ID);
                                     String channelName = parser.getAttributeValue(null, ATT_NAME);
-                                    int channelImportance = XmlUtils.readIntAttribute(
-                                            parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
+                                    int channelImportance = parser.getAttributeInt(
+                                            null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
                                     if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
                                         NotificationChannel channel = new NotificationChannel(id,
                                                 channelName, channelImportance);
@@ -338,14 +334,13 @@
                                 // Delegate
                                 if (TAG_DELEGATE.equals(tagName)) {
                                     int delegateId =
-                                            XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
+                                            parser.getAttributeInt(null, ATT_UID, UNKNOWN_UID);
                                     String delegateName =
                                             XmlUtils.readStringAttribute(parser, ATT_NAME);
-                                    boolean delegateEnabled = XmlUtils.readBooleanAttribute(
-                                            parser, ATT_ENABLED, Delegate.DEFAULT_ENABLED);
-                                    boolean userAllowed = XmlUtils.readBooleanAttribute(
-                                            parser, ATT_USER_ALLOWED,
-                                            Delegate.DEFAULT_USER_ALLOWED);
+                                    boolean delegateEnabled = parser.getAttributeBoolean(
+                                            null, ATT_ENABLED, Delegate.DEFAULT_ENABLED);
+                                    boolean userAllowed = parser.getAttributeBoolean(
+                                            null, ATT_USER_ALLOWED, Delegate.DEFAULT_USER_ALLOWED);
                                     Delegate d = null;
                                     if (delegateId != UNKNOWN_UID && !TextUtils.isEmpty(
                                             delegateName)) {
@@ -502,13 +497,13 @@
         return true;
     }
 
-    public void writeXml(XmlSerializer out, boolean forBackup, int userId) throws IOException {
+    public void writeXml(TypedXmlSerializer out, boolean forBackup, int userId) throws IOException {
         out.startTag(null, TAG_RANKING);
-        out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
+        out.attributeInt(null, ATT_VERSION, XML_VERSION);
         if (mHideSilentStatusBarIcons != DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS
                 && (!forBackup || userId == UserHandle.USER_SYSTEM)) {
             out.startTag(null, TAG_STATUS_ICONS);
-            out.attribute(null, ATT_HIDE_SILENT, String.valueOf(mHideSilentStatusBarIcons));
+            out.attributeBoolean(null, ATT_HIDE_SILENT, mHideSilentStatusBarIcons);
             out.endTag(null, TAG_STATUS_ICONS);
         }
 
@@ -536,42 +531,41 @@
                     out.startTag(null, TAG_PACKAGE);
                     out.attribute(null, ATT_NAME, r.pkg);
                     if (r.importance != DEFAULT_IMPORTANCE) {
-                        out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
+                        out.attributeInt(null, ATT_IMPORTANCE, r.importance);
                     }
                     if (r.priority != DEFAULT_PRIORITY) {
-                        out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
+                        out.attributeInt(null, ATT_PRIORITY, r.priority);
                     }
                     if (r.visibility != DEFAULT_VISIBILITY) {
-                        out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
+                        out.attributeInt(null, ATT_VISIBILITY, r.visibility);
                     }
                     if (r.bubblePreference != DEFAULT_BUBBLE_PREFERENCE) {
-                        out.attribute(null, ATT_ALLOW_BUBBLE, Integer.toString(r.bubblePreference));
+                        out.attributeInt(null, ATT_ALLOW_BUBBLE, r.bubblePreference);
                     }
-                    out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
-                    out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
-                            Integer.toString(r.lockedAppFields));
-                    out.attribute(null, ATT_SENT_INVALID_MESSAGE,
-                            Boolean.toString(r.hasSentInvalidMessage));
-                    out.attribute(null, ATT_SENT_VALID_MESSAGE,
-                            Boolean.toString(r.hasSentValidMessage));
-                    out.attribute(null, ATT_USER_DEMOTED_INVALID_MSG_APP,
-                            Boolean.toString(r.userDemotedMsgApp));
+                    out.attributeBoolean(null, ATT_SHOW_BADGE, r.showBadge);
+                    out.attributeInt(null, ATT_APP_USER_LOCKED_FIELDS,
+                            r.lockedAppFields);
+                    out.attributeBoolean(null, ATT_SENT_INVALID_MESSAGE,
+                            r.hasSentInvalidMessage);
+                    out.attributeBoolean(null, ATT_SENT_VALID_MESSAGE,
+                            r.hasSentValidMessage);
+                    out.attributeBoolean(null, ATT_USER_DEMOTED_INVALID_MSG_APP,
+                            r.userDemotedMsgApp);
 
                     if (!forBackup) {
-                        out.attribute(null, ATT_UID, Integer.toString(r.uid));
+                        out.attributeInt(null, ATT_UID, r.uid);
                     }
 
                     if (r.delegate != null) {
                         out.startTag(null, TAG_DELEGATE);
 
                         out.attribute(null, ATT_NAME, r.delegate.mPkg);
-                        out.attribute(null, ATT_UID, Integer.toString(r.delegate.mUid));
+                        out.attributeInt(null, ATT_UID, r.delegate.mUid);
                         if (r.delegate.mEnabled != Delegate.DEFAULT_ENABLED) {
-                            out.attribute(null, ATT_ENABLED, Boolean.toString(r.delegate.mEnabled));
+                            out.attributeBoolean(null, ATT_ENABLED, r.delegate.mEnabled);
                         }
                         if (r.delegate.mUserAllowed != Delegate.DEFAULT_USER_ALLOWED) {
-                            out.attribute(null, ATT_USER_ALLOWED,
-                                    Boolean.toString(r.delegate.mUserAllowed));
+                            out.attributeBoolean(null, ATT_USER_ALLOWED, r.delegate.mUserAllowed);
                         }
                         out.endTag(null, TAG_DELEGATE);
                     }
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index f7d69fd..b5ca2ab 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -32,6 +32,8 @@
 import android.util.IntArray;
 import android.util.Log;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
@@ -57,7 +59,7 @@
  * NotificationManagerService helper for handling snoozed notifications.
  */
 public class SnoozeHelper {
-    public static final String XML_SNOOZED_NOTIFICATION_VERSION = "1";
+    public static final int XML_SNOOZED_NOTIFICATION_VERSION = 1;
 
     protected static final String XML_TAG_NAME = "snoozed-notifications";
 
@@ -547,7 +549,7 @@
         }
     }
 
-    protected void writeXml(XmlSerializer out) throws IOException {
+    protected void writeXml(TypedXmlSerializer out) throws IOException {
         synchronized (mLock) {
             final long currentTime = System.currentTimeMillis();
             out.startTag(null, XML_TAG_NAME);
@@ -573,7 +575,7 @@
         void insert(T t) throws IOException;
     }
 
-    private <T> void writeXml(XmlSerializer out,
+    private <T> void writeXml(TypedXmlSerializer out,
             ArrayMap<String, ArrayMap<String, T>> targets, String tag,
             Inserter<T> attributeInserter)
             throws IOException {
@@ -596,21 +598,18 @@
 
                 attributeInserter.insert(value);
 
-                out.attribute(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL,
+                out.attributeInt(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL,
                         XML_SNOOZED_NOTIFICATION_VERSION);
                 out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key);
-
-
                 out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, pkg);
-                out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID,
-                        String.valueOf(userId));
+                out.attributeInt(null, XML_SNOOZED_NOTIFICATION_USER_ID, userId);
 
                 out.endTag(null, tag);
             }
         }
     }
 
-    protected void readXml(XmlPullParser parser, long currentTime)
+    protected void readXml(TypedXmlPullParser parser, long currentTime)
             throws XmlPullParserException, IOException {
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -622,16 +621,16 @@
             if (type == XmlPullParser.START_TAG
                     && (XML_SNOOZED_NOTIFICATION.equals(tag)
                         || tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT))
-                    && parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL)
-                        .equals(XML_SNOOZED_NOTIFICATION_VERSION)) {
+                    && parser.getAttributeInt(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL, -1)
+                        == XML_SNOOZED_NOTIFICATION_VERSION) {
                 try {
                     final String key = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_KEY);
                     final String pkg = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_PKG);
-                    final int userId = XmlUtils.readIntAttribute(
-                            parser, XML_SNOOZED_NOTIFICATION_USER_ID, UserHandle.USER_ALL);
+                    final int userId = parser.getAttributeInt(
+                            null, XML_SNOOZED_NOTIFICATION_USER_ID, UserHandle.USER_ALL);
                     if (tag.equals(XML_SNOOZED_NOTIFICATION)) {
-                        final Long time = XmlUtils.readLongAttribute(
-                                parser, XML_SNOOZED_NOTIFICATION_TIME, 0);
+                        final Long time = parser.getAttributeLong(
+                                null, XML_SNOOZED_NOTIFICATION_TIME, 0);
                         if (time > currentTime) { //only read new stuff
                             synchronized (mLock) {
                                 storeRecordLocked(
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 13cd6e5..94f46ba 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -72,6 +72,8 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.StatsEvent;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.R;
@@ -702,7 +704,7 @@
         }
     }
 
-    public void readXml(XmlPullParser parser, boolean forRestore, int userId)
+    public void readXml(TypedXmlPullParser parser, boolean forRestore, int userId)
             throws XmlPullParserException, IOException {
         ZenModeConfig config = ZenModeConfig.readXml(parser);
         String reason = "readXml";
@@ -761,7 +763,7 @@
         }
     }
 
-    public void writeXml(XmlSerializer out, boolean forBackup, Integer version, int userId)
+    public void writeXml(TypedXmlSerializer out, boolean forBackup, Integer version, int userId)
             throws IOException {
         synchronized (mConfigs) {
             final int n = mConfigs.size();
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index 8a2d823..0613dff 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -411,7 +411,7 @@
                 table.clear();
                 final TypedXmlPullParser parser = Xml.resolvePullParser(is);
                 XmlUtils.beginDocument(parser, TAG_OVERLAYS);
-                int version = XmlUtils.readIntAttribute(parser, ATTR_VERSION);
+                int version = parser.getAttributeInt(null, ATTR_VERSION);
                 if (version != CURRENT_VERSION) {
                     upgrade(version);
                 }
@@ -445,19 +445,19 @@
             }
         }
 
-        private static SettingsItem restoreRow(@NonNull final XmlPullParser parser, final int depth)
-                throws IOException {
+        private static SettingsItem restoreRow(@NonNull final TypedXmlPullParser parser,
+                final int depth) throws IOException, XmlPullParserException {
             final String packageName = XmlUtils.readStringAttribute(parser, ATTR_PACKAGE_NAME);
-            final int userId = XmlUtils.readIntAttribute(parser, ATTR_USER_ID);
+            final int userId = parser.getAttributeInt(null, ATTR_USER_ID);
             final String targetPackageName = XmlUtils.readStringAttribute(parser,
                     ATTR_TARGET_PACKAGE_NAME);
             final String targetOverlayableName = XmlUtils.readStringAttribute(parser,
                     ATTR_TARGET_OVERLAYABLE_NAME);
             final String baseCodePath = XmlUtils.readStringAttribute(parser, ATTR_BASE_CODE_PATH);
-            final int state = XmlUtils.readIntAttribute(parser, ATTR_STATE);
-            final boolean isEnabled = XmlUtils.readBooleanAttribute(parser, ATTR_IS_ENABLED);
-            final boolean isStatic = XmlUtils.readBooleanAttribute(parser, ATTR_IS_STATIC);
-            final int priority = XmlUtils.readIntAttribute(parser, ATTR_PRIORITY);
+            final int state = parser.getAttributeInt(null, ATTR_STATE);
+            final boolean isEnabled = parser.getAttributeBoolean(null, ATTR_IS_ENABLED, false);
+            final boolean isStatic = parser.getAttributeBoolean(null, ATTR_IS_STATIC, false);
+            final int priority = parser.getAttributeInt(null, ATTR_PRIORITY);
             final String category = XmlUtils.readStringAttribute(parser, ATTR_CATEGORY);
 
             return new SettingsItem(packageName, userId, targetPackageName, targetOverlayableName,
@@ -470,7 +470,7 @@
             xml.startDocument(null, true);
             xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
             xml.startTag(null, TAG_OVERLAYS);
-            XmlUtils.writeIntAttribute(xml, ATTR_VERSION, CURRENT_VERSION);
+            xml.attributeInt(null, ATTR_VERSION, CURRENT_VERSION);
 
             final int n = table.size();
             for (int i = 0; i < n; i++) {
@@ -485,15 +485,15 @@
                 @NonNull final SettingsItem item) throws IOException {
             xml.startTag(null, TAG_ITEM);
             XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.mPackageName);
-            XmlUtils.writeIntAttribute(xml, ATTR_USER_ID, item.mUserId);
+            xml.attributeInt(null, ATTR_USER_ID, item.mUserId);
             XmlUtils.writeStringAttribute(xml, ATTR_TARGET_PACKAGE_NAME, item.mTargetPackageName);
             XmlUtils.writeStringAttribute(xml, ATTR_TARGET_OVERLAYABLE_NAME,
                     item.mTargetOverlayableName);
             XmlUtils.writeStringAttribute(xml, ATTR_BASE_CODE_PATH, item.mBaseCodePath);
-            XmlUtils.writeIntAttribute(xml, ATTR_STATE, item.mState);
+            xml.attributeInt(null, ATTR_STATE, item.mState);
             XmlUtils.writeBooleanAttribute(xml, ATTR_IS_ENABLED, item.mIsEnabled);
             XmlUtils.writeBooleanAttribute(xml, ATTR_IS_STATIC, !item.mIsMutable);
-            XmlUtils.writeIntAttribute(xml, ATTR_PRIORITY, item.mPriority);
+            xml.attributeInt(null, ATTR_PRIORITY, item.mPriority);
             XmlUtils.writeStringAttribute(xml, ATTR_CATEGORY, item.mCategory);
             xml.endTag(null, TAG_ITEM);
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e95b1a2..85659ed 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1741,6 +1741,14 @@
     private final PackageUsage mPackageUsage = new PackageUsage();
     private final CompilerStats mCompilerStats = new CompilerStats();
 
+    /**
+     * Invalidate the package info cache, which includes updating the cached computer.
+     * @hide
+     */
+    public static void invalidatePackageInfoCache() {
+        PackageManager.invalidatePackageInfoCache();
+    }
+
     class PackageHandler extends Handler {
 
         PackageHandler(Looper looper) {
@@ -2683,14 +2691,14 @@
         // We normally invalidate when we write settings, but in cases where we delay and
         // coalesce settings writes, this strategy would have us invalidate the cache too late.
         // Invalidating on schedule addresses this problem.
-        PackageManager.invalidatePackageInfoCache();
+        invalidatePackageInfoCache();
         if (!mHandler.hasMessages(WRITE_SETTINGS)) {
             mHandler.sendEmptyMessageDelayed(WRITE_SETTINGS, WRITE_SETTINGS_DELAY);
         }
     }
 
     void scheduleWritePackageListLocked(int userId) {
-        PackageManager.invalidatePackageInfoCache();
+        invalidatePackageInfoCache();
         if (!mHandler.hasMessages(WRITE_PACKAGE_LIST)) {
             Message msg = mHandler.obtainMessage(WRITE_PACKAGE_LIST);
             msg.arg1 = userId;
@@ -2704,7 +2712,7 @@
     }
 
     void scheduleWritePackageRestrictionsLocked(int userId) {
-        PackageManager.invalidatePackageInfoCache();
+        invalidatePackageInfoCache();
         final int[] userIds = (userId == UserHandle.USER_ALL)
                 ? mUserManager.getUserIds() : new int[]{userId};
         for (int nextUserId : userIds) {
@@ -6939,8 +6947,7 @@
 
     private boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId,
             String resolvedType, int flags) {
-        PersistentPreferredIntentResolver ppir = mSettings.mPersistentPreferredActivities
-                .get(userId);
+        PersistentPreferredIntentResolver ppir = mSettings.getPersistentPreferredActivities(userId);
         //TODO(b/158003772): Remove double query
         List<PersistentPreferredActivity> pprefs = ppir != null
                 ? ppir.queryIntent(intent, resolvedType,
@@ -6959,8 +6966,7 @@
     private ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType,
             int flags, List<ResolveInfo> query, boolean debug, int userId) {
         final int N = query.size();
-        PersistentPreferredIntentResolver ppir = mSettings.mPersistentPreferredActivities
-                .get(userId);
+        PersistentPreferredIntentResolver ppir = mSettings.getPersistentPreferredActivities(userId);
         // Get the list of persistent preferred activities that handle the intent
         if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for presistent preferred activities...");
         List<PersistentPreferredActivity> pprefs = ppir != null
@@ -7060,7 +7066,7 @@
                 return pri;
             }
 
-            PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId);
+            PreferredIntentResolver pir = mSettings.getPreferredActivities(userId);
             // Get the list of preferred activities that handle the intent
             if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for preferred activities...");
             List<PreferredActivity> prefs = pir != null
@@ -7287,7 +7293,7 @@
 
     private List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent,
             String resolvedType, int userId) {
-        CrossProfileIntentResolver resolver = mSettings.mCrossProfileIntentResolvers.get(userId);
+        CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolvers(userId);
         if (resolver != null) {
             return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);
         }
@@ -9889,7 +9895,7 @@
                 android.Manifest.permission.INTERACT_ACROSS_PROFILES,
                 PermissionChecker.PID_UNKNOWN,
                 callingUid,
-                mPmInternal.getPackage(callingUid).getPackageName())
+                getPackage(callingUid).getPackageName())
                 == PermissionChecker.PERMISSION_GRANTED) {
             return;
         }
@@ -20447,7 +20453,7 @@
         }
 
         synchronized (mLock) {
-            final PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId);
+            final PreferredIntentResolver pir = mSettings.getPreferredActivities(userId);
             if (pir != null) {
                 // Get all of the existing entries that exactly match this filter.
                 final ArrayList<PreferredActivity> existing = pir.findFilters(filter);
@@ -20544,35 +20550,7 @@
     @GuardedBy("mLock")
     private void clearPackagePreferredActivitiesLPw(String packageName,
             @NonNull SparseBooleanArray outUserChanged, int userId) {
-        ArrayList<PreferredActivity> removed = null;
-        for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
-            final int thisUserId = mSettings.mPreferredActivities.keyAt(i);
-            PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i);
-            if (userId != UserHandle.USER_ALL && userId != thisUserId) {
-                continue;
-            }
-            Iterator<PreferredActivity> it = pir.filterIterator();
-            while (it.hasNext()) {
-                PreferredActivity pa = it.next();
-                // Mark entry for removal only if it matches the package name
-                // and the entry is of type "always".
-                if (packageName == null ||
-                        (pa.mPref.mComponent.getPackageName().equals(packageName)
-                                && pa.mPref.mAlways)) {
-                    if (removed == null) {
-                        removed = new ArrayList<>();
-                    }
-                    removed.add(pa);
-                }
-            }
-            if (removed != null) {
-                for (int j=0; j<removed.size(); j++) {
-                    PreferredActivity pa = removed.get(j);
-                    pir.removeFilter(pa);
-                }
-                outUserChanged.put(thisUserId, true);
-            }
-        }
+        mSettings.clearPackagePreferredActivities(packageName, outUserChanged, userId);
     }
 
     /** This method takes a specific user id as well as UserHandle.USER_ALL. */
@@ -20689,7 +20667,7 @@
         final int userId = UserHandle.getCallingUserId();
         // reader
         synchronized (mLock) {
-            PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId);
+            PreferredIntentResolver pir = mSettings.getPreferredActivities(userId);
             if (pir != null) {
                 final Iterator<PreferredActivity> it = pir.filterIterator();
                 while (it.hasNext()) {
@@ -20744,35 +20722,9 @@
             throw new SecurityException(
                     "clearPackagePersistentPreferredActivities can only be run by the system");
         }
-        ArrayList<PersistentPreferredActivity> removed = null;
         boolean changed = false;
         synchronized (mLock) {
-            for (int i=0; i<mSettings.mPersistentPreferredActivities.size(); i++) {
-                final int thisUserId = mSettings.mPersistentPreferredActivities.keyAt(i);
-                PersistentPreferredIntentResolver ppir = mSettings.mPersistentPreferredActivities
-                        .valueAt(i);
-                if (userId != thisUserId) {
-                    continue;
-                }
-                Iterator<PersistentPreferredActivity> it = ppir.filterIterator();
-                while (it.hasNext()) {
-                    PersistentPreferredActivity ppa = it.next();
-                    // Mark entry for removal only if it matches the package name.
-                    if (ppa.mComponent.getPackageName().equals(packageName)) {
-                        if (removed == null) {
-                            removed = new ArrayList<>();
-                        }
-                        removed.add(ppa);
-                    }
-                }
-                if (removed != null) {
-                    for (int j=0; j<removed.size(); j++) {
-                        PersistentPreferredActivity ppa = removed.get(j);
-                        ppir.removeFilter(ppa);
-                    }
-                    changed = true;
-                }
-            }
+            changed = mSettings.clearPackagePersistentPreferredActivities(packageName, userId);
         }
         if (changed) {
             updateDefaultHomeNotLocked(userId);
@@ -22152,33 +22104,9 @@
         }
 
         synchronized (mLock) {
-            // Verify that all of the preferred activity components actually
-            // exist.  It is possible for applications to be updated and at
-            // that point remove a previously declared activity component that
-            // had been set as a preferred activity.  We try to clean this up
-            // the next time we encounter that preferred activity, but it is
-            // possible for the user flow to never be able to return to that
-            // situation so here we do a validity check to make sure we haven't
-            // left any junk around.
-            ArrayList<PreferredActivity> removed = new ArrayList<>();
-            for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
-                PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i);
-                removed.clear();
-                for (PreferredActivity pa : pir.filterSet()) {
-                    if (!mComponentResolver.isActivityDefined(pa.mPref.mComponent)) {
-                        removed.add(pa);
-                    }
-                }
-                if (removed.size() > 0) {
-                    for (int r=0; r<removed.size(); r++) {
-                        PreferredActivity pa = removed.get(r);
-                        Slog.w(TAG, "Removing dangling preferred activity: "
-                                + pa.mPref.mComponent);
-                        pir.removeFilter(pa);
-                    }
-                    mSettings.writePackageRestrictionsLPr(
-                            mSettings.mPreferredActivities.keyAt(i));
-                }
+            ArrayList<Integer> changed = mSettings.systemReady(mComponentResolver);
+            for (int i = 0; i < changed.size(); i++) {
+                mSettings.writePackageRestrictionsLPr(changed.get(i));
             }
         }
 
@@ -22689,17 +22617,7 @@
             }
 
             if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
-                for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
-                    PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i);
-                    int user = mSettings.mPreferredActivities.keyAt(i);
-                    if (pir.dump(pw,
-                            dumpState.getTitlePrinted()
-                                ? "\nPreferred Activities User " + user + ":"
-                                : "Preferred Activities User " + user + ":", "  ",
-                            packageName, true, false)) {
-                        dumpState.setTitlePrinted(true);
-                    }
-                }
+                mSettings.dumpPreferred(pw, dumpState, packageName);
             }
 
             if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) {
@@ -24798,6 +24716,26 @@
         }
     }
 
+    private AndroidPackage getPackage(String packageName) {
+        synchronized (mLock) {
+            packageName = resolveInternalPackageNameLPr(
+                    packageName, PackageManager.VERSION_CODE_HIGHEST);
+            return mPackages.get(packageName);
+        }
+    }
+
+    private AndroidPackage getPackage(int uid) {
+        synchronized (mLock) {
+            final String[] packageNames = getPackagesForUidInternal(uid, Process.SYSTEM_UID);
+            AndroidPackage pkg = null;
+            final int numPackages = packageNames == null ? 0 : packageNames.length;
+            for (int i = 0; pkg == null && i < numPackages; i++) {
+                pkg = mPackages.get(packageNames[i]);
+            }
+            return pkg;
+        }
+    }
+
     private class PackageManagerInternalImpl extends PackageManagerInternal {
         @Override
         public List<ApplicationInfo> getInstalledApplications(int flags, int userId,
@@ -24908,24 +24846,12 @@
 
         @Override
         public AndroidPackage getPackage(String packageName) {
-            synchronized (mLock) {
-                packageName = resolveInternalPackageNameLPr(
-                        packageName, PackageManager.VERSION_CODE_HIGHEST);
-                return mPackages.get(packageName);
-            }
+            return PackageManagerService.this.getPackage(packageName);
         }
 
         @Override
         public AndroidPackage getPackage(int uid) {
-            synchronized (mLock) {
-                final String[] packageNames = getPackagesForUidInternal(uid, Process.SYSTEM_UID);
-                AndroidPackage pkg = null;
-                final int numPackages = packageNames == null ? 0 : packageNames.length;
-                for (int i = 0; pkg == null && i < numPackages; i++) {
-                    pkg = mPackages.get(packageNames[i]);
-                }
-                return pkg;
-            }
+            return PackageManagerService.this.getPackage(uid);
         }
 
         @Nullable
@@ -25532,7 +25458,7 @@
                 }
             }
 
-            PackageManager.invalidatePackageInfoCache();
+            invalidatePackageInfoCache();
             return true;
         }
 
diff --git a/services/core/java/com/android/server/pm/PreferredComponent.java b/services/core/java/com/android/server/pm/PreferredComponent.java
index 804faa1..ff6b73b 100644
--- a/services/core/java/com/android/server/pm/PreferredComponent.java
+++ b/services/core/java/com/android/server/pm/PreferredComponent.java
@@ -252,6 +252,37 @@
         return numMatch == NS;
     }
 
+    public boolean sameSet(PreferredComponent pc) {
+        if (mSetPackages == null || pc == null || pc.mSetPackages == null
+                || !sameComponent(pc.mComponent)) {
+            return false;
+        }
+        final int otherPackageCount = pc.mSetPackages.length;
+        final int packageCount = mSetPackages.length;
+        if (otherPackageCount != packageCount) {
+            return false;
+        }
+        for (int i = 0; i < packageCount; i++) {
+            if (!mSetPackages[i].equals(pc.mSetPackages[i])
+                    || !mSetClasses[i].equals(pc.mSetClasses[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /** Returns true if the preferred component represents the provided ComponentName. */
+    private boolean sameComponent(ComponentName comp) {
+        if (mComponent == null || comp == null) {
+            return false;
+        }
+        if (mComponent.getPackageName().equals(comp.getPackageName())
+                && mComponent.getClassName().equals(comp.getClassName())) {
+            return true;
+        }
+        return false;
+    }
+
     public boolean isSuperset(List<ResolveInfo> query, boolean excludeSetupWizardPackage) {
         if (mSetPackages == null) {
             return query == null;
diff --git a/services/core/java/com/android/server/pm/PreferredIntentResolver.java b/services/core/java/com/android/server/pm/PreferredIntentResolver.java
index a261e29..ff3df13 100644
--- a/services/core/java/com/android/server/pm/PreferredIntentResolver.java
+++ b/services/core/java/com/android/server/pm/PreferredIntentResolver.java
@@ -22,6 +22,7 @@
 import java.io.PrintWriter;
 
 import com.android.server.IntentResolver;
+import java.util.ArrayList;
 
 public class PreferredIntentResolver
         extends IntentResolver<PreferredActivity, PreferredActivity> {
@@ -45,4 +46,24 @@
     protected IntentFilter getIntentFilter(@NonNull PreferredActivity input) {
         return input;
     }
+
+    public boolean shouldAddPreferredActivity(PreferredActivity pa) {
+        ArrayList<PreferredActivity> pal = findFilters(pa);
+        if (pal == null || pal.isEmpty()) {
+            return true;
+        }
+        if (!pa.mPref.mAlways) {
+            return false;
+        }
+        final int activityCount = pal.size();
+        for (int i = 0; i < activityCount; i++) {
+            PreferredActivity cur = pal.get(i);
+            if (cur.mPref.mAlways
+                    && cur.mPref.mMatch == (pa.mPref.mMatch & IntentFilter.MATCH_CATEGORY_MASK)
+                    && cur.mPref.sameSet(pa.mPref)) {
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 966090cb..f47b4b4 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -369,16 +369,16 @@
 
     // The user's preferred activities associated with particular intent
     // filters.
-    final SparseArray<PreferredIntentResolver> mPreferredActivities =
+    private final SparseArray<PreferredIntentResolver> mPreferredActivities =
             new SparseArray<PreferredIntentResolver>();
 
     // The persistent preferred activities of the user's profile/device owner
     // associated with particular intent filters.
-    final SparseArray<PersistentPreferredIntentResolver> mPersistentPreferredActivities =
+    private final SparseArray<PersistentPreferredIntentResolver> mPersistentPreferredActivities =
             new SparseArray<PersistentPreferredIntentResolver>();
 
     // For every user, it is used to find to which other users the intent can be forwarded.
-    final SparseArray<CrossProfileIntentResolver> mCrossProfileIntentResolvers =
+    private final SparseArray<CrossProfileIntentResolver> mCrossProfileIntentResolvers =
             new SparseArray<CrossProfileIntentResolver>();
 
     final ArrayMap<String, SharedUserSetting> mSharedUsers = new ArrayMap<>();
@@ -467,7 +467,7 @@
     }
 
     private static void invalidatePackageCache() {
-        PackageManager.invalidatePackageInfoCache();
+        PackageManagerService.invalidatePackageInfoCache();
         ChangeIdStateCache.invalidate();
     }
 
@@ -1312,8 +1312,7 @@
                 PreferredActivity pa = new PreferredActivity(parser);
                 if (pa.mPref.getParseError() == null) {
                     final PreferredIntentResolver resolver = editPreferredActivitiesLPw(userId);
-                    ArrayList<PreferredActivity> pal = resolver.findFilters(pa);
-                    if (pal == null || pal.size() == 0 || pa.mPref.mAlways) {
+                    if (resolver.shouldAddPreferredActivity(pa)) {
                         resolver.addFilter(pa);
                     }
                 } else {
@@ -5543,4 +5542,130 @@
             }
         }
     }
+
+    /**
+     * Accessor for preferred activities
+     */
+    PersistentPreferredIntentResolver getPersistentPreferredActivities(int userId) {
+        return mPersistentPreferredActivities.get(userId);
+    }
+
+    PreferredIntentResolver getPreferredActivities(int userId) {
+        return mPreferredActivities.get(userId);
+    }
+
+    CrossProfileIntentResolver getCrossProfileIntentResolvers(int userId) {
+        return mCrossProfileIntentResolvers.get(userId);
+    }
+
+    /** This method takes a specific user id as well as UserHandle.USER_ALL. */
+    void clearPackagePreferredActivities(String packageName,
+            @NonNull SparseBooleanArray outUserChanged, int userId) {
+        ArrayList<PreferredActivity> removed = null;
+        for (int i = 0; i < mPreferredActivities.size(); i++) {
+            final int thisUserId = mPreferredActivities.keyAt(i);
+            PreferredIntentResolver pir = mPreferredActivities.valueAt(i);
+            if (userId != UserHandle.USER_ALL && userId != thisUserId) {
+                continue;
+            }
+            Iterator<PreferredActivity> it = pir.filterIterator();
+            while (it.hasNext()) {
+                PreferredActivity pa = it.next();
+                // Mark entry for removal only if it matches the package name
+                // and the entry is of type "always".
+                if (packageName == null
+                        || (pa.mPref.mComponent.getPackageName().equals(packageName)
+                                && pa.mPref.mAlways)) {
+                    if (removed == null) {
+                        removed = new ArrayList<>();
+                    }
+                    removed.add(pa);
+                }
+            }
+            if (removed != null) {
+                for (int j = 0; j < removed.size(); j++) {
+                    PreferredActivity pa = removed.get(j);
+                    pir.removeFilter(pa);
+                }
+                outUserChanged.put(thisUserId, true);
+            }
+        }
+    }
+
+    boolean clearPackagePersistentPreferredActivities(String packageName, int userId) {
+        ArrayList<PersistentPreferredActivity> removed = null;
+        boolean changed = false;
+        for (int i = 0; i < mPersistentPreferredActivities.size(); i++) {
+            final int thisUserId = mPersistentPreferredActivities.keyAt(i);
+            PersistentPreferredIntentResolver ppir = mPersistentPreferredActivities.valueAt(i);
+            if (userId != thisUserId) {
+                continue;
+            }
+            Iterator<PersistentPreferredActivity> it = ppir.filterIterator();
+            while (it.hasNext()) {
+                PersistentPreferredActivity ppa = it.next();
+                // Mark entry for removal only if it matches the package name.
+                if (ppa.mComponent.getPackageName().equals(packageName)) {
+                    if (removed == null) {
+                        removed = new ArrayList<>();
+                    }
+                    removed.add(ppa);
+                }
+            }
+            if (removed != null) {
+                for (int j = 0; j < removed.size(); j++) {
+                    PersistentPreferredActivity ppa = removed.get(j);
+                    ppir.removeFilter(ppa);
+                }
+                changed = true;
+            }
+        }
+        return changed;
+    }
+
+    ArrayList<Integer> systemReady(ComponentResolver resolver) {
+        // Verify that all of the preferred activity components actually
+        // exist.  It is possible for applications to be updated and at
+        // that point remove a previously declared activity component that
+        // had been set as a preferred activity.  We try to clean this up
+        // the next time we encounter that preferred activity, but it is
+        // possible for the user flow to never be able to return to that
+        // situation so here we do a validity check to make sure we haven't
+        // left any junk around.
+        ArrayList<Integer> changed = new ArrayList<>();
+        ArrayList<PreferredActivity> removed = new ArrayList<>();
+        for (int i = 0; i < mPreferredActivities.size(); i++) {
+            PreferredIntentResolver pir = mPreferredActivities.valueAt(i);
+            removed.clear();
+            for (PreferredActivity pa : pir.filterSet()) {
+                if (!resolver.isActivityDefined(pa.mPref.mComponent)) {
+                    removed.add(pa);
+                }
+            }
+            if (removed.size() > 0) {
+                for (int r = 0; r < removed.size(); r++) {
+                    PreferredActivity pa = removed.get(r);
+                    Slog.w(TAG, "Removing dangling preferred activity: "
+                            + pa.mPref.mComponent);
+                    pir.removeFilter(pa);
+                }
+                changed.add(mPreferredActivities.keyAt(i));
+            }
+        }
+        return changed;
+    }
+
+    void dumpPreferred(PrintWriter pw, DumpState dumpState, String packageName) {
+        for (int i = 0; i < mPreferredActivities.size(); i++) {
+            PreferredIntentResolver pir = mPreferredActivities.valueAt(i);
+            int user = mPreferredActivities.keyAt(i);
+            if (pir.dump(pw,
+                         dumpState.getTitlePrinted()
+                         ? "\nPreferred Activities User " + user + ":"
+                         : "Preferred Activities User " + user + ":", "  ",
+                         packageName, true, false)) {
+                dumpState.setTitlePrinted(true);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/ShareTargetInfo.java b/services/core/java/com/android/server/pm/ShareTargetInfo.java
index fdfee77..660874e 100644
--- a/services/core/java/com/android/server/pm/ShareTargetInfo.java
+++ b/services/core/java/com/android/server/pm/ShareTargetInfo.java
@@ -17,10 +17,11 @@
 
 import android.annotation.NonNull;
 import android.text.TextUtils;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -123,7 +124,7 @@
         return strBuilder.toString();
     }
 
-    void saveToXml(@NonNull XmlSerializer out) throws IOException {
+    void saveToXml(@NonNull TypedXmlSerializer out) throws IOException {
         out.startTag(null, TAG_SHARE_TARGET);
 
         ShortcutService.writeAttr(out, ATTR_TARGET_CLASS, mTargetClass);
@@ -149,7 +150,7 @@
         out.endTag(null, TAG_SHARE_TARGET);
     }
 
-    static ShareTargetInfo loadFromXml(XmlPullParser parser)
+    static ShareTargetInfo loadFromXml(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         final String targetClass = ShortcutService.parseStringAttribute(parser, ATTR_TARGET_CLASS);
         final ArrayList<ShareTargetInfo.TargetData> targetData = new ArrayList<>();
@@ -178,7 +179,7 @@
                 targetClass, categories.toArray(new String[categories.size()]));
     }
 
-    private static ShareTargetInfo.TargetData parseTargetData(XmlPullParser parser) {
+    private static ShareTargetInfo.TargetData parseTargetData(TypedXmlPullParser parser) {
         final String scheme = ShortcutService.parseStringAttribute(parser, ATTR_SCHEME);
         final String host = ShortcutService.parseStringAttribute(parser, ATTR_HOST);
         final String port = ShortcutService.parseStringAttribute(parser, ATTR_PORT);
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index 0ebe5961..2960bc9 100644
--- a/services/core/java/com/android/server/pm/ShortcutLauncher.java
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -24,6 +24,8 @@
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -36,15 +38,12 @@
 import org.json.JSONObject;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
-import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -225,7 +224,7 @@
      * Persist.
      */
     @Override
-    public void saveToXml(XmlSerializer out, boolean forBackup)
+    public void saveToXml(TypedXmlSerializer out, boolean forBackup)
             throws IOException {
         if (forBackup && !getPackageInfo().isBackupAllowed()) {
             // If an launcher app doesn't support backup&restore, then nothing to do.
@@ -278,11 +277,8 @@
         }
 
         try {
-            final BufferedInputStream bis = new BufferedInputStream(in);
-
             ShortcutLauncher ret = null;
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(bis, StandardCharsets.UTF_8.name());
+            TypedXmlPullParser parser = Xml.resolvePullParser(in);
 
             int type;
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -313,7 +309,7 @@
     /**
      * Load.
      */
-    public static ShortcutLauncher loadFromXml(XmlPullParser parser, ShortcutUser shortcutUser,
+    public static ShortcutLauncher loadFromXml(TypedXmlPullParser parser, ShortcutUser shortcutUser,
             int ownerUserId, boolean fromBackup) throws IOException, XmlPullParserException {
         final String launcherPackageName = ShortcutService.parseStringAttribute(parser,
                 ATTR_PACKAGE_NAME);
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index f6c60ad..0ac0c8d 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -35,6 +35,8 @@
 import android.util.AtomicFile;
 import android.util.Log;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -52,15 +54,12 @@
 import org.json.JSONObject;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
-import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -1571,7 +1570,7 @@
     }
 
     @Override
-    public void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
+    public void saveToXml(@NonNull TypedXmlSerializer out, boolean forBackup)
             throws IOException, XmlPullParserException {
         final int size = mShortcuts.size();
         final int shareTargetSize = mShareTargets.size();
@@ -1601,7 +1600,7 @@
         out.endTag(null, TAG_ROOT);
     }
 
-    private void saveShortcut(XmlSerializer out, ShortcutInfo si, boolean forBackup,
+    private void saveShortcut(TypedXmlSerializer out, ShortcutInfo si, boolean forBackup,
             boolean appSupportsBackup)
             throws IOException, XmlPullParserException {
 
@@ -1734,11 +1733,8 @@
         }
 
         try {
-            final BufferedInputStream bis = new BufferedInputStream(in);
-
             ShortcutPackage ret = null;
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(bis, StandardCharsets.UTF_8.name());
+            TypedXmlPullParser parser = Xml.resolvePullParser(in);
 
             int type;
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -1767,7 +1763,7 @@
     }
 
     public static ShortcutPackage loadFromXml(ShortcutService s, ShortcutUser shortcutUser,
-            XmlPullParser parser, boolean fromBackup)
+            TypedXmlPullParser parser, boolean fromBackup)
             throws IOException, XmlPullParserException {
 
         final String packageName = ShortcutService.parseStringAttribute(parser,
@@ -1814,7 +1810,7 @@
         return ret;
     }
 
-    private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName,
+    private static ShortcutInfo parseShortcut(TypedXmlPullParser parser, String packageName,
             @UserIdInt int userId, boolean fromBackup)
             throws IOException, XmlPullParserException {
         String id;
@@ -1948,7 +1944,7 @@
                 disabledReason, persons.toArray(new Person[persons.size()]), locusId);
     }
 
-    private static Intent parseIntent(XmlPullParser parser)
+    private static Intent parseIntent(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
 
         Intent intent = ShortcutService.parseIntentAttribute(parser,
@@ -1978,7 +1974,7 @@
         return intent;
     }
 
-    private static Person parsePerson(XmlPullParser parser)
+    private static Person parsePerson(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         CharSequence name = ShortcutService.parseStringAttribute(parser, ATTR_PERSON_NAME);
         String uri = ShortcutService.parseStringAttribute(parser, ATTR_PERSON_URI);
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
index 8c7871f..fce6610 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
@@ -23,6 +23,8 @@
 import android.content.pm.Signature;
 import android.content.pm.SigningInfo;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
@@ -32,7 +34,6 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -205,7 +206,7 @@
         mSigHashes = BackupUtils.hashSignatureArray(signatures);
     }
 
-    public void saveToXml(ShortcutService s, XmlSerializer out, boolean forBackup)
+    public void saveToXml(ShortcutService s, TypedXmlSerializer out, boolean forBackup)
             throws IOException {
         if (forBackup && !mBackupAllowedInitialized) {
             s.wtf("Backup happened before mBackupAllowed is initialized.");
@@ -236,7 +237,7 @@
         out.endTag(null, TAG_ROOT);
     }
 
-    public void loadFromXml(XmlPullParser parser, boolean fromBackup)
+    public void loadFromXml(TypedXmlPullParser parser, boolean fromBackup)
             throws IOException, XmlPullParserException {
         // Don't use the version code from the backup file.
         final long versionCode = ShortcutService.parseLongAttribute(parser, ATTR_VERSION,
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index 801c6cb..829133c 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -20,16 +20,15 @@
 import android.content.pm.ShortcutInfo;
 import android.util.AtomicFile;
 import android.util.Slog;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
 
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.Preconditions;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
-import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -146,7 +145,7 @@
 
     protected abstract void onRestored(int restoreBlockReason);
 
-    public abstract void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
+    public abstract void saveToXml(@NonNull TypedXmlSerializer out, boolean forBackup)
             throws IOException, XmlPullParserException;
 
     public void saveToFile(File path, boolean forBackup) {
@@ -154,18 +153,21 @@
         FileOutputStream os = null;
         try {
             os = file.startWrite();
-            final BufferedOutputStream bos = new BufferedOutputStream(os);
 
             // Write to XML
-            XmlSerializer itemOut = new FastXmlSerializer();
-            itemOut.setOutput(bos, StandardCharsets.UTF_8.name());
+            final TypedXmlSerializer itemOut;
+            if (forBackup) {
+                itemOut = Xml.newFastSerializer();
+                itemOut.setOutput(os, StandardCharsets.UTF_8.name());
+            } else {
+                itemOut = Xml.resolveSerializer(os);
+            }
             itemOut.startDocument(null, true);
 
             saveToXml(itemOut, forBackup);
 
             itemOut.endDocument();
 
-            bos.flush();
             os.flush();
             file.finishWrite(os);
         } catch (XmlPullParserException | IOException e) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 2d77182..c68fe81 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -95,6 +95,8 @@
 import android.util.SparseIntArray;
 import android.util.SparseLongArray;
 import android.util.TypedValue;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 import android.view.IWindowManager;
 
@@ -104,7 +106,6 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.StatLogger;
 import com.android.server.LocalServices;
@@ -119,10 +120,7 @@
 import org.json.JSONObject;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -799,31 +797,31 @@
     // === Persisting ===
 
     @Nullable
-    static String parseStringAttribute(XmlPullParser parser, String attribute) {
+    static String parseStringAttribute(TypedXmlPullParser parser, String attribute) {
         return parser.getAttributeValue(null, attribute);
     }
 
-    static boolean parseBooleanAttribute(XmlPullParser parser, String attribute) {
+    static boolean parseBooleanAttribute(TypedXmlPullParser parser, String attribute) {
         return parseLongAttribute(parser, attribute) == 1;
     }
 
-    static boolean parseBooleanAttribute(XmlPullParser parser, String attribute, boolean def) {
+    static boolean parseBooleanAttribute(TypedXmlPullParser parser, String attribute, boolean def) {
         return parseLongAttribute(parser, attribute, (def ? 1 : 0)) == 1;
     }
 
-    static int parseIntAttribute(XmlPullParser parser, String attribute) {
+    static int parseIntAttribute(TypedXmlPullParser parser, String attribute) {
         return (int) parseLongAttribute(parser, attribute);
     }
 
-    static int parseIntAttribute(XmlPullParser parser, String attribute, int def) {
+    static int parseIntAttribute(TypedXmlPullParser parser, String attribute, int def) {
         return (int) parseLongAttribute(parser, attribute, def);
     }
 
-    static long parseLongAttribute(XmlPullParser parser, String attribute) {
+    static long parseLongAttribute(TypedXmlPullParser parser, String attribute) {
         return parseLongAttribute(parser, attribute, 0);
     }
 
-    static long parseLongAttribute(XmlPullParser parser, String attribute, long def) {
+    static long parseLongAttribute(TypedXmlPullParser parser, String attribute, long def) {
         final String value = parseStringAttribute(parser, attribute);
         if (TextUtils.isEmpty(value)) {
             return def;
@@ -837,7 +835,7 @@
     }
 
     @Nullable
-    static ComponentName parseComponentNameAttribute(XmlPullParser parser, String attribute) {
+    static ComponentName parseComponentNameAttribute(TypedXmlPullParser parser, String attribute) {
         final String value = parseStringAttribute(parser, attribute);
         if (TextUtils.isEmpty(value)) {
             return null;
@@ -846,7 +844,7 @@
     }
 
     @Nullable
-    static Intent parseIntentAttributeNoDefault(XmlPullParser parser, String attribute) {
+    static Intent parseIntentAttributeNoDefault(TypedXmlPullParser parser, String attribute) {
         final String value = parseStringAttribute(parser, attribute);
         Intent parsed = null;
         if (!TextUtils.isEmpty(value)) {
@@ -860,7 +858,7 @@
     }
 
     @Nullable
-    static Intent parseIntentAttribute(XmlPullParser parser, String attribute) {
+    static Intent parseIntentAttribute(TypedXmlPullParser parser, String attribute) {
         Intent parsed = parseIntentAttributeNoDefault(parser, attribute);
         if (parsed == null) {
             // Default intent.
@@ -869,7 +867,7 @@
         return parsed;
     }
 
-    static void writeTagValue(XmlSerializer out, String tag, String value) throws IOException {
+    static void writeTagValue(TypedXmlSerializer out, String tag, String value) throws IOException {
         if (TextUtils.isEmpty(value)) return;
 
         out.startTag(null, tag);
@@ -877,16 +875,17 @@
         out.endTag(null, tag);
     }
 
-    static void writeTagValue(XmlSerializer out, String tag, long value) throws IOException {
+    static void writeTagValue(TypedXmlSerializer out, String tag, long value) throws IOException {
         writeTagValue(out, tag, Long.toString(value));
     }
 
-    static void writeTagValue(XmlSerializer out, String tag, ComponentName name) throws IOException {
+    static void writeTagValue(TypedXmlSerializer out, String tag, ComponentName name)
+            throws IOException {
         if (name == null) return;
         writeTagValue(out, tag, name.flattenToString());
     }
 
-    static void writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle)
+    static void writeTagExtra(TypedXmlSerializer out, String tag, PersistableBundle bundle)
             throws IOException, XmlPullParserException {
         if (bundle == null) return;
 
@@ -895,17 +894,18 @@
         out.endTag(null, tag);
     }
 
-    static void writeAttr(XmlSerializer out, String name, CharSequence value) throws IOException {
+    static void writeAttr(TypedXmlSerializer out, String name, CharSequence value)
+            throws IOException {
         if (TextUtils.isEmpty(value)) return;
 
         out.attribute(null, name, value.toString());
     }
 
-    static void writeAttr(XmlSerializer out, String name, long value) throws IOException {
+    static void writeAttr(TypedXmlSerializer out, String name, long value) throws IOException {
         writeAttr(out, name, String.valueOf(value));
     }
 
-    static void writeAttr(XmlSerializer out, String name, boolean value) throws IOException {
+    static void writeAttr(TypedXmlSerializer out, String name, boolean value) throws IOException {
         if (value) {
             writeAttr(out, name, "1");
         } else {
@@ -913,12 +913,13 @@
         }
     }
 
-    static void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException {
+    static void writeAttr(TypedXmlSerializer out, String name, ComponentName comp)
+            throws IOException {
         if (comp == null) return;
         writeAttr(out, name, comp.flattenToString());
     }
 
-    static void writeAttr(XmlSerializer out, String name, Intent intent) throws IOException {
+    static void writeAttr(TypedXmlSerializer out, String name, Intent intent) throws IOException {
         if (intent == null) return;
 
         writeAttr(out, name, intent.toUri(/* flags =*/ 0));
@@ -937,8 +938,7 @@
             outs = file.startWrite();
 
             // Write to XML
-            XmlSerializer out = new FastXmlSerializer();
-            out.setOutput(outs, StandardCharsets.UTF_8.name());
+            TypedXmlSerializer out = Xml.resolveSerializer(outs);
             out.startDocument(null, true);
             out.startTag(null, TAG_ROOT);
 
@@ -966,8 +966,7 @@
             Slog.d(TAG, "Loading from " + file.getBaseFile());
         }
         try (FileInputStream in = file.openRead()) {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(in, StandardCharsets.UTF_8.name());
+            TypedXmlPullParser parser = Xml.resolvePullParser(in);
 
             int type;
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -1043,18 +1042,20 @@
     private void saveUserInternalLocked(@UserIdInt int userId, OutputStream os,
             boolean forBackup) throws IOException, XmlPullParserException {
 
-        final BufferedOutputStream bos = new BufferedOutputStream(os);
-
         // Write to XML
-        XmlSerializer out = new FastXmlSerializer();
-        out.setOutput(bos, StandardCharsets.UTF_8.name());
+        final TypedXmlSerializer out;
+        if (forBackup) {
+            out = Xml.newFastSerializer();
+            out.setOutput(os, StandardCharsets.UTF_8.name());
+        } else {
+            out = Xml.resolveSerializer(os);
+        }
         out.startDocument(null, true);
 
         getUserShortcutsLocked(userId).saveToXml(out, forBackup);
 
         out.endDocument();
 
-        bos.flush();
         os.flush();
     }
 
@@ -1098,11 +1099,14 @@
             boolean fromBackup) throws XmlPullParserException, IOException,
             InvalidFileFormatException {
 
-        final BufferedInputStream bis = new BufferedInputStream(is);
-
         ShortcutUser ret = null;
-        XmlPullParser parser = Xml.newPullParser();
-        parser.setInput(bis, StandardCharsets.UTF_8.name());
+        TypedXmlPullParser parser;
+        if (fromBackup) {
+            parser = Xml.newFastPullParser();
+            parser.setInput(is, StandardCharsets.UTF_8.name());
+        } else {
+            parser = Xml.resolvePullParser(is);
+        }
 
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 5c1d8fb..3e3aa67 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -26,6 +26,8 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
@@ -38,7 +40,6 @@
 import org.json.JSONObject;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.IOException;
@@ -329,7 +330,7 @@
         });
     }
 
-    public void saveToXml(XmlSerializer out, boolean forBackup)
+    public void saveToXml(TypedXmlSerializer out, boolean forBackup)
             throws IOException, XmlPullParserException {
         out.startTag(null, TAG_ROOT);
 
@@ -371,7 +372,7 @@
         out.endTag(null, TAG_ROOT);
     }
 
-    private void saveShortcutPackageItem(XmlSerializer out, ShortcutPackageItem spi,
+    private void saveShortcutPackageItem(TypedXmlSerializer out, ShortcutPackageItem spi,
             boolean forBackup) throws IOException, XmlPullParserException {
         if (forBackup) {
             if (spi.getPackageUserId() != spi.getOwnerUserId()) {
@@ -408,7 +409,7 @@
         return new File(path, fileName);
     }
 
-    public static ShortcutUser loadFromXml(ShortcutService s, XmlPullParser parser, int userId,
+    public static ShortcutUser loadFromXml(ShortcutService s, TypedXmlPullParser parser, int userId,
             boolean fromBackup) throws IOException, XmlPullParserException, InvalidFileFormatException {
         final ShortcutUser ret = new ShortcutUser(s, userId);
         boolean readShortcutItems = false;
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 2e64a70..1d3aecd 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -303,7 +303,8 @@
                 while (XmlUtils.nextElementWithin(parser, depth)) {
                     final String childName = parser.getName();
                     if ("default-restrictions".equals(childName)) {
-                        final Bundle restrictions = UserRestrictionsUtils.readRestrictions(parser);
+                        final Bundle restrictions = UserRestrictionsUtils
+                                .readRestrictions(XmlUtils.makeTyped(parser));
                         builder.setDefaultRestrictions(restrictions);
                     } else if (isProfile && "badge-labels".equals(childName)) {
                         setResAttributeArray(parser, builder::setBadgeLabels);
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index b3c786e..1656472 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -73,7 +73,6 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -1414,11 +1413,8 @@
                 Slog.w(TAG, "Default permissions file " + file + " cannot be read");
                 continue;
             }
-            try (
-                InputStream str = new BufferedInputStream(new FileInputStream(file))
-            ) {
-                TypedXmlPullParser parser = Xml.newFastPullParser();
-                parser.setInput(str, null);
+            try (InputStream str = new FileInputStream(file)) {
+                TypedXmlPullParser parser = Xml.resolvePullParser(str);
                 parse(pm, parser, grantExceptions);
             } catch (XmlPullParserException | IOException e) {
                 Slog.w(TAG, "Error reading default permissions file " + file, e);
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
index 9829357..c9595c2 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
@@ -177,8 +177,28 @@
             // filename, so any files that don't match the current version number can be deleted.
             File[] files = mDataStorageDir.listFiles();
             for (int i = 0; i < files.length; i++) {
-                if (!files[i].getName().matches(dataStorageFilename + "(.*)")) {
-                    files[i].delete();
+                // Meter and model files are stored in the same directory.
+                //
+                // The format of filenames on disk is:
+                //    log.powerstats.meter.version.timestamp
+                //    log.powerstats.model.version.timestamp
+                //
+                // The format of dataStorageFilenames is:
+                //    log.powerstats.meter.version
+                //    log.powerstats.model.version
+                //
+                // A PowerStatsDataStorage object is created for meter and model data.  Strip off
+                // the version and check that the current file we're checking starts with the stem
+                // (log.powerstats.meter or log.powerstats.model). If the stem matches and the
+                // version number is different, delete the old file.
+                int versionDot = dataStorageFilename.lastIndexOf('.');
+                String beforeVersionDot = dataStorageFilename.substring(0, versionDot);
+                // Check that the stems match.
+                if (files[i].getName().startsWith(beforeVersionDot)) {
+                    // Check that the version number matches.  If not, delete the old file.
+                    if (!files[i].getName().startsWith(dataStorageFilename)) {
+                        files[i].delete();
+                    }
                 }
             }
 
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index b33dc8f..7751397 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -28,6 +28,7 @@
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
@@ -391,8 +392,7 @@
     private void readLegacyFileLocked() {
         File file = getFile(mUserId);
         try (FileInputStream in = new AtomicFile(file).openRead()) {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(in, null);
+            TypedXmlPullParser parser = Xml.resolvePullParser(in);
             parseXmlLocked(parser);
             Slog.i(LOG_TAG, "Read roles.xml successfully");
         } catch (FileNotFoundException e) {
@@ -402,7 +402,7 @@
         }
     }
 
-    private void parseXmlLocked(@NonNull XmlPullParser parser) throws IOException,
+    private void parseXmlLocked(@NonNull TypedXmlPullParser parser) throws IOException,
             XmlPullParserException {
         int type;
         int depth;
@@ -421,9 +421,9 @@
         Slog.w(LOG_TAG, "Missing <" + TAG_ROLES + "> in roles.xml");
     }
 
-    private void parseRolesLocked(@NonNull XmlPullParser parser) throws IOException,
+    private void parseRolesLocked(@NonNull TypedXmlPullParser parser) throws IOException,
             XmlPullParserException {
-        mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_VERSION));
+        mVersion = parser.getAttributeInt(null, ATTRIBUTE_VERSION);
         mPackagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH);
         mRoles.clear();
 
@@ -445,7 +445,7 @@
     }
 
     @NonNull
-    private ArraySet<String> parseRoleHoldersLocked(@NonNull XmlPullParser parser)
+    private ArraySet<String> parseRoleHoldersLocked(@NonNull TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         ArraySet<String> roleHolders = new ArraySet<>();
 
diff --git a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
index d2614e4..4f3101d 100644
--- a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
+++ b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
@@ -51,13 +51,10 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.Preconditions;
 import com.android.server.pm.Installer;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -65,7 +62,6 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -319,12 +315,12 @@
     }
 
     @VisibleForTesting
-    static void saveToXml(XmlSerializer out,
+    static void saveToXml(TypedXmlSerializer out,
             List<CacheQuotaHint> requests, long bytesWhenCalculated) throws IOException {
         out.startDocument(null, true);
         out.startTag(null, CACHE_INFO_TAG);
         int requestSize = requests.size();
-        out.attribute(null, ATTR_PREVIOUS_BYTES, Long.toString(bytesWhenCalculated));
+        out.attributeLong(null, ATTR_PREVIOUS_BYTES, bytesWhenCalculated);
 
         for (int i = 0; i < requestSize; i++) {
             CacheQuotaHint request = requests.get(i);
@@ -333,8 +329,8 @@
             if (uuid != null) {
                 out.attribute(null, ATTR_UUID, request.getVolumeUuid());
             }
-            out.attribute(null, ATTR_UID, Integer.toString(request.getUid()));
-            out.attribute(null, ATTR_QUOTA_IN_BYTES, Long.toString(request.getQuota()));
+            out.attributeInt(null, ATTR_UID, request.getUid());
+            out.attributeLong(null, ATTR_QUOTA_IN_BYTES, request.getQuota());
             out.endTag(null, TAG_QUOTA);
         }
         out.endTag(null, CACHE_INFO_TAG);
@@ -364,8 +360,7 @@
         final List<CacheQuotaHint> quotas = new ArrayList<>();
         long previousBytes;
         try {
-            previousBytes = Long.parseLong(parser.getAttributeValue(
-                    null, ATTR_PREVIOUS_BYTES));
+            previousBytes = parser.getAttributeLong(null, ATTR_PREVIOUS_BYTES);
         } catch (NumberFormatException e) {
             throw new IllegalStateException(
                     "Previous bytes formatted incorrectly; aborting quota read.");
@@ -389,14 +384,14 @@
     }
 
     @VisibleForTesting
-    static CacheQuotaHint getRequestFromXml(XmlPullParser parser) {
+    static CacheQuotaHint getRequestFromXml(TypedXmlPullParser parser) {
         try {
             String uuid = parser.getAttributeValue(null, ATTR_UUID);
-            int uid = Integer.parseInt(parser.getAttributeValue(null, ATTR_UID));
-            long bytes = Long.parseLong(parser.getAttributeValue(null, ATTR_QUOTA_IN_BYTES));
+            int uid = parser.getAttributeInt(null, ATTR_UID);
+            long bytes = parser.getAttributeLong(null, ATTR_QUOTA_IN_BYTES);
             return new CacheQuotaHint.Builder()
                     .setVolumeUuid(uuid).setUid(uid).setQuota(bytes).build();
-        } catch (NumberFormatException e) {
+        } catch (XmlPullParserException e) {
             Slog.e(TAG, "Invalid cache quota request, skipping.");
             return null;
         }
diff --git a/services/core/java/com/android/server/timezone/PackageStatusStorage.java b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
index 7652c43..fd0df8d 100644
--- a/services/core/java/com/android/server/timezone/PackageStatusStorage.java
+++ b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
@@ -129,7 +129,7 @@
     @GuardedBy("this")
     private PackageStatus getPackageStatusLocked() throws ParseException {
         try (FileInputStream fis = mPackageStatusFile.openRead()) {
-            XmlPullParser parser = parseToPackageStatusTag(fis);
+            TypedXmlPullParser parser = parseToPackageStatusTag(fis);
             Integer checkStatus = getNullableIntAttribute(parser, ATTRIBUTE_CHECK_STATUS);
             if (checkStatus == null) {
                 return null;
@@ -254,7 +254,7 @@
     @GuardedBy("this")
     private int getCurrentOptimisticLockId() throws ParseException {
         try (FileInputStream fis = mPackageStatusFile.openRead()) {
-            XmlPullParser parser = parseToPackageStatusTag(fis);
+            TypedXmlPullParser parser = parseToPackageStatusTag(fis);
             return getIntAttribute(parser, ATTRIBUTE_OPTIMISTIC_LOCK_ID);
         } catch (IOException e) {
             ParseException e2 = new ParseException("Unable to read file", 0);
@@ -264,7 +264,7 @@
     }
 
     /** Returns a parser or throws ParseException, never returns null. */
-    private static XmlPullParser parseToPackageStatusTag(FileInputStream fis)
+    private static TypedXmlPullParser parseToPackageStatusTag(FileInputStream fis)
             throws ParseException {
         try {
             TypedXmlPullParser parser = Xml.resolvePullParser(fis);
@@ -358,7 +358,7 @@
         }
     }
 
-    private static Integer getNullableIntAttribute(XmlPullParser parser, String attributeName)
+    private static Integer getNullableIntAttribute(TypedXmlPullParser parser, String attributeName)
             throws ParseException {
         String attributeValue = parser.getAttributeValue(null, attributeName);
         try {
@@ -374,7 +374,7 @@
         }
     }
 
-    private static int getIntAttribute(XmlPullParser parser, String attributeName)
+    private static int getIntAttribute(TypedXmlPullParser parser, String attributeName)
             throws ParseException {
         Integer value = getNullableIntAttribute(parser, attributeName);
         if (value == null) {
diff --git a/services/core/java/com/android/server/tv/PersistentDataStore.java b/services/core/java/com/android/server/tv/PersistentDataStore.java
index 355c385..d3c9b3b 100644
--- a/services/core/java/com/android/server/tv/PersistentDataStore.java
+++ b/services/core/java/com/android/server/tv/PersistentDataStore.java
@@ -26,6 +26,7 @@
 import android.text.TextUtils;
 import android.util.AtomicFile;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
@@ -166,7 +167,7 @@
             return;
         }
 
-        XmlPullParser parser;
+        TypedXmlPullParser parser;
         try {
             parser = Xml.resolvePullParser(is);
             loadFromXml(parser);
@@ -237,7 +238,7 @@
     private static final String ATTR_STRING = "string";
     private static final String ATTR_ENABLED = "enabled";
 
-    private void loadFromXml(XmlPullParser parser)
+    private void loadFromXml(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         XmlUtils.beginDocument(parser, TAG_TV_INPUT_MANAGER_STATE);
         final int outerDepth = parser.getDepth();
@@ -255,7 +256,7 @@
         }
     }
 
-    private void loadBlockedRatingsFromXml(XmlPullParser parser)
+    private void loadBlockedRatingsFromXml(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         final int outerDepth = parser.getDepth();
         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
@@ -270,7 +271,7 @@
         }
     }
 
-    private void saveToXml(XmlSerializer serializer) throws IOException {
+    private void saveToXml(TypedXmlSerializer serializer) throws IOException {
         serializer.startDocument(null, true);
         serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
         serializer.startTag(null, TAG_TV_INPUT_MANAGER_STATE);
@@ -284,7 +285,7 @@
         }
         serializer.endTag(null, TAG_BLOCKED_RATINGS);
         serializer.startTag(null, TAG_PARENTAL_CONTROLS);
-        serializer.attribute(null, ATTR_ENABLED, Boolean.toString(mParentalControlsEnabled));
+        serializer.attributeBoolean(null, ATTR_ENABLED, mParentalControlsEnabled);
         serializer.endTag(null, TAG_PARENTAL_CONTROLS);
         serializer.endTag(null, TAG_TV_INPUT_MANAGER_STATE);
         serializer.endDocument();
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java
index 367b966..beb11ed 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java
@@ -20,6 +20,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -114,9 +115,7 @@
     protected void parseInternal(InputStream in)
             throws IOException, XmlPullParserException {
         try {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
-            parser.setInput(in, null);
+            TypedXmlPullParser parser = Xml.resolvePullParser(in);
             parser.nextTag();
             readUseCase(parser);
             in.close();
@@ -137,7 +136,7 @@
         }
     }
 
-    private void readUseCase(XmlPullParser parser)
+    private void readUseCase(TypedXmlPullParser parser)
             throws XmlPullParserException, IOException {
         parser.require(XmlPullParser.START_TAG, NS, "config");
         while (parser.next() != XmlPullParser.END_TAG) {
@@ -176,7 +175,7 @@
         }
     }
 
-    private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
+    private void skip(TypedXmlPullParser parser) throws XmlPullParserException, IOException {
         if (parser.getEventType() != XmlPullParser.START_TAG) {
             throw new IllegalStateException();
         }
@@ -193,7 +192,7 @@
         }
     }
 
-    private int readAttributeToInt(String attributeName, XmlPullParser parser) {
+    private int readAttributeToInt(String attributeName, TypedXmlPullParser parser) {
         return Integer.valueOf(parser.getAttributeValue(null, attributeName));
     }
 
@@ -203,7 +202,7 @@
     }
 
     @PriorityHintUseCaseType
-    private static int formatTypeToNum(String attributeName, XmlPullParser parser) {
+    private static int formatTypeToNum(String attributeName, TypedXmlPullParser parser) {
         String useCaseName = parser.getAttributeValue(null, attributeName);
         switch (useCaseName) {
             case "USE_CASE_BACKGROUND":
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index bbb5374..dcc1599 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -668,22 +668,22 @@
                     if (TAG_URI_GRANT.equals(tag)) {
                         final int sourceUserId;
                         final int targetUserId;
-                        final int userHandle = readIntAttribute(in,
-                                ATTR_USER_HANDLE, UserHandle.USER_NULL);
+                        final int userHandle = in.getAttributeInt(null, ATTR_USER_HANDLE,
+                                UserHandle.USER_NULL);
                         if (userHandle != UserHandle.USER_NULL) {
                             // For backwards compatibility.
                             sourceUserId = userHandle;
                             targetUserId = userHandle;
                         } else {
-                            sourceUserId = readIntAttribute(in, ATTR_SOURCE_USER_ID);
-                            targetUserId = readIntAttribute(in, ATTR_TARGET_USER_ID);
+                            sourceUserId = in.getAttributeInt(null, ATTR_SOURCE_USER_ID);
+                            targetUserId = in.getAttributeInt(null, ATTR_TARGET_USER_ID);
                         }
                         final String sourcePkg = in.getAttributeValue(null, ATTR_SOURCE_PKG);
                         final String targetPkg = in.getAttributeValue(null, ATTR_TARGET_PKG);
                         final Uri uri = Uri.parse(in.getAttributeValue(null, ATTR_URI));
-                        final boolean prefix = readBooleanAttribute(in, ATTR_PREFIX);
-                        final int modeFlags = readIntAttribute(in, ATTR_MODE_FLAGS);
-                        final long createdTime = readLongAttribute(in, ATTR_CREATED_TIME, now);
+                        final boolean prefix = in.getAttributeBoolean(null, ATTR_PREFIX, false);
+                        final int modeFlags = in.getAttributeInt(null, ATTR_MODE_FLAGS);
+                        final long createdTime = in.getAttributeLong(null, ATTR_CREATED_TIME, now);
 
                         // Validity check that provider still belongs to source package
                         // Both direct boot aware and unaware packages are fine as we
@@ -1319,14 +1319,14 @@
             out.startTag(null, TAG_URI_GRANTS);
             for (UriPermission.Snapshot perm : persist) {
                 out.startTag(null, TAG_URI_GRANT);
-                writeIntAttribute(out, ATTR_SOURCE_USER_ID, perm.uri.sourceUserId);
-                writeIntAttribute(out, ATTR_TARGET_USER_ID, perm.targetUserId);
+                out.attributeInt(null, ATTR_SOURCE_USER_ID, perm.uri.sourceUserId);
+                out.attributeInt(null, ATTR_TARGET_USER_ID, perm.targetUserId);
                 out.attribute(null, ATTR_SOURCE_PKG, perm.sourcePkg);
                 out.attribute(null, ATTR_TARGET_PKG, perm.targetPkg);
                 out.attribute(null, ATTR_URI, String.valueOf(perm.uri.uri));
                 writeBooleanAttribute(out, ATTR_PREFIX, perm.uri.prefix);
-                writeIntAttribute(out, ATTR_MODE_FLAGS, perm.persistedModeFlags);
-                writeLongAttribute(out, ATTR_CREATED_TIME, perm.persistedCreateTime);
+                out.attributeInt(null, ATTR_MODE_FLAGS, perm.persistedModeFlags);
+                out.attributeLong(null, ATTR_CREATED_TIME, perm.persistedCreateTime);
                 out.endTag(null, TAG_URI_GRANT);
             }
             out.endTag(null, TAG_URI_GRANTS);
diff --git a/services/core/java/com/android/server/wm/utils/DeviceConfigInterface.java b/services/core/java/com/android/server/utils/DeviceConfigInterface.java
similarity index 90%
rename from services/core/java/com/android/server/wm/utils/DeviceConfigInterface.java
rename to services/core/java/com/android/server/utils/DeviceConfigInterface.java
index ab7e7f6..ff60903 100644
--- a/services/core/java/com/android/server/wm/utils/DeviceConfigInterface.java
+++ b/services/core/java/com/android/server/utils/DeviceConfigInterface.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.wm.utils;
+package com.android.server.utils;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -54,6 +54,11 @@
     boolean getBoolean(@NonNull String namespace, @NonNull String name, boolean defaultValue);
 
     /**
+     * @see DeviceConfig#getFloat
+     */
+    float getFloat(@NonNull String namespace, @NonNull String name, float defaultValue);
+
+    /**
      * @see DeviceConfig#addOnPropertiesChangedListener
      */
     void addOnPropertiesChangedListener(@NonNull String namespace, @NonNull Executor executor,
@@ -96,6 +101,12 @@
         }
 
         @Override
+        public float getFloat(@NonNull String namespace, @NonNull String name,
+                float defaultValue) {
+            return DeviceConfig.getFloat(namespace, name, defaultValue);
+        }
+
+        @Override
         public void addOnPropertiesChangedListener(String namespace, Executor executor,
                 DeviceConfig.OnPropertiesChangedListener listener) {
             DeviceConfig.addOnPropertiesChangedListener(namespace, executor, listener);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index e07540a..58344120 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -39,7 +39,7 @@
 import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
 import android.app.WallpaperManager.SetWallpaperFlags;
-import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManagerInternal;
 import android.app.backup.WallpaperBackupHelper;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -100,12 +100,12 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
 import com.android.server.EventLogTags;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.SystemService.TargetUser;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.utils.TimingsTraceAndSlog;
 import com.android.server.wm.WindowManagerInternal;
@@ -114,7 +114,6 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.BufferedOutputStream;
 import java.io.File;
@@ -125,7 +124,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -2848,10 +2846,8 @@
         if (!uidMatchPackage) {
             return false;   // callingPackage was faked.
         }
-
-        // TODO(b/144048540): DPM needs to take into account the userId, not just the package.
-        final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
-        if (dpm.isDeviceOwnerApp(callingPackage) || dpm.isProfileOwnerApp(callingPackage)) {
+        if (LocalServices.getService(DevicePolicyManagerInternal.class)
+                .isDeviceOrProfileOwnerInCallingUser(callingPackage)) {
             return true;
         }
         final int callingUserId = UserHandle.getCallingUserId();
@@ -2909,11 +2905,9 @@
     private void saveSettingsLocked(int userId) {
         JournaledFile journal = makeJournaledFile(userId);
         FileOutputStream fstream = null;
-        BufferedOutputStream stream = null;
         try {
             fstream = new FileOutputStream(journal.chooseForWrite(), false);
-            stream = new BufferedOutputStream(fstream);
-            TypedXmlSerializer out = Xml.resolveSerializer(stream);
+            TypedXmlSerializer out = Xml.resolveSerializer(fstream);
             out.startDocument(null, true);
 
             WallpaperData wallpaper;
@@ -2929,56 +2923,56 @@
 
             out.endDocument();
 
-            stream.flush(); // also flushes fstream
+            fstream.flush();
             FileUtils.sync(fstream);
-            stream.close(); // also closes fstream
+            fstream.close();
             journal.commit();
         } catch (IOException e) {
-            IoUtils.closeQuietly(stream);
+            IoUtils.closeQuietly(fstream);
             journal.rollback();
         }
     }
 
-    private void writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper)
+    private void writeWallpaperAttributes(TypedXmlSerializer out, String tag,
+            WallpaperData wallpaper)
             throws IllegalArgumentException, IllegalStateException, IOException {
         if (DEBUG) {
             Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId);
         }
         final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
         out.startTag(null, tag);
-        out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
-        out.attribute(null, "width", Integer.toString(wpdData.mWidth));
-        out.attribute(null, "height", Integer.toString(wpdData.mHeight));
+        out.attributeInt(null, "id", wallpaper.wallpaperId);
+        out.attributeInt(null, "width", wpdData.mWidth);
+        out.attributeInt(null, "height", wpdData.mHeight);
 
-        out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left));
-        out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top));
-        out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right));
-        out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom));
+        out.attributeInt(null, "cropLeft", wallpaper.cropHint.left);
+        out.attributeInt(null, "cropTop", wallpaper.cropHint.top);
+        out.attributeInt(null, "cropRight", wallpaper.cropHint.right);
+        out.attributeInt(null, "cropBottom", wallpaper.cropHint.bottom);
 
         if (wpdData.mPadding.left != 0) {
-            out.attribute(null, "paddingLeft", Integer.toString(wpdData.mPadding.left));
+            out.attributeInt(null, "paddingLeft", wpdData.mPadding.left);
         }
         if (wpdData.mPadding.top != 0) {
-            out.attribute(null, "paddingTop", Integer.toString(wpdData.mPadding.top));
+            out.attributeInt(null, "paddingTop", wpdData.mPadding.top);
         }
         if (wpdData.mPadding.right != 0) {
-            out.attribute(null, "paddingRight", Integer.toString(wpdData.mPadding.right));
+            out.attributeInt(null, "paddingRight", wpdData.mPadding.right);
         }
         if (wpdData.mPadding.bottom != 0) {
-            out.attribute(null, "paddingBottom", Integer.toString(wpdData.mPadding.bottom));
+            out.attributeInt(null, "paddingBottom", wpdData.mPadding.bottom);
         }
 
         if (wallpaper.primaryColors != null) {
             int colorsCount = wallpaper.primaryColors.getMainColors().size();
-            out.attribute(null, "colorsCount", Integer.toString(colorsCount));
+            out.attributeInt(null, "colorsCount", colorsCount);
             if (colorsCount > 0) {
                 for (int i = 0; i < colorsCount; i++) {
                     final Color wc = wallpaper.primaryColors.getMainColors().get(i);
-                    out.attribute(null, "colorValue"+i, Integer.toString(wc.toArgb()));
+                    out.attributeInt(null, "colorValue" + i, wc.toArgb());
                 }
             }
-            out.attribute(null, "colorHints",
-                    Integer.toString(wallpaper.primaryColors.getColorHints()));
+            out.attributeInt(null, "colorHints", wallpaper.primaryColors.getColorHints());
         }
 
         out.attribute(null, "name", wallpaper.name);
@@ -3028,12 +3022,8 @@
         }
     }
 
-    private int getAttributeInt(XmlPullParser parser, String name, int defValue) {
-        String value = parser.getAttributeValue(null, name);
-        if (value == null) {
-            return defValue;
-        }
-        return Integer.parseInt(value);
+    private int getAttributeInt(TypedXmlPullParser parser, String name, int defValue) {
+        return parser.getAttributeInt(null, name, defValue);
     }
 
     /**
@@ -3213,11 +3203,11 @@
         }
     }
 
-    private void parseWallpaperAttributes(XmlPullParser parser, WallpaperData wallpaper,
-            boolean keepDimensionHints) {
-        final String idString = parser.getAttributeValue(null, "id");
-        if (idString != null) {
-            final int id = wallpaper.wallpaperId = Integer.parseInt(idString);
+    private void parseWallpaperAttributes(TypedXmlPullParser parser, WallpaperData wallpaper,
+            boolean keepDimensionHints) throws XmlPullParserException {
+        final int id = parser.getAttributeInt(null, "id", -1);
+        if (id != -1) {
+            wallpaper.wallpaperId = id;
             if (id > mWallpaperId) {
                 mWallpaperId = id;
             }
@@ -3228,8 +3218,8 @@
         final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
 
         if (!keepDimensionHints) {
-            wpData.mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
-            wpData.mHeight = Integer.parseInt(parser.getAttributeValue(null, "height"));
+            wpData.mWidth = parser.getAttributeInt(null, "width");
+            wpData.mHeight = parser.getAttributeInt(null, "height");
         }
         wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0);
         wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0);
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index b4ca7c5..6a50b79 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -425,20 +425,20 @@
         mLastLogTimeSecs = now;
 
         mWindowState = WINDOW_STATE_INVALID;
-        Task stack = mSupervisor.mRootWindowContainer.getTopDisplayFocusedStack();
-        if (stack == null) {
+        Task rootTask = mSupervisor.mRootWindowContainer.getTopDisplayFocusedRootTask();
+        if (rootTask == null) {
             return;
         }
 
-        if (stack.isActivityTypeAssistant()) {
+        if (rootTask.isActivityTypeAssistant()) {
             mWindowState = WINDOW_STATE_ASSISTANT;
             return;
         }
 
-        @WindowingMode int windowingMode = stack.getWindowingMode();
+        @WindowingMode int windowingMode = rootTask.getWindowingMode();
         if (windowingMode == WINDOWING_MODE_PINNED) {
-            stack = mSupervisor.mRootWindowContainer.findStackBehind(stack);
-            windowingMode = stack.getWindowingMode();
+            rootTask = mSupervisor.mRootWindowContainer.findRootTaskBehind(rootTask);
+            windowingMode = rootTask.getWindowingMode();
         }
         switch (windowingMode) {
             case WINDOWING_MODE_FULLSCREEN:
@@ -456,7 +456,7 @@
                 break;
             default:
                 if (windowingMode != WINDOWING_MODE_UNDEFINED) {
-                    throw new IllegalStateException("Unknown windowing mode for stack=" + stack
+                    throw new IllegalStateException("Unknown windowing mode for task=" + rootTask
                             + " windowingMode=" + windowingMode);
                 }
         }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 913c3e5..02643ef 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -18,7 +18,6 @@
 
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
-import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
 import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
 import static android.app.ActivityOptions.ANIM_CUSTOM;
 import static android.app.ActivityOptions.ANIM_NONE;
@@ -96,18 +95,18 @@
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
+import static android.view.WindowManager.TRANSIT_OLD_UNSET;
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_UNSET;
-import static android.view.WindowManager.TransitionOldType;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -183,6 +182,7 @@
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
+import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
 import static com.android.server.wm.Task.ActivityState.DESTROYED;
 import static com.android.server.wm.Task.ActivityState.DESTROYING;
 import static com.android.server.wm.Task.ActivityState.FINISHING;
@@ -194,7 +194,6 @@
 import static com.android.server.wm.Task.ActivityState.STARTED;
 import static com.android.server.wm.Task.ActivityState.STOPPED;
 import static com.android.server.wm.Task.ActivityState.STOPPING;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
 import static com.android.server.wm.TaskPersister.DEBUG;
 import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
@@ -277,6 +276,8 @@
 import android.util.MergedConfiguration;
 import android.util.Slog;
 import android.util.TimeUtils;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.proto.ProtoOutputStream;
 import android.view.AppTransitionAnimationSpec;
 import android.view.DisplayCutout;
@@ -291,6 +292,7 @@
 import android.view.WindowInsets.Type;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
+import android.view.WindowManager.TransitionOldType;
 import android.view.animation.Animation;
 import android.window.WindowContainerToken;
 
@@ -320,9 +322,7 @@
 
 import com.google.android.collect.Sets;
 
-import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.IOException;
@@ -2550,15 +2550,15 @@
             return FINISH_RESULT_CANCELLED;
         }
 
-        final Task stack = getRootTask();
-        final boolean mayAdjustTop = (isState(RESUMED) || stack.mResumedActivity == null)
-                && stack.isFocusedStackOnDisplay()
+        final Task rootTask = getRootTask();
+        final boolean mayAdjustTop = (isState(RESUMED) || rootTask.mResumedActivity == null)
+                && rootTask.isFocusedStackOnDisplay()
                 // Do not adjust focus task because the task will be reused to launch new activity.
                 && !task.isClearingToReuseTask();
         final boolean shouldAdjustGlobalFocus = mayAdjustTop
                 // It must be checked before {@link #makeFinishingLocked} is called, because a stack
                 // is not visible if it only contains finishing activities.
-                && mRootWindowContainer.isTopDisplayFocusedStack(stack);
+                && mRootWindowContainer.isTopDisplayFocusedRootTask(rootTask);
 
         mAtmService.deferWindowLayout();
         try {
@@ -2623,12 +2623,12 @@
                 // Tell window manager to prepare for this one to be removed.
                 setVisibility(false);
 
-                if (stack.mPausingActivity == null) {
+                if (rootTask.mPausingActivity == null) {
                     ProtoLog.v(WM_DEBUG_STATES, "Finish needs to pause: %s", this);
                     if (DEBUG_USER_LEAVING) {
                         Slog.v(TAG_USER_LEAVING, "finish() => pause with userLeaving=false");
                     }
-                    stack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
+                    rootTask.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
                             null /* resuming */, "finish");
                 }
 
@@ -2827,7 +2827,7 @@
                     false /* markFrozenIfConfigChanged */, true /* deferResume */);
         }
         if (activityRemoved) {
-            mRootWindowContainer.resumeFocusedStacksTopActivities();
+            mRootWindowContainer.resumeFocusedTasksTopActivities();
         }
 
         ProtoLog.d(WM_DEBUG_CONTAINERS, "destroyIfPossible: r=%s destroy returned "
@@ -2849,7 +2849,7 @@
             mTaskSupervisor.mFinishingActivities.add(this);
         }
         resumeKeyDispatchingLocked();
-        return mRootWindowContainer.resumeFocusedStacksTopActivities();
+        return mRootWindowContainer.resumeFocusedTasksTopActivities();
     }
 
     /**
@@ -3014,7 +3014,7 @@
             removeFromHistory(reason);
         }
 
-        mRootWindowContainer.resumeFocusedStacksTopActivities();
+        mRootWindowContainer.resumeFocusedTasksTopActivities();
     }
 
     /**
@@ -5206,7 +5206,7 @@
             } else {
                 if (deferRelaunchUntilPaused) {
                     destroyImmediately("stop-config");
-                    mRootWindowContainer.resumeFocusedStacksTopActivities();
+                    mRootWindowContainer.resumeFocusedTasksTopActivities();
                 } else {
                     mRootWindowContainer.updatePreviousProcess(this);
                 }
@@ -5703,7 +5703,7 @@
         // First find the real culprit...  if this activity has stopped, then the key dispatching
         // timeout should not be caused by this.
         if (stopped) {
-            final Task stack = mRootWindowContainer.getTopDisplayFocusedStack();
+            final Task stack = mRootWindowContainer.getTopDisplayFocusedRootTask();
             if (stack == null) {
                 return this;
             }
@@ -6091,7 +6091,7 @@
     @Override
     public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
         t.setLayer(leash, getAnimationLayer());
-        getDisplayContent().assignStackOrdering();
+        getDisplayContent().assignRootTaskOrdering();
     }
 
     @Override
@@ -6372,14 +6372,7 @@
     }
 
     void setRequestedOrientation(int requestedOrientation) {
-        setOrientation(requestedOrientation, mayFreezeScreenLocked());
-        mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
-                task.mTaskId, requestedOrientation);
-    }
-
-    private void setOrientation(int requestedOrientation, boolean freezeScreenIfNeeded) {
-        final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null;
-        setOrientation(requestedOrientation, binder, this);
+        setOrientation(requestedOrientation, this);
 
         // Push the new configuration to the requested app in case where it's not pushed, e.g. when
         // the request is handled at task level with letterbox.
@@ -6387,6 +6380,9 @@
                 mLastReportedConfiguration.getMergedConfiguration())) {
             ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
         }
+
+        mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
+                task.mTaskId, requestedOrientation);
     }
 
     /*
@@ -6403,8 +6399,7 @@
             return;
         }
 
-        final IBinder freezeToken = mayFreezeScreenLocked() ? appToken : null;
-        if (onDescendantOrientationChanged(freezeToken, this)) {
+        if (onDescendantOrientationChanged(this)) {
             // The app is just becoming visible, and the parent Task has updated with the
             // orientation request. Update the size compat mode.
             updateSizeCompatMode();
@@ -7081,6 +7076,12 @@
             return true;
         }
 
+        if (isState(DESTROYED)) {
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check "
+                    + "in destroyed state %s", this);
+            return true;
+        }
+
         if (!ignoreVisibility && (mState == STOPPING || mState == STOPPED || !shouldBeVisible())) {
             ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check "
                     + "invisible: %s", this);
@@ -7464,9 +7465,9 @@
                 || (info.flags & FLAG_NO_HISTORY) != 0;
     }
 
-    void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
-        out.attribute(null, ATTR_ID, String.valueOf(createTime));
-        out.attribute(null, ATTR_LAUNCHEDFROMUID, String.valueOf(launchedFromUid));
+    void saveToXml(TypedXmlSerializer out) throws IOException, XmlPullParserException {
+        out.attributeLong(null, ATTR_ID, createTime);
+        out.attributeInt(null, ATTR_LAUNCHEDFROMUID, launchedFromUid);
         if (launchedFromPackage != null) {
             out.attribute(null, ATTR_LAUNCHEDFROMPACKAGE, launchedFromPackage);
         }
@@ -7476,8 +7477,8 @@
         if (resolvedType != null) {
             out.attribute(null, ATTR_RESOLVEDTYPE, resolvedType);
         }
-        out.attribute(null, ATTR_COMPONENTSPECIFIED, String.valueOf(componentSpecified));
-        out.attribute(null, ATTR_USERID, String.valueOf(mUserId));
+        out.attributeBoolean(null, ATTR_COMPONENTSPECIFIED, componentSpecified);
+        out.attributeInt(null, ATTR_USERID, mUserId);
 
         if (taskDescription != null) {
             taskDescription.saveToXml(out);
@@ -7494,43 +7495,20 @@
         }
     }
 
-    static ActivityRecord restoreFromXml(XmlPullParser in,
+    static ActivityRecord restoreFromXml(TypedXmlPullParser in,
             ActivityTaskSupervisor taskSupervisor) throws IOException, XmlPullParserException {
         Intent intent = null;
         PersistableBundle persistentState = null;
-        int launchedFromUid = 0;
-        String launchedFromPackage = null;
-        String launchedFromFeature = null;
-        String resolvedType = null;
-        boolean componentSpecified = false;
-        int userId = 0;
-        long createTime = -1;
+        int launchedFromUid = in.getAttributeInt(null, ATTR_LAUNCHEDFROMUID, 0);
+        String launchedFromPackage = in.getAttributeValue(null, ATTR_LAUNCHEDFROMPACKAGE);
+        String launchedFromFeature = in.getAttributeValue(null, ATTR_LAUNCHEDFROMFEATURE);
+        String resolvedType = in.getAttributeValue(null, ATTR_RESOLVEDTYPE);
+        boolean componentSpecified = in.getAttributeBoolean(null, ATTR_COMPONENTSPECIFIED, false);
+        int userId = in.getAttributeInt(null, ATTR_USERID, 0);
+        long createTime = in.getAttributeLong(null, ATTR_ID, -1);
         final int outerDepth = in.getDepth();
-        TaskDescription taskDescription = new TaskDescription();
 
-        for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
-            final String attrName = in.getAttributeName(attrNdx);
-            final String attrValue = in.getAttributeValue(attrNdx);
-            if (DEBUG) Slog.d(TaskPersister.TAG,
-                        "ActivityRecord: attribute name=" + attrName + " value=" + attrValue);
-            if (ATTR_ID.equals(attrName)) {
-                createTime = Long.parseLong(attrValue);
-            } else if (ATTR_LAUNCHEDFROMUID.equals(attrName)) {
-                launchedFromUid = Integer.parseInt(attrValue);
-            } else if (ATTR_LAUNCHEDFROMPACKAGE.equals(attrName)) {
-                launchedFromPackage = attrValue;
-            } else if (ATTR_LAUNCHEDFROMFEATURE.equals(attrName)) {
-                launchedFromFeature = attrValue;
-            } else if (ATTR_RESOLVEDTYPE.equals(attrName)) {
-                resolvedType = attrValue;
-            } else if (ATTR_COMPONENTSPECIFIED.equals(attrName)) {
-                componentSpecified = Boolean.parseBoolean(attrValue);
-            } else if (ATTR_USERID.equals(attrName)) {
-                userId = Integer.parseInt(attrValue);
-            } else if (!attrName.startsWith(ATTR_TASKDESCRIPTION_PREFIX)) {
-                Log.d(TAG, "Unknown ActivityRecord attribute=" + attrName);
-            }
-        }
+        TaskDescription taskDescription = new TaskDescription();
         taskDescription.restoreFromXml(in);
 
         int event;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index b88b54e..0bea251 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -664,7 +664,7 @@
             synchronized (mService.mGlobalLock) {
                 final boolean globalConfigWillChange = mRequest.globalConfig != null
                         && mService.getGlobalConfiguration().diff(mRequest.globalConfig) != 0;
-                final Task stack = mRootWindowContainer.getTopDisplayFocusedStack();
+                final Task stack = mRootWindowContainer.getTopDisplayFocusedRootTask();
                 if (stack != null) {
                     stack.mConfigWillChange = globalConfigWillChange;
                 }
@@ -900,7 +900,7 @@
         ActivityRecord sourceRecord = null;
         ActivityRecord resultRecord = null;
         if (resultTo != null) {
-            sourceRecord = mRootWindowContainer.isInAnyStack(resultTo);
+            sourceRecord = mRootWindowContainer.isInAnyTask(resultTo);
             if (DEBUG_RESULTS) {
                 Slog.v(TAG_RESULTS, "Will send result to " + resultTo + " " + sourceRecord);
             }
@@ -1135,7 +1135,7 @@
 
                 if (DEBUG_PERMISSIONS_REVIEW) {
                     final Task focusedStack =
-                            mRootWindowContainer.getTopDisplayFocusedStack();
+                            mRootWindowContainer.getTopDisplayFocusedRootTask();
                     Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
                             true, false) + "} from uid " + callingUid + " on display "
                             + (focusedStack == null ? DEFAULT_DISPLAY
@@ -1175,7 +1175,7 @@
             r.appTimeTracker = sourceRecord.appTimeTracker;
         }
 
-        final Task stack = mRootWindowContainer.getTopDisplayFocusedStack();
+        final Task stack = mRootWindowContainer.getTopDisplayFocusedRootTask();
 
         // If we are starting an activity that is not from the same uid as the currently resumed
         // one, check whether app switches are allowed.
@@ -1718,7 +1718,7 @@
 
         // If the activity being launched is the same as the one currently at the top, then
         // we need to check if it should only be launched once.
-        final Task topStack = mRootWindowContainer.getTopDisplayFocusedStack();
+        final Task topStack = mRootWindowContainer.getTopDisplayFocusedRootTask();
         if (topStack != null) {
             startResult = deliverToCurrentTopIfNeeded(topStack, intentGrants);
             if (startResult != START_SUCCESS) {
@@ -1806,14 +1806,14 @@
                 // task stack to be focusable, then ensure that we now update the focused stack
                 // accordingly.
                 if (mTargetStack.isTopActivityFocusable()
-                        && !mRootWindowContainer.isTopDisplayFocusedStack(mTargetStack)) {
+                        && !mRootWindowContainer.isTopDisplayFocusedRootTask(mTargetStack)) {
                     mTargetStack.moveToFront("startActivityInner");
                 }
-                mRootWindowContainer.resumeFocusedStacksTopActivities(
+                mRootWindowContainer.resumeFocusedTasksTopActivities(
                         mTargetStack, mStartActivity, mOptions);
             }
         }
-        mRootWindowContainer.updateUserStack(mStartActivity.mUserId, mTargetStack);
+        mRootWindowContainer.updateUserRootTask(mStartActivity.mUserId, mTargetStack);
 
         // Update the recent tasks list immediately when the activity starts
         mSupervisor.mRecentTasks.add(mStartActivity.getTask());
@@ -1849,7 +1849,7 @@
     private void computeLaunchParams(ActivityRecord r, ActivityRecord sourceRecord,
             Task targetTask) {
         final Task sourceStack = mSourceStack != null ? mSourceStack
-                : mRootWindowContainer.getTopDisplayFocusedStack();
+                : mRootWindowContainer.getTopDisplayFocusedRootTask();
         if (sourceStack != null && sourceStack.inSplitScreenWindowingMode()
                 && (mOptions == null
                         || mOptions.getLaunchWindowingMode() == WINDOWING_MODE_UNDEFINED)) {
@@ -2048,7 +2048,7 @@
         // For paranoia, make sure we have correctly resumed the top activity.
         topStack.mLastPausedActivity = null;
         if (mDoResume) {
-            mRootWindowContainer.resumeFocusedStacksTopActivities();
+            mRootWindowContainer.resumeFocusedTasksTopActivities();
         }
         ActivityOptions.abort(mOptions);
         if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
@@ -2347,7 +2347,7 @@
         if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
             ActivityRecord checkedCaller = sourceRecord;
             if (checkedCaller == null) {
-                Task topFocusedStack = mRootWindowContainer.getTopDisplayFocusedStack();
+                Task topFocusedStack = mRootWindowContainer.getTopDisplayFocusedRootTask();
                 if (topFocusedStack != null) {
                     checkedCaller = topFocusedStack.topRunningNonDelayedActivityLocked(mNotTop);
                 }
@@ -2567,7 +2567,7 @@
         // to the front if the caller is not itself in the front.
         final boolean differentTopTask;
         if (mTargetStack.getDisplayArea() == mPreferredTaskDisplayArea) {
-            final Task focusStack = mTargetStack.mDisplayContent.getFocusedStack();
+            final Task focusStack = mTargetStack.mDisplayContent.getFocusedRootTask();
             final ActivityRecord curTop = (focusStack == null)
                     ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
             final Task topTask = curTop != null ? curTop.getTask() : null;
@@ -2634,11 +2634,11 @@
             if (next != null) {
                 next.setCurrentLaunchCanTurnScreenOn(true);
             }
-            mRootWindowContainer.resumeFocusedStacksTopActivities(mTargetStack, null, mOptions);
+            mRootWindowContainer.resumeFocusedTasksTopActivities(mTargetStack, null, mOptions);
         } else {
             ActivityOptions.abort(mOptions);
         }
-        mRootWindowContainer.updateUserStack(mStartActivity.mUserId, mTargetStack);
+        mRootWindowContainer.updateUserRootTask(mStartActivity.mUserId, mTargetStack);
     }
 
     private void setNewTask(Task taskToAffiliate) {
@@ -2713,7 +2713,7 @@
 
         final boolean onTop =
                 (aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind;
-        return mRootWindowContainer.getLaunchStack(r, aOptions, task, onTop, mLaunchParams,
+        return mRootWindowContainer.getLaunchRootTask(r, aOptions, task, onTop, mLaunchParams,
                 mRequest.realCallingPid, mRequest.realCallingUid);
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 8ba76be..3710120 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1156,7 +1156,7 @@
         synchronized (mGlobalLock) {
             // If this is coming from the currently resumed activity, it is
             // effectively saying that app switches are allowed at this point.
-            final Task stack = getTopDisplayFocusedStack();
+            final Task stack = getTopDisplayFocusedRootTask();
             if (stack != null && stack.mResumedActivity != null
                     && stack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) {
                 mAppSwitchesAllowedTime = 0;
@@ -1470,7 +1470,7 @@
                 sourceToken = resultTo;
             }
 
-            sourceRecord = mRootWindowContainer.isInAnyStack(sourceToken);
+            sourceRecord = mRootWindowContainer.isInAnyTask(sourceToken);
             if (sourceRecord == null) {
                 throw new SecurityException("Called with bad activity token: " + sourceToken);
             }
@@ -2039,7 +2039,7 @@
     public boolean isTopActivityImmersive() {
         enforceNotIsolatedCaller("isTopActivityImmersive");
         synchronized (mGlobalLock) {
-            final Task topFocusedStack = getTopDisplayFocusedStack();
+            final Task topFocusedStack = getTopDisplayFocusedRootTask();
             if (topFocusedStack == null) {
                 return false;
             }
@@ -2074,7 +2074,7 @@
     public int getFrontActivityScreenCompatMode() {
         enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
         synchronized (mGlobalLock) {
-            final Task stack = getTopDisplayFocusedStack();
+            final Task stack = getTopDisplayFocusedRootTask();
             final ActivityRecord r = stack != null ? stack.topRunningActivity() : null;
             if (r == null) {
                 return ActivityManager.COMPAT_MODE_UNKNOWN;
@@ -2089,7 +2089,7 @@
                 "setFrontActivityScreenCompatMode");
         ApplicationInfo ai;
         synchronized (mGlobalLock) {
-            final Task stack = getTopDisplayFocusedStack();
+            final Task stack = getTopDisplayFocusedRootTask();
             final ActivityRecord r = stack != null ? stack.topRunningActivity() : null;
             if (r == null) {
                 Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
@@ -2165,7 +2165,7 @@
     public void notifyActivityDrawn(IBinder token) {
         if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, "notifyActivityDrawn: token=" + token);
         synchronized (mGlobalLock) {
-            ActivityRecord r = mRootWindowContainer.isInAnyStack(token);
+            ActivityRecord r = mRootWindowContainer.isInAnyTask(token);
             if (r != null) {
                 r.getRootTask().notifyActivityDrawnLocked(r);
             }
@@ -2201,7 +2201,7 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                Task focusedStack = getTopDisplayFocusedStack();
+                Task focusedStack = getTopDisplayFocusedRootTask();
                 if (focusedStack != null) {
                     return mRootWindowContainer.getRootTaskInfo(focusedStack.mTaskId);
                 }
@@ -2219,14 +2219,14 @@
         final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                final Task task = mRootWindowContainer.getStack(taskId);
+                final Task task = mRootWindowContainer.getRootTask(taskId);
                 if (task == null) {
                     Slog.w(TAG, "setFocusedRootTask: No task with id=" + taskId);
                     return;
                 }
                 final ActivityRecord r = task.topRunningActivity();
                 if (r != null && r.moveFocusableActivityToTop("setFocusedRootTask")) {
-                    mRootWindowContainer.resumeFocusedStacksTopActivities();
+                    mRootWindowContainer.resumeFocusedTasksTopActivities();
                 }
             }
         } finally {
@@ -2248,7 +2248,7 @@
                 }
                 final ActivityRecord r = task.topRunningActivityLocked();
                 if (r != null && r.moveFocusableActivityToTop("setFocusedTask")) {
-                    mRootWindowContainer.resumeFocusedStacksTopActivities();
+                    mRootWindowContainer.resumeFocusedTasksTopActivities();
                 }
             }
         } finally {
@@ -2508,7 +2508,7 @@
         synchronized (mGlobalLock) {
             final long origId = Binder.clearCallingIdentity();
             try {
-                final Task topFocusedStack = getTopDisplayFocusedStack();
+                final Task topFocusedStack = getTopDisplayFocusedRootTask();
                 if (topFocusedStack != null) {
                     topFocusedStack.unhandledBackLocked();
                 }
@@ -2820,7 +2820,7 @@
                 ProtoLog.d(WM_DEBUG_TASKS, "moveTaskToRootTask: moving task=%d to "
                         + "rootTaskId=%d toTop=%b", taskId, rootTaskId, toTop);
 
-                final Task rootTask = mRootWindowContainer.getStack(rootTaskId);
+                final Task rootTask = mRootWindowContainer.getRootTask(rootTaskId);
                 if (rootTask == null) {
                     throw new IllegalStateException(
                             "moveTaskToRootTask: No rootTask for rootTaskId=" + rootTaskId);
@@ -3088,8 +3088,8 @@
             return;
         }
 
-        final Task stack = mRootWindowContainer.getTopDisplayFocusedStack();
-        if (stack == null || task != stack.getTopMostTask()) {
+        final Task rootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
+        if (rootTask == null || task != rootTask.getTopMostTask()) {
             throw new IllegalArgumentException("Invalid task, not in foreground");
         }
 
@@ -3370,7 +3370,7 @@
                 }
 
                 final Task stack = r.getRootTask();
-                final Task task = stack.getDisplayArea().createStack(stack.getWindowingMode(),
+                final Task task = stack.getDisplayArea().createRootTask(stack.getWindowingMode(),
                         stack.getActivityType(), !ON_TOP, ainfo, intent,
                         false /* createdByOrganizer */);
 
@@ -3533,7 +3533,7 @@
             try {
                 ProtoLog.d(WM_DEBUG_TASKS, "moveRootTaskToDisplay: moving taskId=%d to "
                         + "displayId=%d", taskId, displayId);
-                mRootWindowContainer.moveStackToDisplay(taskId, displayId, ON_TOP);
+                mRootWindowContainer.moveRootTaskToDisplay(taskId, displayId, ON_TOP);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -3741,7 +3741,7 @@
                 "enqueueAssistContext()");
 
         synchronized (mGlobalLock) {
-            final Task stack = getTopDisplayFocusedStack();
+            final Task stack = getTopDisplayFocusedRootTask();
             ActivityRecord activity = stack != null ? stack.getTopNonFinishingActivity() : null;
             if (activity == null) {
                 Slog.w(TAG, "getAssistContextExtras failed: no top activity");
@@ -3870,7 +3870,7 @@
     public boolean isAssistDataAllowedOnCurrentActivity() {
         int userId;
         synchronized (mGlobalLock) {
-            final Task focusedStack = getTopDisplayFocusedStack();
+            final Task focusedStack = getTopDisplayFocusedRootTask();
             if (focusedStack == null || focusedStack.isActivityTypeAssistant()) {
                 return false;
             }
@@ -3890,7 +3890,7 @@
         try {
             synchronized (mGlobalLock) {
                 ActivityRecord caller = ActivityRecord.forTokenLocked(token);
-                ActivityRecord top = getTopDisplayFocusedStack().getTopNonFinishingActivity();
+                ActivityRecord top = getTopDisplayFocusedRootTask().getTopNonFinishingActivity();
                 if (top != caller) {
                     Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
                             + " is not current top " + top);
@@ -4069,7 +4069,7 @@
 
             final long ident = Binder.clearCallingIdentity();
             try {
-                return mRootWindowContainer.moveTopStackActivityToPinnedRootTask(rootTaskId);
+                return mRootWindowContainer.moveTopRootTaskActivityToPinnedRootTask(rootTaskId);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -4113,7 +4113,7 @@
                 r.setPictureInPictureParams(params);
                 final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
                 final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
-                mRootWindowContainer.moveActivityToPinnedStack(
+                mRootWindowContainer.moveActivityToPinnedRootTask(
                         r, "enterPictureInPictureMode");
                 final Task stack = r.getRootTask();
                 stack.setPictureInPictureAspectRatio(aspectRatio);
@@ -4345,7 +4345,7 @@
     public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
         Slog.i(TAG, "Activity tried to startLocalVoiceInteraction");
         synchronized (mGlobalLock) {
-            ActivityRecord activity = getTopDisplayFocusedStack().getTopNonFinishingActivity();
+            ActivityRecord activity = getTopDisplayFocusedRootTask().getTopNonFinishingActivity();
             if (ActivityRecord.forTokenLocked(callingActivity) != activity) {
                 throw new SecurityException("Only focused activity can call startVoiceInteraction");
             }
@@ -4749,7 +4749,7 @@
         if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) {
             Slog.i(TAG, "Moving " + r.shortComponentName + " from display " + r.getDisplayId()
                     + " to main display for VR");
-            mRootWindowContainer.moveStackToDisplay(
+            mRootWindowContainer.moveRootTaskToDisplay(
                     r.getRootTaskId(), DEFAULT_DISPLAY, true /* toTop */);
         }
         mH.post(() -> {
@@ -4813,8 +4813,8 @@
         }
     }
 
-    Task getTopDisplayFocusedStack() {
-        return mRootWindowContainer.getTopDisplayFocusedStack();
+    Task getTopDisplayFocusedRootTask() {
+        return mRootWindowContainer.getTopDisplayFocusedRootTask();
     }
 
     /** Pokes the task persister. */
@@ -5827,7 +5827,7 @@
     /** Applies latest configuration and/or visibility updates if needed. */
     boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
         boolean kept = true;
-        final Task mainStack = mRootWindowContainer.getTopDisplayFocusedStack();
+        final Task mainStack = mRootWindowContainer.getTopDisplayFocusedRootTask();
         // mainStack is null during startup.
         if (mainStack != null) {
             if (changes != 0 && starting == null) {
@@ -6082,8 +6082,8 @@
         static final int REPORT_TIME_TRACKER_MSG = 1;
 
 
-        static final int FIRST_ACTIVITY_STACK_MSG = 100;
-        static final int FIRST_SUPERVISOR_STACK_MSG = 200;
+        static final int FIRST_ACTIVITY_TASK_MSG = 100;
+        static final int FIRST_SUPERVISOR_TASK_MSG = 200;
 
         H(Looper looper) {
             super(looper);
@@ -6292,7 +6292,7 @@
                             "setFocusedActivity: No activity record matching token=" + token);
                 }
                 if (r.moveFocusableActivityToTop("setFocusedActivity")) {
-                    mRootWindowContainer.resumeFocusedStacksTopActivities();
+                    mRootWindowContainer.resumeFocusedTasksTopActivities();
                 }
             }
         }
@@ -6796,7 +6796,7 @@
                 if (!restarting && hasVisibleActivities) {
                     deferWindowLayout();
                     try {
-                        if (!mRootWindowContainer.resumeFocusedStacksTopActivities()) {
+                        if (!mRootWindowContainer.resumeFocusedTasksTopActivities()) {
                             // If there was nothing to resume, and we are not already restarting
                             // this process, but there is a visible activity that is hosted by the
                             // process...then make sure all visible activities are running, taking
@@ -6849,7 +6849,7 @@
                 if (mRootWindowContainer.finishDisabledPackageActivities(
                         packageName, disabledClasses, true /* doit */, false /* evenPersistent */,
                         userId, false /* onlyRemoveNoProcess */) && booted) {
-                    mRootWindowContainer.resumeFocusedStacksTopActivities();
+                    mRootWindowContainer.resumeFocusedTasksTopActivities();
                     mTaskSupervisor.scheduleIdle();
                 }
 
@@ -6879,7 +6879,7 @@
         @Override
         public void resumeTopActivities(boolean scheduleIdle) {
             synchronized (mGlobalLock) {
-                mRootWindowContainer.resumeFocusedStacksTopActivities();
+                mRootWindowContainer.resumeFocusedTasksTopActivities();
                 if (scheduleIdle) {
                     mTaskSupervisor.scheduleIdle();
                 }
@@ -7056,7 +7056,7 @@
                     mRootWindowContainer.dumpDisplayConfigs(pw, "  ");
                 }
                 if (dumpAll) {
-                    final Task topFocusedStack = getTopDisplayFocusedStack();
+                    final Task topFocusedStack = getTopDisplayFocusedRootTask();
                     if (dumpPackage == null && topFocusedStack != null) {
                         pw.println("  mConfigWillChange: " + topFocusedStack.mConfigWillChange);
                     }
@@ -7139,7 +7139,7 @@
             synchronized (mGlobalLock) {
                 if (dumpPackage == null) {
                     getGlobalConfiguration().dumpDebug(proto, GLOBAL_CONFIGURATION);
-                    final Task topFocusedStack = getTopDisplayFocusedStack();
+                    final Task topFocusedStack = getTopDisplayFocusedRootTask();
                     if (topFocusedStack != null) {
                         proto.write(CONFIG_WILL_CHANGE, topFocusedStack.mConfigWillChange);
                     }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index d0c26af..d94c7af 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -63,7 +63,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
-import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_SUPERVISOR_STACK_MSG;
+import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_SUPERVISOR_TASK_MSG;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_ALLOWLISTED;
 import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE;
@@ -176,18 +176,18 @@
     /** How long we wait until giving up on the activity telling us it released the top state. */
     private static final int TOP_RESUMED_STATE_LOSS_TIMEOUT = 500;
 
-    private static final int IDLE_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG;
-    private static final int IDLE_NOW_MSG = FIRST_SUPERVISOR_STACK_MSG + 1;
-    private static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_STACK_MSG + 2;
-    private static final int SLEEP_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 3;
-    private static final int LAUNCH_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 4;
-    private static final int PROCESS_STOPPING_AND_FINISHING_MSG = FIRST_SUPERVISOR_STACK_MSG + 5;
-    private static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 12;
-    private static final int RESTART_ACTIVITY_PROCESS_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 13;
-    private static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14;
-    private static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15;
-    private static final int START_HOME_MSG = FIRST_SUPERVISOR_STACK_MSG + 16;
-    private static final int TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 17;
+    private static final int IDLE_TIMEOUT_MSG = FIRST_SUPERVISOR_TASK_MSG;
+    private static final int IDLE_NOW_MSG = FIRST_SUPERVISOR_TASK_MSG + 1;
+    private static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_TASK_MSG + 2;
+    private static final int SLEEP_TIMEOUT_MSG = FIRST_SUPERVISOR_TASK_MSG + 3;
+    private static final int LAUNCH_TIMEOUT_MSG = FIRST_SUPERVISOR_TASK_MSG + 4;
+    private static final int PROCESS_STOPPING_AND_FINISHING_MSG = FIRST_SUPERVISOR_TASK_MSG + 5;
+    private static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_TASK_MSG + 12;
+    private static final int RESTART_ACTIVITY_PROCESS_TIMEOUT_MSG = FIRST_SUPERVISOR_TASK_MSG + 13;
+    private static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_TASK_MSG + 14;
+    private static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_TASK_MSG + 15;
+    private static final int START_HOME_MSG = FIRST_SUPERVISOR_TASK_MSG + 16;
+    private static final int TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG = FIRST_SUPERVISOR_TASK_MSG + 17;
 
     // Used to indicate that windows of activities should be preserved during the resize.
     static final boolean PRESERVE_WINDOWS = true;
@@ -202,8 +202,8 @@
     // Used to indicate that a task is removed it should also be removed from recents.
     static final boolean REMOVE_FROM_RECENTS = true;
 
-    /** True if the docked stack is currently being resized. */
-    private boolean mDockedStackResizing;
+    /** True if the docked root task is currently being resized. */
+    private boolean mDockedRootTaskResizing;
 
     // Activity actions an app cannot start if it uses a permission which is not granted.
     private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
@@ -307,7 +307,7 @@
 
     /** The target stack bounds for the picture-in-picture mode changed that we need to report to
      * the application */
-    private Rect mPipModeChangedTargetStackBounds;
+    private Rect mPipModeChangedTargetRootTaskBounds;
 
     /** Used on user changes */
     final ArrayList<UserState> mStartingUsers = new ArrayList<>();
@@ -386,17 +386,17 @@
         final ActivityRecord r;
         final ActivityRecord sourceRecord;
         final int startFlags;
-        final Task stack;
+        final Task rootTask;
         final WindowProcessController callerApp;
         final NeededUriGrants intentGrants;
 
         PendingActivityLaunch(ActivityRecord r, ActivityRecord sourceRecord,
-                int startFlags, Task stack, WindowProcessController callerApp,
+                int startFlags, Task rootTask, WindowProcessController callerApp,
                 NeededUriGrants intentGrants) {
             this.r = r;
             this.sourceRecord = sourceRecord;
             this.startFlags = startFlags;
-            this.stack = stack;
+            this.rootTask = rootTask;
             this.callerApp = callerApp;
             this.intentGrants = intentGrants;
         }
@@ -501,11 +501,11 @@
         getKeyguardController().setWindowManager(wm);
     }
 
-    void moveRecentsStackToFront(String reason) {
-        final Task recentsStack = mRootWindowContainer.getDefaultTaskDisplayArea()
-                .getStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
-        if (recentsStack != null) {
-            recentsStack.moveToFront(reason);
+    void moveRecentsRootTaskToFront(String reason) {
+        final Task recentsRootTask = mRootWindowContainer.getDefaultTaskDisplayArea()
+                .getRootTask(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
+        if (recentsRootTask != null) {
+            recentsRootTask.moveToFront(reason);
         }
     }
 
@@ -944,7 +944,7 @@
         // launching the initial activity (that is, home), so that it can have
         // a chance to initialize itself while in the background, making the
         // switch back to it faster and look better.
-        if (mRootWindowContainer.isTopDisplayFocusedStack(rootTask)) {
+        if (mRootWindowContainer.isTopDisplayFocusedRootTask(rootTask)) {
             mService.getActivityStartController().startSetupActivity();
         }
 
@@ -1376,10 +1376,10 @@
             task.setBounds(bounds);
 
             Task stack =
-                    mRootWindowContainer.getLaunchStack(null, options, task, ON_TOP);
+                    mRootWindowContainer.getLaunchRootTask(null, options, task, ON_TOP);
 
             if (stack != currentStack) {
-                moveHomeStackToFrontIfNeeded(flags, stack.getDisplayArea(), reason);
+                moveHomeRootTaskToFrontIfNeeded(flags, stack.getDisplayArea(), reason);
                 task.reparent(stack, ON_TOP, REPARENT_KEEP_ROOT_TASK_AT_FRONT, !ANIMATE,
                         DEFER_RESUME, reason);
                 currentStack = stack;
@@ -1398,7 +1398,7 @@
         }
 
         if (!reparented) {
-            moveHomeStackToFrontIfNeeded(flags, currentStack.getDisplayArea(), reason);
+            moveHomeRootTaskToFrontIfNeeded(flags, currentStack.getDisplayArea(), reason);
         }
 
         final ActivityRecord r = task.getTopNonFinishingActivity();
@@ -1412,16 +1412,16 @@
                 mRootWindowContainer.getDefaultTaskDisplayArea(), currentStack, forceNonResizeable);
     }
 
-    private void moveHomeStackToFrontIfNeeded(int flags, TaskDisplayArea taskDisplayArea,
+    private void moveHomeRootTaskToFrontIfNeeded(int flags, TaskDisplayArea taskDisplayArea,
             String reason) {
-        final Task focusedStack = taskDisplayArea.getFocusedStack();
+        final Task focusedRootTask = taskDisplayArea.getFocusedRootTask();
 
         if ((taskDisplayArea.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
                 && (flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0)
-                || (focusedStack != null && focusedStack.isActivityTypeRecents())) {
+                || (focusedRootTask != null && focusedRootTask.isActivityTypeRecents())) {
             // We move home stack to front when we are on a fullscreen display area and caller has
             // requested the home activity to move with it. Or the previous stack is recents.
-            taskDisplayArea.moveHomeStackToFront(reason);
+            taskDisplayArea.moveHomeRootTaskToFront(reason);
         }
     }
 
@@ -1441,15 +1441,15 @@
     }
 
     void setSplitScreenResizing(boolean resizing) {
-        if (resizing == mDockedStackResizing) {
+        if (resizing == mDockedRootTaskResizing) {
             return;
         }
 
-        mDockedStackResizing = resizing;
+        mDockedRootTaskResizing = resizing;
         mWindowManager.setDockedStackResizing(resizing);
     }
 
-    private void removePinnedStackInSurfaceTransaction(Task stack) {
+    private void removePinnedRootTaskInSurfaceTransaction(Task rootTask) {
         /**
          * Workaround: Force-stop all the activities in the pinned stack before we reparent them
          * to the fullscreen stack.  This is to guarantee that when we are removing a stack,
@@ -1459,9 +1459,9 @@
          * marked invisible as well and added to the stopping list.  After which we process the
          * stopping list by handling the idle.
          */
-        stack.cancelAnimation();
-        stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */);
-        stack.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+        rootTask.cancelAnimation();
+        rootTask.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */);
+        rootTask.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
         activityIdleInternal(null /* idleActivity */, false /* fromTimeout */,
                 true /* processPausingActivities */, null /* configuration */);
 
@@ -1471,17 +1471,17 @@
 
         mService.deferWindowLayout();
         try {
-            stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
-            if (stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
-                stack.setBounds(null);
+            rootTask.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+            if (rootTask.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+                rootTask.setBounds(null);
             }
-            toDisplay.getDefaultTaskDisplayArea().positionTaskBehindHome(stack);
+            toDisplay.getDefaultTaskDisplayArea().positionTaskBehindHome(rootTask);
 
             // Follow on the workaround: activities are kept force hidden till the new windowing
             // mode is set.
-            stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */);
+            rootTask.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */);
             mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
-            mRootWindowContainer.resumeFocusedStacksTopActivities();
+            mRootWindowContainer.resumeFocusedTasksTopActivities();
         } finally {
             mService.continueWindowLayout();
         }
@@ -1489,7 +1489,7 @@
 
     private void removeRootTaskInSurfaceTransaction(Task rootTask) {
         if (rootTask.getWindowingMode() == WINDOWING_MODE_PINNED) {
-            removePinnedStackInSurfaceTransaction(rootTask);
+            removePinnedRootTaskInSurfaceTransaction(rootTask);
         } else {
             final PooledConsumer c = PooledLambda.obtainConsumer(
                     ActivityTaskSupervisor::processRemoveTask, this, PooledLambda.__(Task.class));
@@ -1627,7 +1627,7 @@
      */
     boolean restoreRecentTaskLocked(Task task, ActivityOptions aOptions, boolean onTop) {
         final Task stack =
-                mRootWindowContainer.getLaunchStack(null, aOptions, task, onTop);
+                mRootWindowContainer.getLaunchRootTask(null, aOptions, task, onTop);
         final WindowContainer parent = task.getParent();
 
         if (parent == stack || task == stack) {
@@ -1668,7 +1668,7 @@
      * the various checks on tasks that are going to be reparented from one stack to another.
      */
     // TODO: Look into changing users to this method to DisplayContent.resolveWindowingMode()
-    Task getReparentTargetStack(Task task, Task stack, boolean toTop) {
+    Task getReparentTargetRootTask(Task task, Task stack, boolean toTop) {
         final Task prevStack = task.getRootTask();
         final int rootTaskId = stack.mTaskId;
         final boolean inMultiWindowMode = stack.inMultiWindowMode();
@@ -1709,7 +1709,7 @@
             if (prevStack != null) {
                 return prevStack;
             }
-            stack = stack.getDisplayArea().createStack(
+            stack = stack.getDisplayArea().createRootTask(
                     WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), toTop);
         }
         return stack;
@@ -1739,7 +1739,7 @@
         boolean timedout = false;
         final long endTime = System.currentTimeMillis() + timeout;
         while (true) {
-            if (!mRootWindowContainer.putStacksToSleep(
+            if (!mRootWindowContainer.putTasksToSleep(
                     true /* allowDelay */, true /* shuttingDown */)) {
                 long timeRemaining = endTime - System.currentTimeMillis();
                 if (timeRemaining > 0) {
@@ -1776,7 +1776,7 @@
             return;
         }
 
-        if (!mRootWindowContainer.putStacksToSleep(
+        if (!mRootWindowContainer.putTasksToSleep(
                 allowDelay, false /* shuttingDown */)) {
             return;
         }
@@ -1931,7 +1931,7 @@
         mService.getLockTaskController().dump(pw, prefix);
         pw.print(prefix);
         pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
-        pw.println(prefix + "mUserStackInFront=" + mRootWindowContainer.mUserStackInFront);
+        pw.println(prefix + "mUserRootTaskInFront=" + mRootWindowContainer.mUserRootTaskInFront);
         pw.println(prefix + "mVisibilityTransactionDepth=" + mVisibilityTransactionDepth);
         if (!mWaitingForActivityVisible.isEmpty()) {
             pw.println(prefix + "mWaitingForActivityVisible=");
@@ -2058,7 +2058,7 @@
      */
     void updateTopResumedActivityIfNeeded() {
         final ActivityRecord prevTopActivity = mTopResumedActivity;
-        final Task topStack = mRootWindowContainer.getTopDisplayFocusedStack();
+        final Task topStack = mRootWindowContainer.getTopDisplayFocusedRootTask();
         if (topStack == null || topStack.mResumedActivity == prevTopActivity) {
             if (mService.isSleepingLocked()) {
                 // There won't be a next resumed activity. The top process should still be updated
@@ -2242,7 +2242,7 @@
         }
     }
 
-    void logStackState() {
+    void logRootTaskState() {
         mActivityMetricsLogger.logWindowState();
     }
 
@@ -2281,7 +2281,7 @@
         task.forAllActivities(c);
         c.recycle();
 
-        mPipModeChangedTargetStackBounds = targetStackBounds;
+        mPipModeChangedTargetRootTaskBounds = targetStackBounds;
 
         if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
             mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG);
@@ -2436,7 +2436,7 @@
                 case REPORT_PIP_MODE_CHANGED_MSG: {
                     for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
                         final ActivityRecord r = mPipModeChangedActivities.remove(i);
-                        r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds,
+                        r.updatePictureInPictureMode(mPipModeChangedTargetRootTaskBounds,
                                 false /* forceUpdate */);
                     }
                 } break;
@@ -2452,7 +2452,7 @@
                     activityIdleFromMessage((ActivityRecord) msg.obj, false /* fromTimeout */);
                 } break;
                 case RESUME_TOP_ACTIVITY_MSG: {
-                    mRootWindowContainer.resumeFocusedStacksTopActivities();
+                    mRootWindowContainer.resumeFocusedTasksTopActivities();
                 } break;
                 case SLEEP_TIMEOUT_MSG: {
                     if (mService.isSleepingOrShuttingDownLocked()) {
@@ -2542,7 +2542,7 @@
                 // from whatever is started from the recents activity, so move the home stack
                 // forward.
                 // TODO (b/115289124): Multi-display supports for recents.
-                mRootWindowContainer.getDefaultTaskDisplayArea().moveHomeStackToFront(
+                mRootWindowContainer.getDefaultTaskDisplayArea().moveHomeRootTaskToFront(
                         "startActivityFromRecents");
             }
 
diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java
index d40dea2..55200b5 100644
--- a/services/core/java/com/android/server/wm/AppWarnings.java
+++ b/services/core/java/com/android/server/wm/AppWarnings.java
@@ -492,7 +492,7 @@
                 }
                 out.startTag(null, "package");
                 out.attribute(null, "name", pkg);
-                out.attribute(null, "flags", Integer.toString(mode));
+                out.attributeInt(null, "flags", mode);
                 out.endTag(null, "package");
             }
 
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index 89c5f62..c22bd20 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -17,10 +17,10 @@
 package com.android.server.wm;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 
 import android.app.ActivityManager;
 import android.app.AppGlobals;
@@ -43,7 +43,6 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -113,14 +112,7 @@
                             if ("pkg".equals(tagName)) {
                                 String pkg = parser.getAttributeValue(null, "name");
                                 if (pkg != null) {
-                                    String mode = parser.getAttributeValue(null, "mode");
-                                    int modeInt = 0;
-                                    if (mode != null) {
-                                        try {
-                                            modeInt = Integer.parseInt(mode);
-                                        } catch (NumberFormatException e) {
-                                        }
-                                    }
+                                    int modeInt = parser.getAttributeInt(null, "mode", 0);
                                     mPackages.put(pkg, modeInt);
                                 }
                             }
@@ -321,7 +313,7 @@
 
             scheduleWrite();
 
-            final Task stack = mService.getTopDisplayFocusedStack();
+            final Task stack = mService.getTopDisplayFocusedRootTask();
             ActivityRecord starting = stack.restartPackage(packageName);
 
             // Tell all processes that loaded this package about the change.
@@ -396,7 +388,7 @@
                 }
                 out.startTag(null, "pkg");
                 out.attribute(null, "name", pkg);
-                out.attribute(null, "mode", Integer.toString(mode));
+                out.attributeInt(null, "mode", mode);
                 out.endTag(null, "pkg");
             }
 
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 60a62dc..36a1ef9 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -182,6 +182,11 @@
         // writing to proto (which has significant cost if we write a lot of empty configurations).
         mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration);
         mRequestedOverrideConfiguration.setTo(overrideConfiguration);
+        final Rect newBounds = mRequestedOverrideConfiguration.windowConfiguration.getBounds();
+        if (mHasOverrideConfiguration && providesMaxBounds()
+                && diffRequestedOverrideMaxBounds(newBounds) != BOUNDS_CHANGE_NONE) {
+            mRequestedOverrideConfiguration.windowConfiguration.setMaxBounds(newBounds);
+        }
         // Update full configuration of this container and all its children.
         final ConfigurationContainer parent = getParent();
         onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY);
@@ -341,9 +346,6 @@
 
         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
         mRequestsTmpConfig.windowConfiguration.setBounds(bounds);
-        if (overrideMaxBounds) {
-            mRequestsTmpConfig.windowConfiguration.setMaxBounds(bounds);
-        }
         onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
 
         return boundsChange;
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 48e0300..a4ac16f 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -33,7 +33,6 @@
 import android.annotation.Nullable;
 import android.content.res.Configuration;
 import android.graphics.Rect;
-import android.os.IBinder;
 import android.util.proto.ProtoOutputStream;
 import android.window.DisplayAreaInfo;
 import android.window.IDisplayAreaOrganizer;
@@ -151,12 +150,11 @@
     }
 
     @Override
-    boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
-            WindowContainer requestingContainer) {
+    boolean onDescendantOrientationChanged(WindowContainer requestingContainer) {
         // If this is set to ignore the orientation request, we don't propagate descendant
         // orientation request.
         return !mIgnoreOrientationRequest
-                && super.onDescendantOrientationChanged(freezeDisplayToken, requestingContainer);
+                && super.onDescendantOrientationChanged(requestingContainer);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ccc85f8..1d2cd0a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -55,6 +55,8 @@
 import static android.view.WindowInsets.Type.ime;
 import static android.view.WindowInsets.Type.systemBars;
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
+import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
@@ -73,8 +75,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
-import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
 import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
@@ -93,7 +93,6 @@
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
-import static com.android.server.wm.DisplayContentProto.IME_POLICY;
 import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
 import static com.android.server.wm.DisplayContentProto.CURRENT_FOCUS;
 import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES;
@@ -105,6 +104,7 @@
 import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID;
 import static com.android.server.wm.DisplayContentProto.ID;
 import static com.android.server.wm.DisplayContentProto.IME_INSETS_SOURCE_PROVIDER;
+import static com.android.server.wm.DisplayContentProto.IME_POLICY;
 import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_CONTROL_TARGET;
 import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_INPUT_TARGET;
 import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_TARGET;
@@ -559,6 +559,9 @@
      */
     InsetsControlTarget mInputMethodControlTarget;
 
+    /** The surface parent of the IME container. */
+    private SurfaceControl mInputMethodSurfaceParent;
+
     /** If true hold off on modifying the animation layer of mInputMethodTarget */
     boolean mInputMethodTargetWaitingAnim;
 
@@ -1300,10 +1303,9 @@
     }
 
     @Override
-    boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
-            WindowContainer requestingContainer) {
+    boolean onDescendantOrientationChanged(WindowContainer requestingContainer) {
         final Configuration config = updateOrientation(
-                getRequestedOverrideConfiguration(), freezeDisplayToken, false /* forceUpdate */);
+                getRequestedOverrideConfiguration(), requestingContainer, false /* forceUpdate */);
         // If display rotation class tells us that it doesn't consider app requested orientation,
         // this display won't rotate just because of an app changes its requested orientation. Thus
         // it indicates that this display chooses not to handle this request.
@@ -1318,7 +1320,7 @@
                     false /* deferResume */, null /* result */);
             activityRecord.frozenBeforeDestroy = true;
             if (!kept) {
-                mRootWindowContainer.resumeFocusedStacksTopActivities();
+                mRootWindowContainer.resumeFocusedTasksTopActivities();
             }
         } else {
             // We have a new configuration to push so we need to update ATMS for now.
@@ -1355,11 +1357,11 @@
      * @param currentConfig The current requested override configuration (it is usually set from
      *                      the last {@link #sendNewConfiguration}) of the display. It is used to
      *                      check if the configuration container has the latest state.
-     * @param freezeDisplayToken Freeze the app window token if the orientation is changed.
+     * @param freezeDisplayWindow Freeze the app window if the orientation is changed.
      * @param forceUpdate See {@link DisplayRotation#updateRotationUnchecked(boolean)}
      */
-    Configuration updateOrientation(Configuration currentConfig, IBinder freezeDisplayToken,
-            boolean forceUpdate) {
+    Configuration updateOrientation(Configuration currentConfig,
+            WindowContainer freezeDisplayWindow, boolean forceUpdate) {
         if (!mDisplayReady) {
             return null;
         }
@@ -1368,9 +1370,9 @@
         if (updateOrientation(forceUpdate)) {
             // If we changed the orientation but mOrientationChangeComplete is already true,
             // we used seamless rotation, and we don't need to freeze the screen.
-            if (freezeDisplayToken != null && !mWmService.mRoot.mOrientationChangeComplete) {
-                final ActivityRecord activity = getActivityRecord(freezeDisplayToken);
-                if (activity != null) {
+            if (freezeDisplayWindow != null && !mWmService.mRoot.mOrientationChangeComplete) {
+                final ActivityRecord activity = freezeDisplayWindow.asActivityRecord();
+                if (activity != null && activity.mayFreezeScreenLocked()) {
                     activity.startFreezingScreen();
                 }
             }
@@ -1677,6 +1679,28 @@
         }
     }
 
+    void notifyInsetsChanged(Consumer<WindowState> dispatchInsetsChanged) {
+        if (mFixedRotationLaunchingApp != null) {
+            // The insets state of fixed rotation app is a rotated copy. Make sure the visibilities
+            // of insets sources are consistent with the latest state.
+            final InsetsState rotatedState =
+                    mFixedRotationLaunchingApp.getFixedRotationTransformInsetsState();
+            if (rotatedState != null) {
+                final InsetsState state = mInsetsStateController.getRawInsetsState();
+                for (int i = 0; i < InsetsState.SIZE; i++) {
+                    final InsetsSource source = state.peekSource(i);
+                    if (source != null) {
+                        rotatedState.setSourceVisible(i, source.isVisible());
+                    }
+                }
+            }
+        }
+        forAllWindows(dispatchInsetsChanged, true /* traverseTopToBottom */);
+        if (mRemoteInsetsControlTarget != null) {
+            mRemoteInsetsControlTarget.notifyInsetsChanged();
+        }
+    }
+
     /**
      * Update rotation of the display.
      *
@@ -2219,26 +2243,26 @@
      * activity type. Null is no compatible stack on the display.
      */
     @Nullable
-    Task getStack(int windowingMode, int activityType) {
+    Task getRootTask(int windowingMode, int activityType) {
         return getItemFromTaskDisplayAreas(taskDisplayArea ->
-                taskDisplayArea.getStack(windowingMode, activityType));
+                taskDisplayArea.getRootTask(windowingMode, activityType));
     }
 
     @Nullable
-    Task getStack(int rootTaskId) {
+    Task getRootTask(int rootTaskId) {
         return getItemFromTaskDisplayAreas(taskDisplayArea ->
-                        taskDisplayArea.getStack(rootTaskId));
+                        taskDisplayArea.getRootTask(rootTaskId));
     }
 
-    protected int getStackCount() {
+    protected int getRootTaskCount() {
         return reduceOnAllTaskDisplayAreas((taskDisplayArea, count) ->
-                count + taskDisplayArea.getStackCount(), 0 /* initValue */);
+                count + taskDisplayArea.getRootTaskCount(), 0 /* initValue */);
     }
 
     @VisibleForTesting
     @Nullable
-    Task getTopStack() {
-        return getItemFromTaskDisplayAreas(TaskDisplayArea::getTopStack);
+    Task getTopRootTask() {
+        return getItemFromTaskDisplayAreas(TaskDisplayArea::getTopRootTask);
     }
 
     /**
@@ -2902,7 +2926,7 @@
             mClosingApps.valueAt(i).writeIdentifierToProto(proto, CLOSING_APPS);
         }
 
-        final Task focusedStack = getFocusedStack();
+        final Task focusedStack = getFocusedRootTask();
         if (focusedStack != null) {
             proto.write(FOCUSED_ROOT_TASK_ID, focusedStack.getRootTaskId());
             final ActivityRecord focusedActivity = focusedStack.getDisplayArea()
@@ -2946,7 +2970,7 @@
     public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         super.dump(pw, prefix, dumpAll);
         pw.print(prefix);
-        pw.println("Display: mDisplayId=" + mDisplayId + " stacks=" + getStackCount());
+        pw.println("Display: mDisplayId=" + mDisplayId + " stacks=" + getRootTaskCount());
         final String subPrefix = "  " + prefix;
         pw.print(subPrefix); pw.print("init="); pw.print(mInitialDisplayWidth); pw.print("x");
         pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity);
@@ -3044,13 +3068,13 @@
             pw.println(prefix + "splitScreenPrimaryStack=" + splitScreenPrimaryStack.getName());
         }
         // TODO: Support recents on non-default task containers
-        final Task recentsStack = getDefaultTaskDisplayArea().getStack(
+        final Task recentsStack = getDefaultTaskDisplayArea().getRootTask(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
         if (recentsStack != null) {
             pw.println(prefix + "recentsStack=" + recentsStack.getName());
         }
         final Task dreamStack =
-                getStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_DREAM);
+                getRootTask(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_DREAM);
         if (dreamStack != null) {
             pw.println(prefix + "dreamStack=" + dreamStack.getName());
         }
@@ -3610,7 +3634,9 @@
         ProtoLog.i(WM_DEBUG_IME, "setInputMethodTarget %s", target);
         mInputMethodTarget = target;
         mInputMethodTargetWaitingAnim = targetWaitingAnim;
-        assignWindowLayers(true /* setLayoutNeeded */);
+
+        // 1. Reparent the IME container window to the target root DA to get the correct bounds and
+        // config. (Only happens when the target window is in a different root DA)
         if (target != null) {
             RootDisplayArea targetRoot = target.getRootDisplayArea();
             if (targetRoot != null) {
@@ -3619,7 +3645,13 @@
                 targetRoot.placeImeContainer(mImeWindowsContainers);
             }
         }
+        // 2. Reparent the IME container surface to either the input target app, or the IME window
+        // parent.
         updateImeParent();
+        // 3. Assign window layers based on the IME surface parent to make sure it is on top of the
+        // app.
+        assignWindowLayers(true /* setLayoutNeeded */);
+        // 4. Update the IME control target to apply any inset change and animation.
         updateImeControlTarget();
     }
 
@@ -3649,7 +3681,8 @@
 
     void updateImeParent() {
         final SurfaceControl newParent = computeImeParent();
-        if (newParent != null) {
+        if (newParent != null && newParent != mInputMethodSurfaceParent) {
+            mInputMethodSurfaceParent = newParent;
             getPendingTransaction().reparent(mImeWindowsContainers.mSurfaceControl, newParent);
             scheduleAnimation();
         }
@@ -4426,18 +4459,15 @@
         //
         // In the case of split-screen windowing mode, we need to elevate the IME above the
         // docked divider while keeping the app itself below the docked divider, so instead
-        // we use relative layering of the IME targets child windows, and place the IME in
-        // the non-app layer (see {@link AboveAppWindowContainers#assignChildLayers}).
+        // we will put the docked divider below the IME. @see #assignRelativeLayerForImeTargetChild
         //
         // In the case the IME target is animating, the animation Z order may be different
         // than the WindowContainer Z order, so it's difficult to be sure we have the correct
-        // IME target. In this case we just layer the IME over all transitions by placing it
-        // in the above applications layer.
+        // IME target. In this case we just layer the IME over its parent surface.
         //
-        // In the case where we have no IME target we assign it where its base layer would
-        // place it in the AboveAppWindowContainers.
+        // In the case where we have no IME target we let its window parent to place it.
         //
-        // Keep IME window in mAboveAppWindowsContainers as long as app's starting window
+        // Keep IME window in surface parent as long as app's starting window
         // exists so it get's layered above the starting window.
         if (imeTarget != null && !(imeTarget.mActivityRecord != null
                 && imeTarget.mActivityRecord.hasStartingWindow()) && (
@@ -4448,6 +4478,11 @@
                     // TODO: We need to use an extra level on the app surface to ensure
                     // this is always above SurfaceView but always below attached window.
                     1);
+        } else if (mInputMethodSurfaceParent != null) {
+            // The IME surface parent may not be its window parent's surface
+            // (@see #computeImeParent), so set relative layer here instead of letting the window
+            // parent to assign layer.
+            mImeWindowsContainers.assignRelativeLayer(t, mInputMethodSurfaceParent, 1);
         }
         super.assignChildLayers(t);
     }
@@ -4481,9 +4516,9 @@
         }
     }
 
-    void assignStackOrdering() {
+    void assignRootTaskOrdering() {
         forAllTaskDisplayAreas(taskDisplayArea -> {
-            taskDisplayArea.assignStackOrdering(getPendingTransaction());
+            taskDisplayArea.assignRootTaskOrdering(getPendingTransaction());
         });
     }
 
@@ -5048,7 +5083,7 @@
         mWmService.requestTraversal();
     }
 
-    static boolean alwaysCreateStack(int windowingMode, int activityType) {
+    static boolean alwaysCreateRootTask(int windowingMode, int activityType) {
         // Always create a stack for fullscreen, freeform, and split-screen-secondary windowing
         // modes so that we can manage visual ordering and return types correctly.
         return activityType == ACTIVITY_TYPE_STANDARD
@@ -5062,13 +5097,13 @@
     static boolean canReuseExistingTask(int windowingMode, int activityType) {
         // Existing Tasks can be reused if a new stack will be created anyway, or for the Dream -
         // because there can only ever be one DreamActivity.
-        return alwaysCreateStack(windowingMode, activityType)
+        return alwaysCreateRootTask(windowingMode, activityType)
                 || activityType == ACTIVITY_TYPE_DREAM;
     }
 
     @Nullable
-    Task getFocusedStack() {
-        return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedStack);
+    Task getFocusedRootTask() {
+        return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedRootTask);
     }
 
     /**
@@ -5197,7 +5232,7 @@
         mCurrentOverrideConfigurationChanges = currOverrideConfig.diff(overrideConfiguration);
         super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
         mCurrentOverrideConfigurationChanges = 0;
-        mWmService.setNewDisplayOverrideConfiguration(overrideConfiguration, this);
+        mWmService.setNewDisplayOverrideConfiguration(currOverrideConfig, this);
         mAtmService.addWindowLayoutReasons(
                 ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED);
     }
@@ -5305,19 +5340,19 @@
 
         // Check if all task display areas have only the empty home stacks left.
         boolean hasNonEmptyHomeStack = forAllTaskDisplayAreas(taskDisplayArea -> {
-            if (taskDisplayArea.getStackCount() != 1) {
+            if (taskDisplayArea.getRootTaskCount() != 1) {
                 return true;
             }
-            final Task stack = taskDisplayArea.getStackAt(0);
+            final Task stack = taskDisplayArea.getRootTaskAt(0);
             return !stack.isActivityTypeHome() || stack.hasChild();
         });
         if (!hasNonEmptyHomeStack) {
             // Release this display if only empty home stack(s) are left. This display will be
             // released along with the stack(s) removal.
             forAllTaskDisplayAreas(taskDisplayArea -> {
-                taskDisplayArea.getStackAt(0).removeIfPossible();
+                taskDisplayArea.getRootTaskAt(0).removeIfPossible();
             });
-        } else if (getTopStack() == null) {
+        } else if (getTopRootTask() == null) {
             removeIfPossible();
             mRootWindowContainer.mTaskSupervisor
                     .getKeyguardController().onDisplayRemoved(mDisplayId);
@@ -5344,7 +5379,7 @@
     }
 
     boolean shouldSleep() {
-        return (getStackCount() == 0 || !mAllSleepTokens.isEmpty())
+        return (getRootTaskCount() == 0 || !mAllSleepTokens.isEmpty())
                 && (mAtmService.mRunningVoice == null);
     }
 
@@ -5689,4 +5724,8 @@
         }
         return count;
     }
+
+    MagnificationSpec getMagnificationSpec() {
+        return mMagnificationSpec;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 826b725..2ddd001 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2046,7 +2046,7 @@
                 // requests to hide the status bar.  Not sure if there is another way that to be the
                 // case though.
                 if (!topIsFullscreen || mDisplayContent.getDefaultTaskDisplayArea()
-                        .isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
+                        .isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
                     topAppHidesStatusBar = false;
                 }
             }
@@ -2830,9 +2830,9 @@
 
     private int updateSystemBarsLw(WindowState win, int disableFlags) {
         final boolean dockedStackVisible = mDisplayContent.getDefaultTaskDisplayArea()
-                .isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+                .isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
         final boolean freeformStackVisible = mDisplayContent.getDefaultTaskDisplayArea()
-                .isStackVisible(WINDOWING_MODE_FREEFORM);
+                .isRootTaskVisible(WINDOWING_MODE_FREEFORM);
         final boolean resizing = mDisplayContent.getDockedDividerController().isResizing();
 
         // We need to force system bars when the docked stack is visible, when the freeform stack
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
index 78f1426..aa603ab 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
@@ -335,36 +335,31 @@
         return fileData;
     }
 
-    private static int getIntAttribute(XmlPullParser parser, String name, int defaultValue) {
-        try {
-            final String str = parser.getAttributeValue(null, name);
-            return str != null ? Integer.parseInt(str) : defaultValue;
-        } catch (NumberFormatException e) {
-            Slog.w(TAG, "Failed to parse display window settings attribute: " + name, e);
-            return defaultValue;
-        }
+    private static int getIntAttribute(TypedXmlPullParser parser, String name, int defaultValue) {
+        return parser.getAttributeInt(null, name, defaultValue);
     }
 
     @Nullable
-    private static Integer getIntegerAttribute(XmlPullParser parser, String name,
+    private static Integer getIntegerAttribute(TypedXmlPullParser parser, String name,
             @Nullable Integer defaultValue) {
         try {
-            final String str = parser.getAttributeValue(null, name);
-            return str != null ? Integer.valueOf(str) : defaultValue;
-        } catch (NumberFormatException e) {
-            Slog.w(TAG, "Failed to parse display window settings attribute: " + name, e);
+            return parser.getAttributeInt(null, name);
+        } catch (Exception ignored) {
             return defaultValue;
         }
     }
 
     @Nullable
-    private static Boolean getBooleanAttribute(XmlPullParser parser, String name,
+    private static Boolean getBooleanAttribute(TypedXmlPullParser parser, String name,
             @Nullable Boolean defaultValue) {
-        final String str = parser.getAttributeValue(null, name);
-        return str != null ? Boolean.valueOf(str) : defaultValue;
+        try {
+            return parser.getAttributeBoolean(null, name);
+        } catch (Exception ignored) {
+            return defaultValue;
+        }
     }
 
-    private static void readDisplay(XmlPullParser parser, FileData fileData)
+    private static void readDisplay(TypedXmlPullParser parser, FileData fileData)
             throws NumberFormatException, XmlPullParserException, IOException {
         String name = parser.getAttributeValue(null, "name");
         if (name != null) {
@@ -407,7 +402,7 @@
         XmlUtils.skipCurrentTag(parser);
     }
 
-    private static void readConfig(XmlPullParser parser, FileData fileData)
+    private static void readConfig(TypedXmlPullParser parser, FileData fileData)
             throws NumberFormatException,
             XmlPullParserException, IOException {
         fileData.mIdentifierType = getIntAttribute(parser, "identifier",
@@ -432,8 +427,7 @@
             out.startTag(null, "display-settings");
 
             out.startTag(null, "config");
-            out.attribute(null, "identifier",
-                    Integer.toString(data.mIdentifierType));
+            out.attributeInt(null, "identifier", data.mIdentifierType);
             out.endTag(null, "config");
 
             for (Map.Entry<String, SettingsEntry> entry
@@ -447,8 +441,7 @@
                 out.startTag(null, "display");
                 out.attribute(null, "name", displayIdentifier);
                 if (settingsEntry.mWindowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
-                    out.attribute(null, "windowingMode",
-                            Integer.toString(settingsEntry.mWindowingMode));
+                    out.attributeInt(null, "windowingMode", settingsEntry.mWindowingMode);
                 }
                 if (settingsEntry.mUserRotationMode != null) {
                     out.attribute(null, "userRotationMode",
@@ -459,22 +452,18 @@
                             settingsEntry.mUserRotation.toString());
                 }
                 if (settingsEntry.mForcedWidth != 0 && settingsEntry.mForcedHeight != 0) {
-                    out.attribute(null, "forcedWidth",
-                            Integer.toString(settingsEntry.mForcedWidth));
-                    out.attribute(null, "forcedHeight",
-                            Integer.toString(settingsEntry.mForcedHeight));
+                    out.attributeInt(null, "forcedWidth", settingsEntry.mForcedWidth);
+                    out.attributeInt(null, "forcedHeight", settingsEntry.mForcedHeight);
                 }
                 if (settingsEntry.mForcedDensity != 0) {
-                    out.attribute(null, "forcedDensity",
-                            Integer.toString(settingsEntry.mForcedDensity));
+                    out.attributeInt(null, "forcedDensity", settingsEntry.mForcedDensity);
                 }
                 if (settingsEntry.mForcedScalingMode != null) {
                     out.attribute(null, "forcedScalingMode",
                             settingsEntry.mForcedScalingMode.toString());
                 }
                 if (settingsEntry.mRemoveContentMode != REMOVE_CONTENT_MODE_UNDEFINED) {
-                    out.attribute(null, "removeContentMode",
-                            Integer.toString(settingsEntry.mRemoveContentMode));
+                    out.attributeInt(null, "removeContentMode", settingsEntry.mRemoveContentMode);
                 }
                 if (settingsEntry.mShouldShowWithInsecureKeyguard != null) {
                     out.attribute(null, "shouldShowWithInsecureKeyguard",
diff --git a/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java
index cdc14cd..92baadf 100644
--- a/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java
+++ b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java
@@ -27,7 +27,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
-import com.android.server.wm.utils.DeviceConfigInterface;
+import com.android.server.utils.DeviceConfigInterface;
 
 import java.io.PrintWriter;
 
diff --git a/services/core/java/com/android/server/wm/ImpressionAttestationController.java b/services/core/java/com/android/server/wm/ImpressionAttestationController.java
index 4793e1b..b0afc57 100644
--- a/services/core/java/com/android/server/wm/ImpressionAttestationController.java
+++ b/services/core/java/com/android/server/wm/ImpressionAttestationController.java
@@ -18,6 +18,9 @@
 
 import static android.service.attestation.ImpressionAttestationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
 
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -29,7 +32,9 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
+import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.hardware.HardwareBuffer;
 import android.os.Binder;
 import android.os.Bundle;
@@ -43,6 +48,7 @@
 import android.service.attestation.ImpressionAttestationService;
 import android.service.attestation.ImpressionToken;
 import android.util.Slog;
+import android.view.MagnificationSpec;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -59,7 +65,8 @@
  * blocking calls into another service.
  */
 public class ImpressionAttestationController {
-    private static final String TAG = "ImpressionAttestationController";
+    private static final String TAG =
+            TAG_WITH_CLASS_NAME ? "ImpressionAttestationController" : TAG_WM;
     private static final boolean DEBUG = false;
 
     private final Object mServiceConnectionLock = new Object();
@@ -81,6 +88,10 @@
 
     private final String mSalt;
 
+    private final float[] mTmpFloat9 = new float[9];
+    private final Matrix mTmpMatrix = new Matrix();
+    private final RectF mTmpRectF = new RectF();
+
     private interface Command {
         void run(IImpressionAttestationService service) throws RemoteException;
     }
@@ -151,6 +162,79 @@
     }
 
     /**
+     * Calculate the bounds to take the screenshot when generating the impression token. This takes
+     * into account window transform, magnification, and display bounds.
+     *
+     * Call while holding {@link WindowManagerService#mGlobalLock}
+     *
+     * @param win Window that the impression token is generated for.
+     * @param boundsInWindow The bounds passed in about where in the window to take the screenshot.
+     * @param outBounds The result of the calculated bounds
+     */
+    void calculateImpressionTokenBoundsLocked(WindowState win, Rect boundsInWindow,
+            Rect outBounds) {
+        if (DEBUG) {
+            Slog.d(TAG, "calculateImpressionTokenBounds: boundsInWindow=" + boundsInWindow);
+        }
+        outBounds.set(boundsInWindow);
+
+        DisplayContent displayContent = win.getDisplayContent();
+        if (displayContent == null) {
+            return;
+        }
+
+        // Intersect boundsInWindow with the window to make sure it's not outside the window
+        // requesting the token. Offset the window bounds to 0,0 since the boundsInWindow are
+        // offset from the window location, not display.
+        final Rect windowBounds = new Rect();
+        win.getBounds(windowBounds);
+        windowBounds.offsetTo(0, 0);
+        outBounds.intersectUnchecked(windowBounds);
+
+        if (DEBUG) {
+            Slog.d(TAG, "calculateImpressionTokenBounds: boundsIntersectWindow=" + outBounds);
+        }
+
+        if (outBounds.isEmpty()) {
+            return;
+        }
+
+        // Transform the bounds using the window transform in case there's a scale or offset.
+        // This allows the bounds to be in display space.
+        win.getTransformationMatrix(mTmpFloat9, mTmpMatrix);
+        mTmpRectF.set(outBounds);
+        mTmpMatrix.mapRect(mTmpRectF, mTmpRectF);
+        outBounds.set((int) mTmpRectF.left, (int) mTmpRectF.top, (int) mTmpRectF.right,
+                (int) mTmpRectF.bottom);
+        if (DEBUG) {
+            Slog.d(TAG, "calculateImpressionTokenBounds: boundsInDisplay=" + outBounds);
+        }
+
+        // Apply the magnification spec values to the bounds since the content could be magnified
+        final MagnificationSpec magSpec = displayContent.getMagnificationSpec();
+        if (magSpec != null) {
+            outBounds.scale(magSpec.scale);
+            outBounds.offset((int) magSpec.offsetX, (int) magSpec.offsetY);
+        }
+
+        if (DEBUG) {
+            Slog.d(TAG, "calculateImpressionTokenBounds: boundsWithMagnification=" + outBounds);
+        }
+
+        if (outBounds.isEmpty()) {
+            return;
+        }
+
+        // Intersect with the display bounds since it shouldn't take a screenshot of content
+        // outside the display since it's not visible to the user.
+        final Rect displayBounds = displayContent.getBounds();
+        outBounds.intersectUnchecked(displayBounds);
+        if (DEBUG) {
+            Slog.d(TAG, "calculateImpressionTokenBounds: finalBounds=" + outBounds);
+        }
+    }
+
+    /**
      * Run a command, starting the service connection if necessary.
      */
     private void connectAndRun(@NonNull Command command) {
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index b49d83d..25d779f 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -40,6 +40,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
 import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
@@ -655,6 +656,7 @@
                 || type == TYPE_SECURE_SYSTEM_OVERLAY
                 || type == TYPE_DOCK_DIVIDER
                 || type == TYPE_ACCESSIBILITY_OVERLAY
-                || type == TYPE_INPUT_CONSUMER;
+                || type == TYPE_INPUT_CONSUMER
+                || type == TYPE_VOICE_INTERACTION;
     }
 }
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index d308766..ee150c3 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -392,9 +392,9 @@
 
     private boolean forceShowsSystemBarsForWindowingMode() {
         final boolean isDockedStackVisible = mDisplayContent.getDefaultTaskDisplayArea()
-                .isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+                .isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
         final boolean isFreeformStackVisible = mDisplayContent.getDefaultTaskDisplayArea()
-                .isStackVisible(WINDOWING_MODE_FREEFORM);
+                .isRootTaskVisible(WINDOWING_MODE_FREEFORM);
         final boolean isResizing = mDisplayContent.getDockedDividerController().isResizing();
 
         // We need to force system bars when the docked stack is visible, when the freeform stack
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 9d70fd7..752d6b4 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -496,10 +496,7 @@
     }
 
     void notifyInsetsChanged() {
-        mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */);
-        if (mDisplayContent.mRemoteInsetsControlTarget != null) {
-            mDisplayContent.mRemoteInsetsControlTarget.notifyInsetsChanged();
-        }
+        mDisplayContent.notifyInsetsChanged(mDispatchInsetsChanged);
     }
 
     void dump(String prefix, PrintWriter pw) {
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index ebd91a0..e45310a 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -211,7 +211,7 @@
             updateKeyguardSleepToken();
 
             // Some stack visibility might change (e.g. docked stack)
-            mRootWindowContainer.resumeFocusedStacksTopActivities();
+            mRootWindowContainer.resumeFocusedTasksTopActivities();
             mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
             mRootWindowContainer.addStartingWindowsForVisibleActivities();
             mWindowManager.executeAppTransition();
@@ -595,8 +595,8 @@
         @Nullable
         private Task getRootTaskForControllingOccluding(DisplayContent display) {
             return display.getItemFromTaskDisplayAreas(taskDisplayArea -> {
-                for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
-                    final Task task = taskDisplayArea.getStackAt(sNdx);
+                for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) {
+                    final Task task = taskDisplayArea.getRootTaskAt(sNdx);
                     if (task != null && task.isFocusableAndVisible()
                             && !task.inPinnedWindowingMode()) {
                         return task;
diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java
index b6b172e..8745e95 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsController.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsController.java
@@ -143,7 +143,7 @@
         try {
             if (mTmpParams.mPreferredTaskDisplayArea != null
                     && task.getDisplayArea() != mTmpParams.mPreferredTaskDisplayArea) {
-                mService.mRootWindowContainer.moveStackToTaskDisplayArea(task.getRootTaskId(),
+                mService.mRootWindowContainer.moveRootTaskToTaskDisplayArea(task.getRootTaskId(),
                         mTmpParams.mPreferredTaskDisplayArea, true /* onTop */);
             }
 
diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
index 2f8cfdd..391e659 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
@@ -27,26 +27,24 @@
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 import android.view.DisplayInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.server.LocalServices;
 import com.android.server.pm.PackageList;
 import com.android.server.wm.LaunchParamsController.LaunchParams;
 
-import libcore.io.IoUtils;
-
 import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
 
-import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
-import java.io.FileReader;
 import java.io.IOException;
-import java.io.StringWriter;
+import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -195,12 +193,9 @@
                 continue;
             }
 
-            BufferedReader reader = null;
-            try {
-                reader = new BufferedReader(new FileReader(paramsFile));
+            try (InputStream in = new FileInputStream(paramsFile)) {
                 final PersistableLaunchParams params = new PersistableLaunchParams();
-                final XmlPullParser parser = Xml.newPullParser();
-                parser.setInput(reader);
+                final TypedXmlPullParser parser = Xml.resolvePullParser(in);
                 int event;
                 while ((event = parser.next()) != XmlPullParser.END_DOCUMENT
                         && event != XmlPullParser.END_TAG) {
@@ -223,8 +218,6 @@
             } catch (Exception e) {
                 Slog.w(TAG, "Failed to restore launch params for " + name, e);
                 filesToDelete.add(paramsFile);
-            } finally {
-                IoUtils.closeQuietly(reader);
             }
         }
 
@@ -410,12 +403,11 @@
             mLaunchParams = launchParams;
         }
 
-        private StringWriter saveParamsToXml() {
-            final StringWriter writer = new StringWriter();
-            final XmlSerializer serializer = new FastXmlSerializer();
-
+        private byte[] saveParamsToXml() {
             try {
-                serializer.setOutput(writer);
+                final ByteArrayOutputStream os = new ByteArrayOutputStream();
+                final TypedXmlSerializer serializer = Xml.resolveSerializer(os);
+
                 serializer.startDocument(/* encoding */ null, /* standalone */ true);
                 serializer.startTag(null, TAG_LAUNCH_PARAMS);
 
@@ -425,7 +417,7 @@
                 serializer.endDocument();
                 serializer.flush();
 
-                return writer;
+                return os.toByteArray();
             } catch (IOException e) {
                 return null;
             }
@@ -433,7 +425,7 @@
 
         @Override
         public void process() {
-            final StringWriter writer = saveParamsToXml();
+            final byte[] data = saveParamsToXml();
 
             final File launchParamFolder = getLaunchParamFolder(mUserId);
             if (!launchParamFolder.isDirectory() && !launchParamFolder.mkdirs()) {
@@ -447,7 +439,7 @@
             FileOutputStream stream = null;
             try {
                 stream = atomicFile.startWrite();
-                stream.write(writer.toString().getBytes());
+                stream.write(data);
             } catch (Exception e) {
                 Slog.e(TAG, "Failed to write param file for " + mComponentName, e);
                 if (stream != null) {
@@ -513,17 +505,16 @@
          */
         long mTimestamp;
 
-        void saveToXml(XmlSerializer serializer) throws IOException {
+        void saveToXml(TypedXmlSerializer serializer) throws IOException {
             serializer.attribute(null, ATTR_DISPLAY_UNIQUE_ID, mDisplayUniqueId);
-            serializer.attribute(null, ATTR_WINDOWING_MODE,
-                    Integer.toString(mWindowingMode));
+            serializer.attributeInt(null, ATTR_WINDOWING_MODE, mWindowingMode);
             serializer.attribute(null, ATTR_BOUNDS, mBounds.flattenToString());
             if (mWindowLayoutAffinity != null) {
                 serializer.attribute(null, ATTR_WINDOW_LAYOUT_AFFINITY, mWindowLayoutAffinity);
             }
         }
 
-        void restore(File xmlFile, XmlPullParser parser) {
+        void restore(File xmlFile, TypedXmlPullParser parser) {
             for (int i = 0; i < parser.getAttributeCount(); ++i) {
                 final String attrValue = parser.getAttributeValue(i);
                 switch (parser.getAttributeName(i)) {
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index ee39787..4b3a434 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -504,7 +504,7 @@
             return;
         }
         task.performClearTaskLocked();
-        mSupervisor.mRootWindowContainer.resumeFocusedStacksTopActivities();
+        mSupervisor.mRootWindowContainer.resumeFocusedTasksTopActivities();
     }
 
     /**
@@ -640,7 +640,7 @@
         if (andResume) {
             mSupervisor.findTaskToMoveToFront(task, 0, null, reason,
                     lockTaskModeState != LOCK_TASK_MODE_NONE);
-            mSupervisor.mRootWindowContainer.resumeFocusedStacksTopActivities();
+            mSupervisor.mRootWindowContainer.resumeFocusedTasksTopActivities();
             final Task rootTask = task.getRootTask();
             if (rootTask != null) {
                 rootTask.mDisplayContent.executeAppTransition();
@@ -717,7 +717,7 @@
         }
 
         if (taskChanged) {
-            mSupervisor.mRootWindowContainer.resumeFocusedStacksTopActivities();
+            mSupervisor.mRootWindowContainer.resumeFocusedTasksTopActivities();
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index ba6b27a..5598937 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -36,13 +36,13 @@
 import static android.os.Process.SYSTEM_UID;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
-import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -220,7 +220,7 @@
                     final RootWindowContainer rac = mService.mRootWindowContainer;
                     final DisplayContent dc = rac.getDisplayContent(displayId).mDisplayContent;
                     if (dc.pointWithinAppWindow(x, y)) {
-                        final Task stack = mService.getTopDisplayFocusedStack();
+                        final Task stack = mService.getTopDisplayFocusedRootTask();
                         final Task topTask = stack != null ? stack.getTopMostTask() : null;
                         resetFreezeTaskListReordering(topTask);
                     }
@@ -328,7 +328,7 @@
     @VisibleForTesting
     void resetFreezeTaskListReorderingOnTimeout() {
         synchronized (mService.mGlobalLock) {
-            final Task focusedStack = mService.getTopDisplayFocusedStack();
+            final Task focusedStack = mService.getTopDisplayFocusedRootTask();
             final Task topTask = focusedStack != null ? focusedStack.getTopMostTask() : null;
             resetFreezeTaskListReordering(topTask);
         }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 823dc51..067c772 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -29,7 +29,7 @@
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP;
-import static com.android.server.wm.TaskDisplayArea.getStackAbove;
+import static com.android.server.wm.TaskDisplayArea.getRootTaskAbove;
 
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
@@ -45,13 +45,13 @@
 import com.android.internal.util.function.pooled.PooledPredicate;
 import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
 import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
+import com.android.server.wm.TaskDisplayArea.OnRootTaskOrderChangedListener;
 
 /**
  * Manages the recents animation, including the reordering of the stacks for the transition and
  * cleanup. See {@link com.android.server.wm.RecentsAnimationController}.
  */
-class RecentsAnimation implements RecentsAnimationCallbacks,
-        TaskDisplayArea.OnStackOrderChangedListener {
+class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChangedListener {
     private static final String TAG = RecentsAnimation.class.getSimpleName();
 
     private final ActivityTaskManagerService mService;
@@ -106,7 +106,7 @@
     void preloadRecentsActivity() {
         ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Preload recents with %s",
                 mTargetIntent);
-        Task targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED,
+        Task targetStack = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED,
                 mTargetActivityType);
         ActivityRecord targetActivity = getTargetActivity(targetStack);
         if (targetActivity != null) {
@@ -127,7 +127,7 @@
             // Create the activity record. Because the activity is invisible, this doesn't really
             // start the client.
             startRecentsActivityInBackground("preloadRecents");
-            targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED,
+            targetStack = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED,
                     mTargetActivityType);
             targetActivity = getTargetActivity(targetStack);
             if (targetActivity == null) {
@@ -165,12 +165,12 @@
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RecentsAnimation#startRecentsActivity");
 
         // If the activity is associated with the recents stack, then try and get that first
-        Task targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED,
+        Task targetStack = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED,
                 mTargetActivityType);
         ActivityRecord targetActivity = getTargetActivity(targetStack);
         final boolean hasExistingActivity = targetActivity != null;
         if (hasExistingActivity) {
-            mRestoreTargetBehindStack = getStackAbove(targetStack);
+            mRestoreTargetBehindStack = getRootTaskAbove(targetStack);
             if (mRestoreTargetBehindStack == null) {
                 notifyAnimationCancelBeforeStart(recentsAnimationRunner);
                 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
@@ -197,9 +197,9 @@
         try {
             if (hasExistingActivity) {
                 // Move the recents activity into place for the animation if it is not top most
-                mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(targetStack);
+                mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(targetStack);
                 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Moved stack=%s behind stack=%s",
-                        targetStack, getStackAbove(targetStack));
+                        targetStack, getRootTaskAbove(targetStack));
 
                 // If there are multiple tasks in the target stack (ie. the home stack, with 3p
                 // and default launchers coexisting), then move the task to the top as a part of
@@ -213,12 +213,12 @@
                 startRecentsActivityInBackground("startRecentsActivity_noTargetActivity");
 
                 // Move the recents activity into place for the animation
-                targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED,
+                targetStack = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED,
                         mTargetActivityType);
                 targetActivity = getTargetActivity(targetStack);
-                mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(targetStack);
+                mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(targetStack);
                 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Moved stack=%s behind stack=%s",
-                        targetStack, getStackAbove(targetStack));
+                        targetStack, getRootTaskAbove(targetStack));
 
                 mWindowManager.prepareAppTransitionNone();
                 mWindowManager.executeAppTransition();
@@ -257,7 +257,7 @@
                     START_TASK_TO_FRONT, targetActivity, options);
 
             // Register for stack order changes
-            mDefaultTaskDisplayArea.registerStackOrderChangedListener(this);
+            mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(this);
         } catch (Exception e) {
             Slog.e(TAG, "Failed to start recents activity", e);
             throw e;
@@ -275,7 +275,7 @@
                             mWindowManager.getRecentsAnimationController(), reorderMode);
 
             // Unregister for stack order changes
-            mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(this);
+            mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(this);
 
             final RecentsAnimationController controller =
                     mWindowManager.getRecentsAnimationController();
@@ -303,7 +303,7 @@
                 try {
                     mWindowManager.cleanupRecentsAnimation(reorderMode);
 
-                    final Task targetStack = mDefaultTaskDisplayArea.getStack(
+                    final Task targetStack = mDefaultTaskDisplayArea.getRootTask(
                             WINDOWING_MODE_UNDEFINED, mTargetActivityType);
                     // Prefer to use the original target activity instead of top activity because
                     // we may have moved another task to top (starting 3p launcher).
@@ -348,10 +348,10 @@
                     } else if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION){
                         // Restore the target stack to its previous position
                         final TaskDisplayArea taskDisplayArea = targetActivity.getDisplayArea();
-                        taskDisplayArea.moveStackBehindStack(targetStack,
+                        taskDisplayArea.moveRootTaskBehindRootTask(targetStack,
                                 mRestoreTargetBehindStack);
                         if (WM_DEBUG_RECENTS_ANIMATIONS.isLogToAny()) {
-                            final Task aboveTargetStack = getStackAbove(targetStack);
+                            final Task aboveTargetStack = getRootTaskAbove(targetStack);
                             if (mRestoreTargetBehindStack != null
                                     && aboveTargetStack != mRestoreTargetBehindStack) {
                                 ProtoLog.w(WM_DEBUG_RECENTS_ANIMATIONS,
@@ -378,7 +378,7 @@
 
                     mWindowManager.prepareAppTransitionNone();
                     mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, false);
-                    mService.mRootWindowContainer.resumeFocusedStacksTopActivities();
+                    mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
 
                     // No reason to wait for the pausing activity in this case, as the hiding of
                     // surfaces needs to be done immediately.
@@ -412,9 +412,9 @@
     }
 
     @Override
-    public void onStackOrderChanged(Task stack) {
-        ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "onStackOrderChanged(): stack=%s", stack);
-        if (mDefaultTaskDisplayArea.getIndexOf(stack) == -1 || !stack.shouldBeVisible(null)) {
+    public void onRootTaskOrderChanged(Task rootTask) {
+        ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "onStackOrderChanged(): stack=%s", rootTask);
+        if (mDefaultTaskDisplayArea.getIndexOf(rootTask) == -1 || !rootTask.shouldBeVisible(null)) {
             // The stack is not visible, so ignore this change
             return;
         }
@@ -428,8 +428,8 @@
         // cases:
         // 1) The next launching task is not being animated by the recents animation
         // 2) The next task is home activity. (i.e. pressing home key to back home in recents).
-        if ((!controller.isAnimatingTask(stack.getTopMostTask())
-                || controller.isTargetApp(stack.getTopNonFinishingActivity()))
+        if ((!controller.isAnimatingTask(rootTask.getTopMostTask())
+                || controller.isTargetApp(rootTask.getTopNonFinishingActivity()))
                 && controller.shouldDeferCancelUntilNextTransition()) {
             // Always prepare an app transition since we rely on the transition callbacks to cleanup
             mWindowManager.prepareAppTransitionNone();
@@ -468,8 +468,8 @@
      * @return The top stack that is not always-on-top.
      */
     private Task getTopNonAlwaysOnTopStack() {
-        for (int i = mDefaultTaskDisplayArea.getStackCount() - 1; i >= 0; i--) {
-            final Task s = mDefaultTaskDisplayArea.getStackAt(i);
+        for (int i = mDefaultTaskDisplayArea.getRootTaskCount() - 1; i >= 0; i--) {
+            final Task s = mDefaultTaskDisplayArea.getRootTaskAt(i);
             if (s.getWindowConfiguration().isAlwaysOnTop()) {
                 continue;
             }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index abee032..5da668c 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -398,7 +398,7 @@
         final ArrayList<Task> visibleTasks = mDisplayContent.getDefaultTaskDisplayArea()
                 .getVisibleTasks();
         final Task targetStack = mDisplayContent.getDefaultTaskDisplayArea()
-                .getStack(WINDOWING_MODE_UNDEFINED, targetActivityType);
+                .getRootTask(WINDOWING_MODE_UNDEFINED, targetActivityType);
         if (targetStack != null) {
             final PooledConsumer c = PooledLambda.obtainConsumer((t, outList) ->
 	            { if (!outList.contains(t)) outList.add(t); }, PooledLambda.__(Task.class),
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index 7bd5d03..17cb890 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -237,7 +237,7 @@
 
         while (!mPendingReparentActivities.isEmpty()) {
             final ActivityRecord r = mPendingReparentActivities.remove(0);
-            final boolean alwaysCreateTask = DisplayContent.alwaysCreateStack(windowingMode,
+            final boolean alwaysCreateTask = DisplayContent.alwaysCreateRootTask(windowingMode,
                     activityType);
             final Task task = alwaysCreateTask
                     ? taskDisplayArea.getBottomMostTask() : mTargetStack.getBottomMostTask();
@@ -251,7 +251,7 @@
             }
             if (targetTask == null) {
                 if (alwaysCreateTask) {
-                    targetTask = taskDisplayArea.getOrCreateStack(windowingMode,
+                    targetTask = taskDisplayArea.getOrCreateRootTask(windowingMode,
                             activityType, false /* onTop */);
                 } else {
                     targetTask = mTargetStack.reuseOrCreateTask(r.info, null /*intent*/,
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 7073548..497087a 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -256,8 +256,8 @@
 
     /** The current user */
     int mCurrentUser;
-    /** Stack id of the front stack when user switched, indexed by userId. */
-    SparseIntArray mUserStackInFront = new SparseIntArray(2);
+    /** Root task id of the front root task when user switched, indexed by userId. */
+    SparseIntArray mUserRootTaskInFront = new SparseIntArray(2);
 
     /**
      * A list of tokens that cause the top activity to be put to sleep.
@@ -296,7 +296,7 @@
                     c.recycle();
                 } finally {
                     mTaskSupervisor.endDeferResume();
-                    resumeFocusedStacksTopActivities();
+                    resumeFocusedTasksTopActivities();
                 }
             }
         }
@@ -1484,7 +1484,7 @@
             boolean fromHomeKey) {
         // Fallback to top focused display or default display if the displayId is invalid.
         if (displayId == INVALID_DISPLAY) {
-            final Task stack = getTopDisplayFocusedStack();
+            final Task stack = getTopDisplayFocusedRootTask();
             displayId = stack != null ? stack.getDisplayId() : DEFAULT_DISPLAY;
         }
 
@@ -1510,7 +1510,7 @@
             boolean allowInstrumenting, boolean fromHomeKey) {
         // Fallback to top focused display area if the provided one is invalid.
         if (taskDisplayArea == null) {
-            final Task stack = getTopDisplayFocusedStack();
+            final Task stack = getTopDisplayFocusedRootTask();
             taskDisplayArea = stack != null ? stack.getDisplayArea()
                     : getDefaultTaskDisplayArea();
         }
@@ -1677,7 +1677,7 @@
         // Only resume home activity if isn't finishing.
         if (r != null && !r.finishing) {
             r.moveFocusableActivityToTop(myReason);
-            return resumeFocusedStacksTopActivities(r.getRootTask(), prev, null);
+            return resumeFocusedTasksTopActivities(r.getRootTask(), prev, null);
         }
         return startHomeOnTaskDisplayArea(mCurrentUser, myReason, taskDisplayArea,
                 false /* allowInstrumenting */, false /* fromHomeKey */);
@@ -1804,10 +1804,7 @@
         Configuration config = null;
         if (displayContent != null) {
             config = displayContent.updateOrientation(
-                    getDisplayOverrideConfiguration(displayId),
-                    starting != null && starting.mayFreezeScreenLocked()
-                            ? starting.appToken : null,
-                    true /* forceUpdate */);
+                    getDisplayOverrideConfiguration(displayId), starting, true /* forceUpdate */);
         }
         // Visibilities may change so let the starting activity have a chance to report. Can't do it
         // when visibility is changed in each AppWindowToken because it may trigger wrong
@@ -1834,12 +1831,12 @@
      */
     List<IBinder> getTopVisibleActivities() {
         final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
-        final Task topFocusedStack = getTopDisplayFocusedStack();
+        final Task topFocusedStack = getTopDisplayFocusedRootTask();
         // Traverse all displays.
         forAllTaskDisplayAreas(taskDisplayArea -> {
             // Traverse all stacks on a display area.
-            for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
-                final Task stack = taskDisplayArea.getStackAt(sNdx);
+            for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) {
+                final Task stack = taskDisplayArea.getRootTaskAt(sNdx);
                 // Get top activity from a visible stack and add it to the list.
                 if (stack.shouldBeVisible(null /* starting */)) {
                     final ActivityRecord top = stack.getTopNonFinishingActivity();
@@ -1857,9 +1854,9 @@
     }
 
     @Nullable
-    Task getTopDisplayFocusedStack() {
+    Task getTopDisplayFocusedRootTask() {
         for (int i = getChildCount() - 1; i >= 0; --i) {
-            final Task focusedStack = getChildAt(i).getFocusedStack();
+            final Task focusedStack = getChildAt(i).getFocusedRootTask();
             if (focusedStack != null) {
                 return focusedStack;
             }
@@ -1869,7 +1866,7 @@
 
     @Nullable
     ActivityRecord getTopResumedActivity() {
-        final Task focusedStack = getTopDisplayFocusedStack();
+        final Task focusedStack = getTopDisplayFocusedRootTask();
         if (focusedStack == null) {
             return null;
         }
@@ -1882,8 +1879,8 @@
         return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedActivity);
     }
 
-    boolean isTopDisplayFocusedStack(Task stack) {
-        return stack != null && stack == getTopDisplayFocusedStack();
+    boolean isTopDisplayFocusedRootTask(Task task) {
+        return task != null && task == getTopDisplayFocusedRootTask();
     }
 
     void updatePreviousProcess(ActivityRecord r) {
@@ -1894,9 +1891,9 @@
         // previous app if this activity is being hosted by the process that is actually still the
         // foreground.
         WindowProcessController fgApp = reduceOnAllTaskDisplayAreas((taskDisplayArea, app) -> {
-            for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
-                final Task stack = taskDisplayArea.getStackAt(sNdx);
-                if (isTopDisplayFocusedStack(stack)) {
+            for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) {
+                final Task stack = taskDisplayArea.getRootTaskAt(sNdx);
+                if (isTopDisplayFocusedRootTask(stack)) {
                     final ActivityRecord resumedActivity = stack.getResumedActivity();
                     if (resumedActivity != null) {
                         app = resumedActivity.app;
@@ -1929,8 +1926,8 @@
                     return;
                 }
 
-                for (int taskNdx = displayArea.getStackCount() - 1; taskNdx >= 0; --taskNdx) {
-                    final Task rootTask = displayArea.getStackAt(taskNdx);
+                for (int taskNdx = displayArea.getRootTaskCount() - 1; taskNdx >= 0; --taskNdx) {
+                    final Task rootTask = displayArea.getRootTaskAt(taskNdx);
                     if (rootTask.getVisibility(null /*starting*/) == TASK_VISIBILITY_INVISIBLE) {
                         break;
                     }
@@ -2012,7 +2009,7 @@
     }
 
     boolean switchUser(int userId, UserState uss) {
-        final Task topFocusedStack = getTopDisplayFocusedStack();
+        final Task topFocusedStack = getTopDisplayFocusedRootTask();
         final int focusStackId = topFocusedStack != null
                 ? topFocusedStack.getRootTaskId() : INVALID_TASK_ID;
         // We dismiss the docked stack whenever we switch users.
@@ -2024,19 +2021,19 @@
         // appropriate.
         removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED);
 
-        mUserStackInFront.put(mCurrentUser, focusStackId);
+        mUserRootTaskInFront.put(mCurrentUser, focusStackId);
         mCurrentUser = userId;
 
         mTaskSupervisor.mStartingUsers.add(uss);
         forAllTaskDisplayAreas(taskDisplayArea -> {
-            for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
-                final Task stack = taskDisplayArea.getStackAt(sNdx);
+            for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) {
+                final Task stack = taskDisplayArea.getRootTaskAt(sNdx);
                 stack.switchUser(userId);
             }
         });
 
-        final int restoreStackId = mUserStackInFront.get(userId);
-        Task stack = getStack(restoreStackId);
+        final int restoreStackId = mUserRootTaskInFront.get(userId);
+        Task stack = getRootTask(restoreStackId);
         if (stack == null) {
             stack = getDefaultTaskDisplayArea().getOrCreateRootHomeTask();
         }
@@ -2051,20 +2048,20 @@
     }
 
     void removeUser(int userId) {
-        mUserStackInFront.delete(userId);
+        mUserRootTaskInFront.delete(userId);
     }
 
     /**
-     * Update the last used stack id for non-current user (current user's last
-     * used stack is the focused stack)
+     * Update the last used root task id for non-current user (current user's last
+     * used root task is the focused root task)
      */
-    void updateUserStack(int userId, Task stack) {
+    void updateUserRootTask(int userId, Task rootTask) {
         if (userId != mCurrentUser) {
-            if (stack == null) {
-                stack = getDefaultTaskDisplayArea().getOrCreateRootHomeTask();
+            if (rootTask == null) {
+                rootTask = getDefaultTaskDisplayArea().getOrCreateRootHomeTask();
             }
 
-            mUserStackInFront.put(userId, stack.getRootTaskId());
+            mUserRootTaskInFront.put(userId, rootTask.getRootTaskId());
         }
     }
 
@@ -2075,8 +2072,9 @@
      * @param taskDisplayArea The task display area to move stack to.
      * @param onTop           Indicates whether container should be place on top or on bottom.
      */
-    void moveStackToTaskDisplayArea(int stackId, TaskDisplayArea taskDisplayArea, boolean onTop) {
-        final Task stack = getStack(stackId);
+    void moveRootTaskToTaskDisplayArea(int stackId, TaskDisplayArea taskDisplayArea,
+            boolean onTop) {
+        final Task stack = getRootTask(stackId);
         if (stack == null) {
             throw new IllegalArgumentException("moveStackToTaskDisplayArea: Unknown stackId="
                     + stackId);
@@ -2104,22 +2102,23 @@
     /**
      * Move stack with all its existing content to specified display.
      *
-     * @param stackId   Id of stack to move.
+     * @param rootTaskId   Id of stack to move.
      * @param displayId Id of display to move stack to.
      * @param onTop     Indicates whether container should be place on top or on bottom.
      */
-    void moveStackToDisplay(int stackId, int displayId, boolean onTop) {
+    void moveRootTaskToDisplay(int rootTaskId, int displayId, boolean onTop) {
         final DisplayContent displayContent = getDisplayContentOrCreate(displayId);
         if (displayContent == null) {
             throw new IllegalArgumentException("moveStackToDisplay: Unknown displayId="
                     + displayId);
         }
 
-        moveStackToTaskDisplayArea(stackId, displayContent.getDefaultTaskDisplayArea(), onTop);
+        moveRootTaskToTaskDisplayArea(rootTaskId, displayContent.getDefaultTaskDisplayArea(),
+                onTop);
     }
 
-    boolean moveTopStackActivityToPinnedRootTask(int rootTaskId) {
-        final Task rootTask = getStack(rootTaskId);
+    boolean moveTopRootTaskActivityToPinnedRootTask(int rootTaskId) {
+        final Task rootTask = getRootTask(rootTaskId);
         if (rootTask == null) {
             throw new IllegalArgumentException(
                     "moveTopStackActivityToPinnedRootTask: Unknown rootTaskId=" + rootTaskId);
@@ -2138,11 +2137,11 @@
             return false;
         }
 
-        moveActivityToPinnedStack(r, "moveTopStackActivityToPinnedRootTask");
+        moveActivityToPinnedRootTask(r, "moveTopStackActivityToPinnedRootTask");
         return true;
     }
 
-    void moveActivityToPinnedStack(ActivityRecord r, String reason) {
+    void moveActivityToPinnedRootTask(ActivityRecord r, String reason) {
         mService.deferWindowLayout();
 
         final TaskDisplayArea taskDisplayArea = r.getDisplayArea();
@@ -2162,25 +2161,26 @@
             r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);
 
             final boolean singleActivity = task.getChildCount() == 1;
-            final Task stack;
+            final Task rootTask;
             if (singleActivity) {
-                stack = task;
+                rootTask = task;
             } else {
                 // In the case of multiple activities, we will create a new task for it and then
                 // move the PIP activity into the task.
-                stack = taskDisplayArea.createStack(WINDOWING_MODE_UNDEFINED, r.getActivityType(),
-                        ON_TOP, r.info, r.intent, false /* createdByOrganizer */);
+                rootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_UNDEFINED,
+                        r.getActivityType(), ON_TOP, r.info, r.intent,
+                        false /* createdByOrganizer */);
                 // It's possible the task entering PIP is in freeform, so save the last
                 // non-fullscreen bounds. Then when this new PIP task exits PIP, it can restore
                 // to its previous freeform bounds.
-                stack.setLastNonFullscreenBounds(task.mLastNonFullscreenBounds);
-                stack.setBounds(task.getBounds());
+                rootTask.setLastNonFullscreenBounds(task.mLastNonFullscreenBounds);
+                rootTask.setBounds(task.getBounds());
 
                 // There are multiple activities in the task and moving the top activity should
                 // reveal/leave the other activities in their original task.
                 // On the other hand, ActivityRecord#onParentChanged takes care of setting the
                 // up-to-dated pinned stack information on this newly created stack.
-                r.reparent(stack, MAX_VALUE, reason);
+                r.reparent(rootTask, MAX_VALUE, reason);
 
                 // In the case of this activity entering PIP due to it being moved to the back,
                 // the old activity would have a TRANSIT_TASK_TO_BACK transition that needs to be
@@ -2199,17 +2199,17 @@
             // The intermediate windowing mode to be set on the ActivityRecord later.
             // This needs to happen before the re-parenting, otherwise we will always set the
             // ActivityRecord to be fullscreen.
-            final int intermediateWindowingMode = stack.getWindowingMode();
-            if (stack.getParent() != taskDisplayArea) {
+            final int intermediateWindowingMode = rootTask.getWindowingMode();
+            if (rootTask.getParent() != taskDisplayArea) {
                 // stack is nested, but pinned tasks need to be direct children of their
                 // display area, so reparent.
-                stack.reparent(taskDisplayArea, true /* onTop */);
+                rootTask.reparent(taskDisplayArea, true /* onTop */);
             }
             // Defer the windowing mode change until after the transition to prevent the activity
             // from doing work and changing the activity visuals while animating
             // TODO(task-org): Figure-out more structured way to do this long term.
             r.setWindowingMode(intermediateWindowingMode);
-            stack.setWindowingMode(WINDOWING_MODE_PINNED);
+            rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
 
             // Reset the state that indicates it can enter PiP while pausing after we've moved it
             // to the pinned stack
@@ -2219,7 +2219,7 @@
         }
 
         ensureActivitiesVisible(null, 0, false /* preserveWindows */);
-        resumeFocusedStacksTopActivities();
+        resumeFocusedTasksTopActivities();
 
         notifyActivityPipModeChanged(r);
     }
@@ -2291,13 +2291,13 @@
      * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished.
      */
     int finishTopCrashedActivities(WindowProcessController app, String reason) {
-        Task focusedStack = getTopDisplayFocusedStack();
+        Task focusedStack = getTopDisplayFocusedRootTask();
         Task finishedTask = reduceOnAllTaskDisplayAreas((taskDisplayArea, task) -> {
             // It is possible that request to finish activity might also remove its task and
             // stack, so we need to be careful with indexes in the loop and check child count
             // every time.
-            for (int stackNdx = 0; stackNdx < taskDisplayArea.getStackCount(); ++stackNdx) {
-                final Task stack = taskDisplayArea.getStackAt(stackNdx);
+            for (int stackNdx = 0; stackNdx < taskDisplayArea.getRootTaskCount(); ++stackNdx) {
+                final Task stack = taskDisplayArea.getRootTaskAt(stackNdx);
                 final Task t = stack.finishTopCrashedActivityLocked(app, reason);
                 if (stack == focusedStack || task == null) {
                     task = t;
@@ -2308,21 +2308,21 @@
         return finishedTask != null ? finishedTask.mTaskId : INVALID_TASK_ID;
     }
 
-    boolean resumeFocusedStacksTopActivities() {
-        return resumeFocusedStacksTopActivities(null, null, null);
+    boolean resumeFocusedTasksTopActivities() {
+        return resumeFocusedTasksTopActivities(null, null, null);
     }
 
-    boolean resumeFocusedStacksTopActivities(
-            Task targetStack, ActivityRecord target, ActivityOptions targetOptions) {
+    boolean resumeFocusedTasksTopActivities(
+            Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions) {
 
         if (!mTaskSupervisor.readyToResume()) {
             return false;
         }
 
         boolean result = false;
-        if (targetStack != null && (targetStack.isTopStackInDisplayArea()
-                || getTopDisplayFocusedStack() == targetStack)) {
-            result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
+        if (targetRootTask != null && (targetRootTask.isTopStackInDisplayArea()
+                || getTopDisplayFocusedRootTask() == targetRootTask)) {
+            result = targetRootTask.resumeTopActivityUncheckedLocked(target, targetOptions);
         }
 
         for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
@@ -2330,13 +2330,13 @@
             final boolean curResult = result;
             boolean resumedOnDisplay = display.reduceOnAllTaskDisplayAreas(
                     (taskDisplayArea, resumed) -> {
-                        for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
-                            final Task stack = taskDisplayArea.getStackAt(sNdx);
-                            final ActivityRecord topRunningActivity = stack.topRunningActivity();
-                            if (!stack.isFocusableAndVisible() || topRunningActivity == null) {
+                        for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) {
+                            final Task rootTask = taskDisplayArea.getRootTaskAt(sNdx);
+                            final ActivityRecord topRunningActivity = rootTask.topRunningActivity();
+                            if (!rootTask.isFocusableAndVisible() || topRunningActivity == null) {
                                 continue;
                             }
-                            if (stack == targetStack) {
+                            if (rootTask == targetRootTask) {
                                 // Simply update the result for targetStack because the targetStack
                                 // had already resumed in above. We don't want to resume it again,
                                 // especially in some cases, it would cause a second launch failure
@@ -2344,12 +2344,12 @@
                                 resumed |= curResult;
                                 continue;
                             }
-                            if (taskDisplayArea.isTopStack(stack)
+                            if (taskDisplayArea.isTopRootTask(rootTask)
                                     && topRunningActivity.isState(RESUMED)) {
                                 // Kick off any lingering app transitions form the MoveTaskToFront
                                 // operation, but only consider the top task and stack on that
                                 // display.
-                                stack.executeAppTransition(targetOptions);
+                                rootTask.executeAppTransition(targetOptions);
                             } else {
                                 resumed |= topRunningActivity.makeActiveIfNeeded(target);
                             }
@@ -2362,10 +2362,10 @@
                 // crashed) it's possible that nothing was resumed on a display. Requesting resume
                 // of top activity in focused stack explicitly will make sure that at least home
                 // activity is started and resumed, and no recursion occurs.
-                final Task focusedStack = display.getFocusedStack();
-                if (focusedStack != null) {
-                    result |= focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions);
-                } else if (targetStack == null) {
+                final Task focusedRoot = display.getFocusedRootTask();
+                if (focusedRoot != null) {
+                    result |= focusedRoot.resumeTopActivityUncheckedLocked(target, targetOptions);
+                } else if (targetRootTask == null) {
                     result |= resumeHomeActivity(null /* prev */, "no-focusable-task",
                             display.getDefaultTaskDisplayArea());
                 }
@@ -2391,8 +2391,8 @@
 
             // Set the sleeping state of the stacks on the display.
             display.forAllTaskDisplayAreas(taskDisplayArea -> {
-                for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
-                    final Task stack = taskDisplayArea.getStackAt(sNdx);
+                for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) {
+                    final Task stack = taskDisplayArea.getRootTaskAt(sNdx);
                     if (displayShouldSleep) {
                         stack.goToSleepIfPossible(false /* shuttingDown */);
                     } else {
@@ -2419,34 +2419,34 @@
         }
     }
 
-    protected Task getStack(int stackId) {
+    protected Task getRootTask(int rooTaskId) {
         for (int i = getChildCount() - 1; i >= 0; --i) {
-            final Task stack = getChildAt(i).getStack(stackId);
-            if (stack != null) {
-                return stack;
+            final Task rootTask = getChildAt(i).getRootTask(rooTaskId);
+            if (rootTask != null) {
+                return rootTask;
             }
         }
         return null;
     }
 
-    /** @see DisplayContent#getStack(int, int) */
-    Task getStack(int windowingMode, int activityType) {
+    /** @see DisplayContent#getRootTask(int, int) */
+    Task getRootTask(int windowingMode, int activityType) {
         for (int i = getChildCount() - 1; i >= 0; --i) {
-            final Task stack = getChildAt(i).getStack(windowingMode, activityType);
-            if (stack != null) {
-                return stack;
+            final Task rootTask = getChildAt(i).getRootTask(windowingMode, activityType);
+            if (rootTask != null) {
+                return rootTask;
             }
         }
         return null;
     }
 
-    private Task getStack(int windowingMode, int activityType,
+    private Task getRootTask(int windowingMode, int activityType,
             int displayId) {
         DisplayContent display = getDisplayContent(displayId);
         if (display == null) {
             return null;
         }
-        return display.getStack(windowingMode, activityType);
+        return display.getRootTask(windowingMode, activityType);
     }
 
     private RootTaskInfo getRootTaskInfo(Task task) {
@@ -2491,7 +2491,7 @@
     }
 
     RootTaskInfo getRootTaskInfo(int taskId) {
-        Task task = getStack(taskId);
+        Task task = getRootTask(taskId);
         if (task != null) {
             return getRootTaskInfo(task);
         }
@@ -2499,12 +2499,12 @@
     }
 
     RootTaskInfo getRootTaskInfo(int windowingMode, int activityType) {
-        final Task stack = getStack(windowingMode, activityType);
+        final Task stack = getRootTask(windowingMode, activityType);
         return (stack != null) ? getRootTaskInfo(stack) : null;
     }
 
     RootTaskInfo getRootTaskInfo(int windowingMode, int activityType, int displayId) {
-        final Task stack = getStack(windowingMode, activityType, displayId);
+        final Task stack = getRootTask(windowingMode, activityType, displayId);
         return (stack != null) ? getRootTaskInfo(stack) : null;
     }
 
@@ -2513,8 +2513,8 @@
         ArrayList<RootTaskInfo> list = new ArrayList<>();
         if (displayId == INVALID_DISPLAY) {
             forAllTaskDisplayAreas(taskDisplayArea -> {
-                for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
-                    final Task stack = taskDisplayArea.getStackAt(sNdx);
+                for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) {
+                    final Task stack = taskDisplayArea.getRootTaskAt(sNdx);
                     list.add(getRootTaskInfo(stack));
                 }
             });
@@ -2525,8 +2525,8 @@
             return list;
         }
         display.forAllTaskDisplayAreas(taskDisplayArea -> {
-            for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
-                final Task stack = taskDisplayArea.getStackAt(sNdx);
+            for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) {
+                final Task stack = taskDisplayArea.getRootTaskAt(sNdx);
                 list.add(getRootTaskInfo(stack));
             }
         });
@@ -2598,16 +2598,16 @@
         mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs);
     }
 
-    Task findStackBehind(Task stack) {
-        final TaskDisplayArea taskDisplayArea = stack.getDisplayArea();
+    Task findRootTaskBehind(Task rootTask) {
+        final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
         if (taskDisplayArea != null) {
-            for (int i = taskDisplayArea.getStackCount() - 1; i >= 0; i--) {
-                if (taskDisplayArea.getStackAt(i) == stack && i > 0) {
-                    return taskDisplayArea.getStackAt(i - 1);
+            for (int i = taskDisplayArea.getRootTaskCount() - 1; i >= 0; i--) {
+                if (taskDisplayArea.getRootTaskAt(i) == rootTask && i > 0) {
+                    return taskDisplayArea.getRootTaskAt(i - 1);
                 }
             }
         }
-        throw new IllegalStateException("Failed to find a stack behind stack=" + stack
+        throw new IllegalStateException("Failed to find a root task behind root task =" + rootTask
                 + " in=" + taskDisplayArea);
     }
 
@@ -2743,22 +2743,22 @@
         r.destroyImmediately(mDestroyAllActivitiesReason);
     }
 
-    // Tries to put all activity stacks to sleep. Returns true if all stacks were
+    // Tries to put all activity tasks to sleep. Returns true if all tasks were
     // successfully put to sleep.
-    boolean putStacksToSleep(boolean allowDelay, boolean shuttingDown) {
+    boolean putTasksToSleep(boolean allowDelay, boolean shuttingDown) {
         return reduceOnAllTaskDisplayAreas((taskDisplayArea, result) -> {
-            for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+            for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) {
                 // Stacks and activities could be removed while putting activities to sleep if
                 // the app process was gone. This prevents us getting exception by accessing an
                 // invalid stack index.
-                if (sNdx >= taskDisplayArea.getStackCount()) {
+                if (sNdx >= taskDisplayArea.getRootTaskCount()) {
                     continue;
                 }
-                final Task stack = taskDisplayArea.getStackAt(sNdx);
+                final Task task = taskDisplayArea.getRootTaskAt(sNdx);
                 if (allowDelay) {
-                    result &= stack.goToSleepIfPossible(shuttingDown);
+                    result &= task.goToSleepIfPossible(shuttingDown);
                 } else {
-                    stack.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+                    task.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
                             !PRESERVE_WINDOWS);
                 }
             }
@@ -2827,9 +2827,9 @@
         return false;
     }
 
-    Task getLaunchStack(@Nullable ActivityRecord r,
+    Task getLaunchRootTask(@Nullable ActivityRecord r,
             @Nullable ActivityOptions options, @Nullable Task candidateTask, boolean onTop) {
-        return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */,
+        return getLaunchRootTask(r, options, candidateTask, onTop, null /* launchParams */,
                 -1 /* no realCallingPid */, -1 /* no realCallingUid */);
     }
 
@@ -2844,7 +2844,7 @@
      * @param realCallingUid The uid from {@link ActivityStarter#setRealCallingUid}
      * @return The stack to use for the launch or INVALID_STACK_ID.
      */
-    Task getLaunchStack(@Nullable ActivityRecord r,
+    Task getLaunchRootTask(@Nullable ActivityRecord r,
             @Nullable ActivityOptions options, @Nullable Task candidateTask, boolean onTop,
             @Nullable LaunchParamsController.LaunchParams launchParams, int realCallingPid,
             int realCallingUid) {
@@ -2897,7 +2897,7 @@
                             realCallingPid, realCallingUid, r.info);
             if (canLaunchOnDisplayFromStartRequest || canLaunchOnDisplay(r, tdaDisplayId)) {
                 if (r != null) {
-                    final Task result = getValidLaunchStackInTaskDisplayArea(
+                    final Task result = getValidLaunchRootTaskInTaskDisplayArea(
                             taskDisplayArea, r, candidateTask, options, launchParams);
                     if (result != null) {
                         return result;
@@ -2905,7 +2905,7 @@
                 }
                 // Falling back to default task container
                 taskDisplayArea = taskDisplayArea.mDisplayContent.getDefaultTaskDisplayArea();
-                stack = taskDisplayArea.getOrCreateStack(r, options, candidateTask, activityType,
+                stack = taskDisplayArea.getOrCreateRootTask(r, options, candidateTask, activityType,
                         onTop);
                 if (stack != null) {
                     return stack;
@@ -2959,7 +2959,7 @@
             }
         }
 
-        return container.getOrCreateStack(r, options, candidateTask, activityType, onTop);
+        return container.getOrCreateRootTask(r, options, candidateTask, activityType, onTop);
     }
 
     /** @return true if activity record is null or can be launched on provided display. */
@@ -2980,7 +2980,7 @@
      * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
      */
     @VisibleForTesting
-    Task getValidLaunchStackInTaskDisplayArea(@NonNull TaskDisplayArea taskDisplayArea,
+    Task getValidLaunchRootTaskInTaskDisplayArea(@NonNull TaskDisplayArea taskDisplayArea,
             @NonNull ActivityRecord r, @Nullable Task candidateTask,
             @Nullable ActivityOptions options,
             @Nullable LaunchParamsController.LaunchParams launchParams) {
@@ -3019,9 +3019,9 @@
                 r.getActivityType());
 
         // Return the topmost valid stack on the display.
-        for (int i = taskDisplayArea.getStackCount() - 1; i >= 0; --i) {
-            final Task stack = taskDisplayArea.getStackAt(i);
-            if (isValidLaunchStack(stack, r, windowingMode)) {
+        for (int i = taskDisplayArea.getRootTaskCount() - 1; i >= 0; --i) {
+            final Task stack = taskDisplayArea.getRootTaskAt(i);
+            if (isValidLaunchRootTask(stack, r, windowingMode)) {
                 return stack;
             }
         }
@@ -3033,15 +3033,15 @@
             final int activityType =
                     options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED
                             ? options.getLaunchActivityType() : r.getActivityType();
-            return taskDisplayArea.createStack(windowingMode, activityType, true /*onTop*/);
+            return taskDisplayArea.createRootTask(windowingMode, activityType, true /*onTop*/);
         }
 
         return null;
     }
 
     // TODO: Can probably be consolidated into getLaunchStack()...
-    private boolean isValidLaunchStack(Task stack, ActivityRecord r, int windowingMode) {
-        switch (stack.getActivityType()) {
+    private boolean isValidLaunchRootTask(Task task, ActivityRecord r, int windowingMode) {
+        switch (task.getActivityType()) {
             case ACTIVITY_TYPE_HOME:
                 return r.isActivityTypeHome();
             case ACTIVITY_TYPE_RECENTS:
@@ -3051,13 +3051,13 @@
             case ACTIVITY_TYPE_DREAM:
                 return r.isActivityTypeDream();
         }
-        if (stack.mCreatedByOrganizer) {
+        if (task.mCreatedByOrganizer) {
             // Don't launch directly into task created by organizer...but why can't we?
             return false;
         }
         // There is a 1-to-1 relationship between stack and task when not in
         // primary split-windowing mode.
-        if (stack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+        if (task.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
                 && r.supportsSplitScreenWindowingMode()
                 && (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
                 || windowingMode == WINDOWING_MODE_UNDEFINED)) {
@@ -3094,8 +3094,7 @@
      *                      candidate.
      * @return Next focusable {@link Task}, {@code null} if not found.
      */
-    Task getNextFocusableStack(@NonNull Task currentFocus,
-            boolean ignoreCurrent) {
+    Task getNextFocusableRootTask(@NonNull Task currentFocus, boolean ignoreCurrent) {
         // First look for next focusable stack on the same display
         TaskDisplayArea preferredDisplayArea = currentFocus.getDisplayArea();
         if (preferredDisplayArea == null) {
@@ -3104,7 +3103,7 @@
             preferredDisplayArea = getDisplayContent(currentFocus.mPrevDisplayId)
                     .getDefaultTaskDisplayArea();
         }
-        final Task preferredFocusableStack = preferredDisplayArea.getNextFocusableStack(
+        final Task preferredFocusableStack = preferredDisplayArea.getNextFocusableRootTask(
                 currentFocus, ignoreCurrent);
         if (preferredFocusableStack != null) {
             return preferredFocusableStack;
@@ -3124,7 +3123,7 @@
                 continue;
             }
             final Task nextFocusableStack = display.getDefaultTaskDisplayArea()
-                    .getNextFocusableStack(currentFocus, ignoreCurrent);
+                    .getNextFocusableRootTask(currentFocus, ignoreCurrent);
             if (nextFocusableStack != null) {
                 return nextFocusableStack;
             }
@@ -3260,9 +3259,9 @@
 
     void finishVoiceTask(IVoiceInteractionSession session) {
         forAllTaskDisplayAreas(taskDisplayArea -> {
-            final int numStacks = taskDisplayArea.getStackCount();
+            final int numStacks = taskDisplayArea.getRootTaskCount();
             for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-                final Task stack = taskDisplayArea.getStackAt(stackNdx);
+                final Task stack = taskDisplayArea.getRootTaskAt(stackNdx);
                 stack.finishVoiceTask(session);
             }
         });
@@ -3305,7 +3304,7 @@
 
             // If the focused stack is not null or not empty, there should have some activities
             // resuming or resumed. Make sure these activities are idle.
-            final Task stack = display.getFocusedStack();
+            final Task stack = display.getFocusedRootTask();
             if (stack == null || !stack.hasActivity()) {
                 continue;
             }
@@ -3325,8 +3324,8 @@
         boolean[] foundResumed = {false};
         final boolean foundInvisibleResumedActivity = forAllTaskDisplayAreas(
                 taskDisplayArea -> {
-                    for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
-                        final Task stack = taskDisplayArea.getStackAt(sNdx);
+                    for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) {
+                        final Task stack = taskDisplayArea.getRootTaskAt(sNdx);
                         final ActivityRecord r = stack.getResumedActivity();
                         if (r != null) {
                             if (!r.nowVisible) {
@@ -3347,8 +3346,8 @@
         boolean[] pausing = {true};
         final boolean hasActivityNotCompleted = forAllTaskDisplayAreas(
                 taskDisplayArea -> {
-                    for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
-                        final Task stack = taskDisplayArea.getStackAt(sNdx);
+                    for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) {
+                        final Task stack = taskDisplayArea.getRootTaskAt(sNdx);
                         final ActivityRecord r = stack.mPausingActivity;
                         if (r != null && !r.isState(PAUSED, STOPPED, STOPPING, FINISHING)) {
                             ProtoLog.d(WM_DEBUG_STATES, "allPausedActivitiesComplete: "
@@ -3413,10 +3412,10 @@
 
     void cancelInitializingActivities() {
         forAllTaskDisplayAreas(taskDisplayArea -> {
-            for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+            for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) {
                 // We don't want to clear starting window for activities that aren't occluded
                 // as we need to display their starting window until they are done initializing.
-                taskDisplayArea.getStackAt(sNdx).forAllOccludedActivities(
+                taskDisplayArea.getRootTaskAt(sNdx).forAllOccludedActivities(
                         ActivityRecord::cancelInitializing);
             }
         });
@@ -3456,7 +3455,7 @@
                 // Resolve the stack the task should be placed in now based on options
                 // and reparent if needed.
                 final Task launchStack =
-                        getLaunchStack(null, aOptions, task, onTop);
+                        getLaunchRootTask(null, aOptions, task, onTop);
                 if (launchStack != null && task.getRootTask() != launchStack) {
                     final int reparentMode = onTop
                             ? REPARENT_MOVE_ROOT_TASK_TO_FRONT : REPARENT_LEAVE_ROOT_TASK_IN_PLACE;
@@ -3501,7 +3500,7 @@
         return task;
     }
 
-    ActivityRecord isInAnyStack(IBinder token) {
+    ActivityRecord isInAnyTask(IBinder token) {
         final ActivityRecord r = ActivityRecord.forTokenLocked(token);
         return (r != null && r.isDescendantOf(this)) ? r : null;
     }
@@ -3571,7 +3570,7 @@
     ArrayList<ActivityRecord> getDumpActivities(String name, boolean dumpVisibleStacksOnly,
             boolean dumpFocusedStackOnly) {
         if (dumpFocusedStackOnly) {
-            final Task topFocusedStack = getTopDisplayFocusedStack();
+            final Task topFocusedStack = getTopDisplayFocusedRootTask();
             if (topFocusedStack != null) {
                 return topFocusedStack.getDumpActivitiesLocked(name);
             } else {
@@ -3580,8 +3579,8 @@
         } else {
             ArrayList<ActivityRecord> activities = new ArrayList<>();
             forAllTaskDisplayAreas(taskDisplayArea -> {
-                for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
-                    final Task stack = taskDisplayArea.getStackAt(sNdx);
+                for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) {
+                    final Task stack = taskDisplayArea.getRootTaskAt(sNdx);
                     if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
                         activities.addAll(stack.getDumpActivitiesLocked(name));
                     }
@@ -3595,7 +3594,7 @@
     public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         super.dump(pw, prefix, dumpAll);
         pw.print(prefix);
-        pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack());
+        pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedRootTask());
         for (int i = getChildCount() - 1; i >= 0; --i) {
             final DisplayContent display = getChildAt(i);
             display.dump(pw, prefix, dumpAll);
@@ -3635,8 +3634,8 @@
             pw.print(displayContent.mDisplayId);
             pw.println(" (activities from top to bottom):");
             displayContent.forAllTaskDisplayAreas(taskDisplayArea -> {
-                for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
-                    final Task stack = taskDisplayArea.getStackAt(sNdx);
+                for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) {
+                    final Task stack = taskDisplayArea.getRootTaskAt(sNdx);
                     if (needSep[0]) {
                         pw.println();
                     }
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 7b5b0ad..6ef5d4d 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -67,7 +67,7 @@
         mProfileIds = profileIds;
         mAllowed = allowed;
         mFilterOnlyVisibleRecents = filterOnlyVisibleRecents;
-        mTopDisplayFocusStack = root.getTopDisplayFocusedStack();
+        mTopDisplayFocusStack = root.getTopDisplayFocusedRootTask();
         mRecentTasks = root.mService.getRecentTasks();
 
         final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this,
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b46e796..c414c64 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -38,7 +38,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.annotation.Nullable;
-import android.app.ActivityManagerInternal;
 import android.app.PendingIntent;
 import android.content.ClipData;
 import android.content.ClipDescription;
@@ -56,6 +55,7 @@
 import android.os.RemoteException;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.service.attestation.ImpressionToken;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.MergedConfiguration;
@@ -848,4 +848,15 @@
             Binder.restoreCallingIdentity(identity);
         }
     }
+
+    @Override
+    public ImpressionToken generateImpressionToken(IWindow window, Rect boundsInWindow,
+            String hashAlgorithm) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            return mService.generateImpressionToken(this, window, boundsInWindow, hashAlgorithm);
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index bde40909..4b65ce0f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -101,7 +101,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
+import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_TASK_MSG;
 import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
 import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
@@ -196,6 +196,8 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
 import android.view.RemoteAnimationAdapter;
@@ -597,7 +599,7 @@
     private final AnimatingActivityRegistry mAnimatingActivityRegistry =
             new AnimatingActivityRegistry();
 
-    private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
+    private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_TASK_MSG + 1;
 
     private final Handler mHandler;
 
@@ -650,7 +652,7 @@
             if (mUpdateConfig) {
                 // Ensure the resumed state of the focus activity if we updated the configuration of
                 // any activity.
-                mRootWindowContainer.resumeFocusedStacksTopActivities();
+                mRootWindowContainer.resumeFocusedTasksTopActivities();
             }
         }
 
@@ -973,7 +975,7 @@
         }
         mResizeMode = resizeMode;
         mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
-        mRootWindowContainer.resumeFocusedStacksTopActivities();
+        mRootWindowContainer.resumeFocusedTasksTopActivities();
         updateTaskDescription();
     }
 
@@ -1028,7 +1030,7 @@
                     // activities stay the same.
                     mRootWindowContainer.ensureActivitiesVisible(r, 0, preserveWindow);
                     if (!kept) {
-                        mRootWindowContainer.resumeFocusedStacksTopActivities();
+                        mRootWindowContainer.resumeFocusedTasksTopActivities();
                     }
                 }
             }
@@ -1096,7 +1098,7 @@
         final RootWindowContainer root = mRootWindowContainer;
         final WindowManagerService windowManager = mAtmService.mWindowManager;
         final Task sourceStack = getRootTask();
-        final Task toStack = supervisor.getReparentTargetStack(this, preferredStack,
+        final Task toStack = supervisor.getReparentTargetRootTask(this, preferredStack,
                 position == MAX_VALUE);
         if (toStack == sourceStack) {
             return false;
@@ -1134,7 +1136,7 @@
         boolean kept = true;
         try {
             final ActivityRecord r = topRunningActivityLocked();
-            final boolean wasFocused = r != null && root.isTopDisplayFocusedStack(sourceStack)
+            final boolean wasFocused = r != null && root.isTopDisplayFocusedRootTask(sourceStack)
                     && (topRunningActivityLocked() == r);
             final boolean wasResumed = r != null && sourceStack.getResumedActivity() == r;
             final boolean wasPaused = r != null && sourceStack.mPausingActivity == r;
@@ -1174,7 +1176,7 @@
                     && moveStackMode == REPARENT_KEEP_ROOT_TASK_AT_FRONT) {
                 // Move recents to front so it is not behind home stack when going into docked
                 // mode
-                mTaskSupervisor.moveRecentsStackToFront(reason);
+                mTaskSupervisor.moveRecentsRootTaskToFront(reason);
             }
         } finally {
             mAtmService.continueWindowLayout();
@@ -1191,7 +1193,7 @@
             // The task might have already been running and its visibility needs to be synchronized
             // with the visibility of the stack / windows.
             root.ensureActivitiesVisible(null, 0, !mightReplaceWindow);
-            root.resumeFocusedStacksTopActivities();
+            root.resumeFocusedTasksTopActivities();
         }
 
         // TODO: Handle incorrect request to move before the actual move, not after.
@@ -1694,7 +1696,7 @@
         // A rootable task that is now being added to be the child of an organized task. Making
         // sure the stack references is keep updated.
         if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) {
-            getDisplayArea().addStackReferenceIfNeeded((Task) child);
+            getDisplayArea().addRootTaskReferenceIfNeeded((Task) child);
         }
 
         // Make sure the list of display UID allowlists is updated
@@ -1744,7 +1746,7 @@
         // A rootable child task that is now being removed from an organized task. Making sure
         // the stack references is keep updated.
         if (mCreatedByOrganizer && r.asTask() != null) {
-            getDisplayArea().removeStackReferenceIfNeeded((Task) r);
+            getDisplayArea().removeRootTaskReferenceIfNeeded((Task) r);
         }
         if (!mChildren.contains(r)) {
             Slog.e(TAG, "removeChild: r=" + r + " not found in t=" + this);
@@ -2249,7 +2251,7 @@
             // the rotation animation needs to capture snapshot earlier to avoid animating from
             // an intermediate state.
             if (oldOrientation != getOrientation()) {
-                onDescendantOrientationChanged(null, this);
+                onDescendantOrientationChanged(this);
             }
         } finally {
             if (pipChanging) {
@@ -2301,7 +2303,7 @@
         }
 
         if (prevWindowingMode != getWindowingMode()) {
-            taskDisplayArea.onStackWindowingModeChanged(this);
+            taskDisplayArea.onRootTaskWindowingModeChanged(this);
         }
 
         if (mDisplayContent == null) {
@@ -2328,7 +2330,7 @@
         }
 
         if (windowingModeChanged) {
-            taskDisplayArea.onStackWindowingModeChanged(this);
+            taskDisplayArea.onRootTaskWindowingModeChanged(this);
         }
         if (hasNewOverrideBounds) {
             if (inSplitScreenWindowingMode()) {
@@ -3095,7 +3097,7 @@
             boolean moveDisplayToTop) {
         Task focusableTask = getNextFocusableTask(allowFocusSelf);
         if (focusableTask == null) {
-            focusableTask = mRootWindowContainer.getNextFocusableStack(this, !allowFocusSelf);
+            focusableTask = mRootWindowContainer.getNextFocusableRootTask(this, !allowFocusSelf);
         }
         if (focusableTask == null) {
             return null;
@@ -3305,9 +3307,8 @@
     }
 
     @Override
-    public boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
-            WindowContainer requestingContainer) {
-        if (super.onDescendantOrientationChanged(freezeDisplayToken, requestingContainer)) {
+    public boolean onDescendantOrientationChanged(WindowContainer requestingContainer) {
+        if (super.onDescendantOrientationChanged(requestingContainer)) {
             return true;
         }
 
@@ -4484,14 +4485,14 @@
     /**
      * Saves this {@link Task} to XML using given serializer.
      */
-    void saveToXml(XmlSerializer out) throws Exception {
+    void saveToXml(TypedXmlSerializer out) throws Exception {
         if (DEBUG_RECENTS) Slog.i(TAG_RECENTS, "Saving task=" + this);
 
-        out.attribute(null, ATTR_TASKID, String.valueOf(mTaskId));
+        out.attributeInt(null, ATTR_TASKID, mTaskId);
         if (realActivity != null) {
             out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString());
         }
-        out.attribute(null, ATTR_REALACTIVITY_SUSPENDED, String.valueOf(realActivitySuspended));
+        out.attributeBoolean(null, ATTR_REALACTIVITY_SUSPENDED, realActivitySuspended);
         if (origActivity != null) {
             out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString());
         }
@@ -4510,37 +4511,36 @@
         if (mWindowLayoutAffinity != null) {
             out.attribute(null, ATTR_WINDOW_LAYOUT_AFFINITY, mWindowLayoutAffinity);
         }
-        out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
-        out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
-        out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
-        out.attribute(null, ATTR_USERID, String.valueOf(mUserId));
-        out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete));
-        out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid));
-        out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
-        out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
+        out.attributeBoolean(null, ATTR_ROOTHASRESET, rootWasReset);
+        out.attributeBoolean(null, ATTR_AUTOREMOVERECENTS, autoRemoveRecents);
+        out.attributeBoolean(null, ATTR_ASKEDCOMPATMODE, askedCompatMode);
+        out.attributeInt(null, ATTR_USERID, mUserId);
+        out.attributeBoolean(null, ATTR_USER_SETUP_COMPLETE, mUserSetupComplete);
+        out.attributeInt(null, ATTR_EFFECTIVE_UID, effectiveUid);
+        out.attributeLong(null, ATTR_LASTTIMEMOVED, mLastTimeMoved);
+        out.attributeBoolean(null, ATTR_NEVERRELINQUISH, mNeverRelinquishIdentity);
         if (lastDescription != null) {
             out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
         }
         if (getTaskDescription() != null) {
             getTaskDescription().saveToXml(out);
         }
-        out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId));
-        out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId));
-        out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId));
-        out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid));
+        out.attributeInt(null, ATTR_TASK_AFFILIATION, mAffiliatedTaskId);
+        out.attributeInt(null, ATTR_PREV_AFFILIATION, mPrevAffiliateTaskId);
+        out.attributeInt(null, ATTR_NEXT_AFFILIATION, mNextAffiliateTaskId);
+        out.attributeInt(null, ATTR_CALLING_UID, mCallingUid);
         out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
         out.attribute(null, ATTR_CALLING_FEATURE_ID,
                 mCallingFeatureId == null ? "" : mCallingFeatureId);
-        out.attribute(null, ATTR_RESIZE_MODE, String.valueOf(mResizeMode));
-        out.attribute(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE,
-                String.valueOf(mSupportsPictureInPicture));
+        out.attributeInt(null, ATTR_RESIZE_MODE, mResizeMode);
+        out.attributeBoolean(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE, mSupportsPictureInPicture);
         if (mLastNonFullscreenBounds != null) {
             out.attribute(
                     null, ATTR_NON_FULLSCREEN_BOUNDS, mLastNonFullscreenBounds.flattenToString());
         }
-        out.attribute(null, ATTR_MIN_WIDTH, String.valueOf(mMinWidth));
-        out.attribute(null, ATTR_MIN_HEIGHT, String.valueOf(mMinHeight));
-        out.attribute(null, ATTR_PERSIST_TASK_VERSION, String.valueOf(PERSIST_TASK_VERSION));
+        out.attributeInt(null, ATTR_MIN_WIDTH, mMinWidth);
+        out.attributeInt(null, ATTR_MIN_HEIGHT, mMinHeight);
+        out.attributeInt(null, ATTR_PERSIST_TASK_VERSION, PERSIST_TASK_VERSION);
 
         if (affinityIntent != null) {
             out.startTag(null, TAG_AFFINITYINTENT);
@@ -4565,7 +4565,7 @@
     }
 
     private static boolean saveActivityToXml(
-            ActivityRecord r, ActivityRecord first, XmlSerializer out) {
+            ActivityRecord r, ActivityRecord first, TypedXmlSerializer out) {
         if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable()
                 || ((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT
                 | FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT)
@@ -4584,7 +4584,7 @@
         }
     }
 
-    static Task restoreFromXml(XmlPullParser in, ActivityTaskSupervisor taskSupervisor)
+    static Task restoreFromXml(TypedXmlPullParser in, ActivityTaskSupervisor taskSupervisor)
             throws IOException, XmlPullParserException {
         Intent intent = null;
         Intent affinityIntent = null;
@@ -5133,7 +5133,7 @@
             // The change in force-hidden state will change visibility without triggering a stack
             // order change, so we should reset the preferred top focusable stack to ensure it's not
             // used if a new activity is started from this task.
-            getDisplayArea().resetPreferredTopFocusableStackIfBelow(this);
+            getDisplayArea().resetPreferredTopFocusableRootTaskIfBelow(this);
         }
         return true;
     }
@@ -5276,14 +5276,14 @@
         }
 
         mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
-        mRootWindowContainer.resumeFocusedStacksTopActivities();
+        mRootWindowContainer.resumeFocusedTasksTopActivities();
     }
 
     /** Resume next focusable stack after reparenting to another display. */
     void postReparent() {
         adjustFocusToNextFocusableTask("reparent", true /* allowFocusSelf */,
                 true /* moveDisplayToTop */);
-        mRootWindowContainer.resumeFocusedStacksTopActivities();
+        mRootWindowContainer.resumeFocusedTasksTopActivities();
         // Update visibility of activities before notifying WM. This way it won't try to resize
         // windows that are no longer visible.
         mRootWindowContainer.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
@@ -5320,7 +5320,7 @@
             // cutting between them.
             // TODO(b/70677280): This is a workaround until we can fix as part of b/70677280.
             final Task topFullScreenStack =
-                    taskDisplayArea.getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
+                    taskDisplayArea.getTopRootTaskInWindowingMode(WINDOWING_MODE_FULLSCREEN);
             if (topFullScreenStack != null) {
                 final Task primarySplitScreenStack =
                         taskDisplayArea.getRootSplitScreenPrimaryTask();
@@ -5335,10 +5335,10 @@
         if (!isActivityTypeHome() && returnsToHomeStack()) {
             // Make sure the home stack is behind this stack since that is where we should return to
             // when this stack is no longer visible.
-            taskDisplayArea.moveHomeStackToFront(reason + " returnToHome");
+            taskDisplayArea.moveHomeRootTaskToFront(reason + " returnToHome");
         }
 
-        final Task lastFocusedTask = isRootTask() ? taskDisplayArea.getFocusedStack() : null;
+        final Task lastFocusedTask = isRootTask() ? taskDisplayArea.getFocusedRootTask() : null;
         if (task == null) {
             task = this;
         }
@@ -5366,7 +5366,7 @@
             if (parentTask != null) {
                 parentTask.moveToBack(reason, this);
             } else {
-                final Task lastFocusedTask = displayArea.getFocusedStack();
+                final Task lastFocusedTask = displayArea.getFocusedRootTask();
                 displayArea.positionChildAt(POSITION_BOTTOM, this, false /*includingParents*/);
                 displayArea.updateLastFocusedRootTask(lastFocusedTask, reason);
             }
@@ -5515,7 +5515,7 @@
         if (prev == null) {
             if (resuming == null) {
                 Slog.wtf(TAG, "Trying to pause when nothing is resumed");
-                mRootWindowContainer.resumeFocusedStacksTopActivities();
+                mRootWindowContainer.resumeFocusedTasksTopActivities();
             }
             return false;
         }
@@ -5622,7 +5622,7 @@
             // pause, so just treat it as being paused now.
             ProtoLog.v(WM_DEBUG_STATES, "Activity not running, resuming next.");
             if (resuming == null) {
-                mRootWindowContainer.resumeFocusedStacksTopActivities();
+                mRootWindowContainer.resumeFocusedTasksTopActivities();
             }
             return false;
         }
@@ -5675,9 +5675,9 @@
         }
 
         if (resumeNext) {
-            final Task topStack = mRootWindowContainer.getTopDisplayFocusedStack();
+            final Task topStack = mRootWindowContainer.getTopDisplayFocusedRootTask();
             if (topStack != null && !topStack.shouldSleepOrShutDownActivities()) {
-                mRootWindowContainer.resumeFocusedStacksTopActivities(topStack, prev, null);
+                mRootWindowContainer.resumeFocusedTasksTopActivities(topStack, prev, null);
             } else {
                 checkReadyForSleep();
                 final ActivityRecord top = topStack != null ? topStack.topRunningActivity() : null;
@@ -5686,7 +5686,7 @@
                     // something. Also if the top activity on the stack is not the just paused
                     // activity, we need to go ahead and resume it to ensure we complete an
                     // in-flight app switch.
-                    mRootWindowContainer.resumeFocusedStacksTopActivities();
+                    mRootWindowContainer.resumeFocusedTasksTopActivities();
                 }
             }
         }
@@ -5709,7 +5709,7 @@
 
     boolean isTopStackInDisplayArea() {
         final TaskDisplayArea taskDisplayArea = getDisplayArea();
-        return taskDisplayArea != null && taskDisplayArea.isTopStack(this);
+        return taskDisplayArea != null && taskDisplayArea.isTopRootTask(this);
     }
 
     /**
@@ -5717,7 +5717,7 @@
      * otherwise.
      */
     boolean isFocusedStackOnDisplay() {
-        return mDisplayContent != null && this == mDisplayContent.getFocusedStack();
+        return mDisplayContent != null && this == mDisplayContent.getFocusedRootTask();
     }
 
     /**
@@ -5795,7 +5795,7 @@
      */
     boolean isTopSplitScreenStack() {
         return inSplitScreenWindowingMode()
-                && this == getDisplayArea().getTopStackInWindowingMode(getWindowingMode());
+                && this == getDisplayArea().getTopRootTaskInWindowingMode(getWindowingMode());
     }
 
     void checkTranslucentActivityWaiting(ActivityRecord top) {
@@ -5879,7 +5879,7 @@
      *
      * NOTE: It is not safe to call this method directly as it can cause an activity in a
      *       non-focused stack to be resumed.
-     *       Use {@link RootWindowContainer#resumeFocusedStacksTopActivities} to resume the
+     *       Use {@link RootWindowContainer#resumeFocusedTasksTopActivities} to resume the
      *       right activity for the current system state.
      */
     @GuardedBy("mService")
@@ -6033,7 +6033,7 @@
         mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid);
 
         ActivityRecord lastResumed = null;
-        final Task lastFocusedStack = taskDisplayArea.getLastFocusedStack();
+        final Task lastFocusedStack = taskDisplayArea.getLastFocusedRootTask();
         if (lastFocusedStack != null && lastFocusedStack != this) {
             // So, why aren't we using prev here??? See the param comment on the method. prev
             // doesn't represent the last resumed activity. However, the last focus stack does if
@@ -6048,7 +6048,7 @@
             }
         }
 
-        boolean pausing = taskDisplayArea.pauseBackStacks(userLeaving, next);
+        boolean pausing = taskDisplayArea.pauseBackTasks(userLeaving, next);
         if (mResumedActivity != null) {
             ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Pausing %s", mResumedActivity);
             pausing |= startPausingLocked(userLeaving, false /* uiSleeping */, next,
@@ -6069,7 +6069,7 @@
                 // Since the start-process is asynchronous, if we already know the process of next
                 // activity isn't running, we can start the process earlier to save the time to wait
                 // for the current activity to be paused.
-                final boolean isTop = this == taskDisplayArea.getFocusedStack();
+                final boolean isTop = this == taskDisplayArea.getFocusedRootTask();
                 mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,
                         isTop ? "pre-top-activity" : "pre-activity");
             }
@@ -6359,7 +6359,7 @@
                 // Try to move focus to the next visible stack with a running activity if this
                 // stack is not covering the entire screen or is on a secondary display with no home
                 // stack.
-                return mRootWindowContainer.resumeFocusedStacksTopActivities(nextFocusedStack,
+                return mRootWindowContainer.resumeFocusedTasksTopActivities(nextFocusedStack,
                         prev, null /* targetOptions */);
             }
         }
@@ -6839,7 +6839,7 @@
             AppTimeTracker timeTracker, boolean deferResume, String reason) {
         if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
 
-        final Task topStack = getDisplayArea().getTopStack();
+        final Task topStack = getDisplayArea().getTopRootTask();
         final ActivityRecord topActivity = topStack != null
                 ? topStack.getTopNonFinishingActivity() : null;
 
@@ -6902,7 +6902,7 @@
             }
 
             if (!deferResume) {
-                mRootWindowContainer.resumeFocusedStacksTopActivities();
+                mRootWindowContainer.resumeFocusedTasksTopActivities();
             }
         } finally {
             mDisplayContent.continueUpdateImeTarget();
@@ -6975,7 +6975,7 @@
             // resumed in this case, so we need to execute it explicitly.
             mDisplayContent.executeAppTransition();
         } else {
-            mRootWindowContainer.resumeFocusedStacksTopActivities();
+            mRootWindowContainer.resumeFocusedTasksTopActivities();
         }
         return true;
     }
@@ -7280,7 +7280,7 @@
         final boolean wasResumed = topRunningActivity == task.getRootTask().mResumedActivity;
 
         boolean toTop = position >= getChildCount();
-        boolean includingParents = toTop || getDisplayArea().getNextFocusableStack(this,
+        boolean includingParents = toTop || getDisplayArea().getNextFocusableRootTask(this,
                 true /* ignoreCurrent */) == null;
         if (WindowManagerDebugConfig.DEBUG_ROOT_TASK) {
             Slog.i(TAG_WM, "positionChildAt: positioning task=" + task + " at " + position);
@@ -7309,7 +7309,7 @@
         // The task might have already been running and its visibility needs to be synchronized with
         // the visibility of the stack / windows.
         ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
-        mRootWindowContainer.resumeFocusedStacksTopActivities();
+        mRootWindowContainer.resumeFocusedTasksTopActivities();
     }
 
     public void setAlwaysOnTop(boolean alwaysOnTop) {
@@ -7431,7 +7431,7 @@
         // If there are other focusable stacks on the display, the z-order of the display should not
         // be changed just because a task was placed at the bottom. E.g. if it is moving the topmost
         // task to bottom, the next focusable stack on the same display should be focused.
-        final Task nextFocusableStack = getDisplayArea().getNextFocusableStack(
+        final Task nextFocusableStack = getDisplayArea().getNextFocusableRootTask(
                 child.getRootTask(), true /* ignoreCurrent */);
         positionChildAtBottom(child, nextFocusableStack == null /* includingParents */);
     }
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 4682ba8..b4d069c 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -193,7 +193,7 @@
             switch (msg.what) {
                 case LOG_STACK_STATE_MSG: {
                     synchronized (mServiceLock) {
-                        mTaskSupervisor.logStackState();
+                        mTaskSupervisor.logRootTaskState();
                     }
                     break;
                 }
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 81b8200..4498a8c 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -38,7 +38,7 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
 import static com.android.server.wm.ActivityTaskManagerService.TAG_ROOT_TASK;
-import static com.android.server.wm.DisplayContent.alwaysCreateStack;
+import static com.android.server.wm.DisplayContent.alwaysCreateRootTask;
 import static com.android.server.wm.Task.ActivityState.RESUMED;
 import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
@@ -107,9 +107,9 @@
     // TODO(b/159029784): Remove when getStack() behavior is cleaned-up
     private Task mRootRecentsTask;
 
-    private final ArrayList<Task> mTmpAlwaysOnTopStacks = new ArrayList<>();
-    private final ArrayList<Task> mTmpNormalStacks = new ArrayList<>();
-    private final ArrayList<Task> mTmpHomeStacks = new ArrayList<>();
+    private final ArrayList<Task> mTmpAlwaysOnTopRootTasks = new ArrayList<>();
+    private final ArrayList<Task> mTmpNormalRootTasks = new ArrayList<>();
+    private final ArrayList<Task> mTmpHomeRootTasks = new ArrayList<>();
     private final IntArray mTmpNeedsZBoostIndexes = new IntArray();
     private int mTmpLayerForSplitScreenDividerAnchor;
     private int mTmpLayerForAnimationLayer;
@@ -128,23 +128,25 @@
      * have the topmost index, it is used as a preferred candidate to prevent being unable to resume
      * target stack properly when there are other focusable always-on-top stacks.
      */
-    Task mPreferredTopFocusableStack;
+    Task mPreferredTopFocusableRootTask;
 
     private final RootWindowContainer.FindTaskResult
             mTmpFindTaskResult = new RootWindowContainer.FindTaskResult();
 
     /**
-     * If this is the same as {@link #getFocusedStack} then the activity on the top of the focused
-     * stack has been resumed. If stacks are changing position this will hold the old stack until
-     * the new stack becomes resumed after which it will be set to current focused stack.
+     * If this is the same as {@link #getFocusedRootTask} then the activity on the top of the
+     * focused root task has been resumed. If root tasks are changing position this will hold the
+     * old root task until the new root task becomes resumed after which it will be set to
+     * current focused root task.
      */
-    Task mLastFocusedStack;
+    Task mLastFocusedRootTask;
     /**
      * All of the stacks on this display. Order matters, topmost stack is in front of all other
      * stacks, bottommost behind. Accessed directly by ActivityManager package classes. Any calls
-     * changing the list should also call {@link #onStackOrderChanged()}.
+     * changing the list should also call {@link #onRootTaskOrderChanged(Task)}.
      */
-    private ArrayList<OnStackOrderChangedListener> mStackOrderChangedCallbacks = new ArrayList<>();
+    private ArrayList<OnRootTaskOrderChangedListener> mRootTaskOrderChangedCallbacks =
+            new ArrayList<>();
 
     /**
      * The task display area is removed from the system and we are just waiting for all activities
@@ -181,7 +183,7 @@
      * Returns the topmost stack on the display that is compatible with the input windowing mode
      * and activity type. Null is no compatible stack on the display.
      */
-    Task getStack(int windowingMode, int activityType) {
+    Task getRootTask(int windowingMode, int activityType) {
         if (activityType == ACTIVITY_TYPE_HOME) {
             return mRootHomeTask;
         } else if (activityType == ACTIVITY_TYPE_RECENTS) {
@@ -208,7 +210,7 @@
     }
 
     @VisibleForTesting
-    Task getTopStack() {
+    Task getTopRootTask() {
         final int count = getChildCount();
         return count > 0 ? getChildAt(count - 1) : null;
     }
@@ -255,68 +257,68 @@
         return visibleTasks;
     }
 
-    void onStackWindowingModeChanged(Task stack) {
-        removeStackReferenceIfNeeded(stack);
-        addStackReferenceIfNeeded(stack);
-        if (stack == mRootPinnedTask && getTopStack() != stack) {
+    void onRootTaskWindowingModeChanged(Task rootTask) {
+        removeRootTaskReferenceIfNeeded(rootTask);
+        addRootTaskReferenceIfNeeded(rootTask);
+        if (rootTask == mRootPinnedTask && getTopRootTask() != rootTask) {
             // Looks like this stack changed windowing mode to pinned. Move it to the top.
-            positionChildAt(POSITION_TOP, stack, false /* includingParents */);
+            positionChildAt(POSITION_TOP, rootTask, false /* includingParents */);
         }
     }
 
-    void addStackReferenceIfNeeded(Task stack) {
-        if (stack.isActivityTypeHome()) {
+    void addRootTaskReferenceIfNeeded(Task rootTask) {
+        if (rootTask.isActivityTypeHome()) {
             if (mRootHomeTask != null) {
-                if (!stack.isDescendantOf(mRootHomeTask)) {
+                if (!rootTask.isDescendantOf(mRootHomeTask)) {
                     throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
                             + mRootHomeTask + " already exist on display=" + this
-                            + " stack=" + stack);
+                            + " stack=" + rootTask);
                 }
             } else {
-                mRootHomeTask = stack;
+                mRootHomeTask = rootTask;
             }
-        } else if (stack.isActivityTypeRecents()) {
+        } else if (rootTask.isActivityTypeRecents()) {
             if (mRootRecentsTask != null) {
-                if (!stack.isDescendantOf(mRootRecentsTask)) {
+                if (!rootTask.isDescendantOf(mRootRecentsTask)) {
                     throw new IllegalArgumentException("addStackReferenceIfNeeded: recents stack="
                             + mRootRecentsTask + " already exist on display=" + this
-                            + " stack=" + stack);
+                            + " stack=" + rootTask);
                 }
             } else {
-                mRootRecentsTask = stack;
+                mRootRecentsTask = rootTask;
             }
         }
 
-        if (!stack.isRootTask()) {
+        if (!rootTask.isRootTask()) {
             return;
         }
-        final int windowingMode = stack.getWindowingMode();
+        final int windowingMode = rootTask.getWindowingMode();
         if (windowingMode == WINDOWING_MODE_PINNED) {
             if (mRootPinnedTask != null) {
                 throw new IllegalArgumentException(
                         "addStackReferenceIfNeeded: pinned stack=" + mRootPinnedTask
-                                + " already exist on display=" + this + " stack=" + stack);
+                                + " already exist on display=" + this + " stack=" + rootTask);
             }
-            mRootPinnedTask = stack;
+            mRootPinnedTask = rootTask;
         } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
             if (mRootSplitScreenPrimaryTask != null) {
                 throw new IllegalArgumentException(
                         "addStackReferenceIfNeeded: split screen primary stack="
                                 + mRootSplitScreenPrimaryTask
-                                + " already exist on display=" + this + " stack=" + stack);
+                                + " already exist on display=" + this + " stack=" + rootTask);
             }
-            mRootSplitScreenPrimaryTask = stack;
+            mRootSplitScreenPrimaryTask = rootTask;
         }
     }
 
-    void removeStackReferenceIfNeeded(Task stack) {
-        if (stack == mRootHomeTask) {
+    void removeRootTaskReferenceIfNeeded(Task rootTask) {
+        if (rootTask == mRootHomeTask) {
             mRootHomeTask = null;
-        } else if (stack == mRootRecentsTask) {
+        } else if (rootTask == mRootRecentsTask) {
             mRootRecentsTask = null;
-        } else if (stack == mRootPinnedTask) {
+        } else if (rootTask == mRootPinnedTask) {
             mRootPinnedTask = null;
-        } else if (stack == mRootSplitScreenPrimaryTask) {
+        } else if (rootTask == mRootSplitScreenPrimaryTask) {
             mRootSplitScreenPrimaryTask = null;
         }
     }
@@ -325,20 +327,20 @@
     void addChild(Task task, int position) {
         if (DEBUG_ROOT_TASK) Slog.d(TAG_WM, "Set task=" + task + " on taskDisplayArea=" + this);
 
-        addStackReferenceIfNeeded(task);
-        position = findPositionForStack(position, task, true /* adding */);
+        addRootTaskReferenceIfNeeded(task);
+        position = findPositionForRootTask(position, task, true /* adding */);
 
         super.addChild(task, position);
         mAtmService.updateSleepIfNeededLocked();
-        onStackOrderChanged(task);
+        onRootTaskOrderChanged(task);
     }
 
     @Override
     protected void removeChild(Task stack) {
         super.removeChild(stack);
-        onStackRemoved(stack);
+        onRootTaskRemoved(stack);
         mAtmService.updateSleepIfNeededLocked();
-        removeStackReferenceIfNeeded(stack);
+        removeRootTaskReferenceIfNeeded(stack);
     }
 
     @Override
@@ -367,7 +369,7 @@
         if (!mDisplayContent.isTrusted() && !getParent().isOnTop()) {
             includingParents = false;
         }
-        final int targetPosition = findPositionForStack(position, child, false /* adding */);
+        final int targetPosition = findPositionForRootTask(position, child, false /* adding */);
         super.positionChildAt(targetPosition, child, false /* includingParents */);
 
         if (includingParents && getParent() != null && (moveToTop || moveToBottom)) {
@@ -385,16 +387,16 @@
         // preferred stack is set only when moving an existing stack to top instead of adding a new
         // stack that may be too early (e.g. in the middle of launching or reparenting).
         if (moveToTop && child.isFocusableAndVisible()) {
-            mPreferredTopFocusableStack = child;
-        } else if (mPreferredTopFocusableStack == child) {
-            mPreferredTopFocusableStack = null;
+            mPreferredTopFocusableRootTask = child;
+        } else if (mPreferredTopFocusableRootTask == child) {
+            mPreferredTopFocusableRootTask = null;
         }
 
         // Update the top resumed activity because the preferred top focusable task may be changed.
         mAtmService.mTaskSupervisor.updateTopResumedActivityIfNeeded();
 
         if (mChildren.indexOf(child) != oldPosition) {
-            onStackOrderChanged(child);
+            onRootTaskOrderChanged(child);
         }
     }
 
@@ -469,21 +471,21 @@
         return 0;
     }
 
-    private int findMinPositionForStack(Task stack) {
+    private int findMinPositionForRootTask(Task rootTask) {
         int minPosition = POSITION_BOTTOM;
         for (int i = 0; i < mChildren.size(); ++i) {
-            if (getPriority(getStackAt(i)) < getPriority(stack)) {
+            if (getPriority(getRootTaskAt(i)) < getPriority(rootTask)) {
                 minPosition = i;
             } else {
                 break;
             }
         }
 
-        if (stack.isAlwaysOnTop()) {
+        if (rootTask.isAlwaysOnTop()) {
             // Since a stack could be repositioned while still being one of the children, we check
             // if this always-on-top stack already exists and if so, set the minPosition to its
             // previous position.
-            final int currentIndex = getIndexOf(stack);
+            final int currentIndex = getIndexOf(rootTask);
             if (currentIndex > minPosition) {
                 minPosition = currentIndex;
             }
@@ -491,13 +493,13 @@
         return minPosition;
     }
 
-    private int findMaxPositionForStack(Task stack) {
+    private int findMaxPositionForRootTask(Task rootTask) {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final Task curr = getStackAt(i);
+            final Task curr = getRootTaskAt(i);
             // Since a stack could be repositioned while still being one of the children, we check
             // if 'curr' is the same stack and skip it if so
-            final boolean sameStack = curr == stack;
-            if (getPriority(curr) <= getPriority(stack) && !sameStack) {
+            final boolean sameRootTask = curr == rootTask;
+            if (getPriority(curr) <= getPriority(rootTask) && !sameRootTask) {
                 return i;
             }
         }
@@ -519,16 +521,16 @@
      * (including the Dream); otherwise, it is a normal non-always-on-top stack
      *
      * @param requestedPosition Position requested by caller.
-     * @param stack             Stack to be added or positioned.
+     * @param rootTask          Root task to be added or positioned.
      * @param adding            Flag indicates whether we're adding a new stack or positioning an
      *                          existing.
      * @return The proper position for the stack.
      */
-    private int findPositionForStack(int requestedPosition, Task stack, boolean adding) {
+    private int findPositionForRootTask(int requestedPosition, Task rootTask, boolean adding) {
         // The max possible position we can insert the stack at.
-        int maxPosition = findMaxPositionForStack(stack);
+        int maxPosition = findMaxPositionForRootTask(rootTask);
         // The min possible position we can insert the stack at.
-        int minPosition = findMinPositionForStack(stack);
+        int minPosition = findMinPositionForRootTask(rootTask);
 
         // Cap the requested position to something reasonable for the previous position check
         // below.
@@ -542,7 +544,7 @@
         targetPosition = Math.min(targetPosition, maxPosition);
         targetPosition = Math.max(targetPosition, minPosition);
 
-        int prevPosition = mChildren.indexOf(stack);
+        int prevPosition = mChildren.indexOf(rootTask);
         // The positions we calculated above (maxPosition, minPosition) do not take into
         // consideration the following edge cases.
         // 1) We need to adjust the position depending on the value "adding".
@@ -645,7 +647,7 @@
             return SCREEN_ORIENTATION_UNSET;
         }
 
-        if (isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
+        if (isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
             // Apps and their containers are not allowed to specify an orientation while using
             // root tasks...except for the home stack if it is not resizable and currently
             // visible (top of) its root task.
@@ -672,7 +674,7 @@
         } else {
             // Apps and their containers are not allowed to specify an orientation of full screen
             // tasks created by organizer. The organizer handles the orientation instead.
-            final Task task = getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
+            final Task task = getTopRootTaskInWindowingMode(WINDOWING_MODE_FULLSCREEN);
             if (task != null && task.isVisible() && task.mCreatedByOrganizer) {
                 return SCREEN_ORIENTATION_UNSPECIFIED;
             }
@@ -697,7 +699,7 @@
 
     @Override
     void assignChildLayers(SurfaceControl.Transaction t) {
-        assignStackOrdering(t);
+        assignRootTaskOrdering(t);
 
         for (int i = 0; i < mChildren.size(); i++) {
             final Task s = mChildren.get(i);
@@ -705,37 +707,37 @@
         }
     }
 
-    void assignStackOrdering(SurfaceControl.Transaction t) {
+    void assignRootTaskOrdering(SurfaceControl.Transaction t) {
         if (getParent() == null) {
             return;
         }
-        mTmpAlwaysOnTopStacks.clear();
-        mTmpHomeStacks.clear();
-        mTmpNormalStacks.clear();
+        mTmpAlwaysOnTopRootTasks.clear();
+        mTmpHomeRootTasks.clear();
+        mTmpNormalRootTasks.clear();
         for (int i = 0; i < mChildren.size(); ++i) {
             final Task s = mChildren.get(i);
             if (s.isAlwaysOnTop()) {
-                mTmpAlwaysOnTopStacks.add(s);
+                mTmpAlwaysOnTopRootTasks.add(s);
             } else if (s.isActivityTypeHome()) {
-                mTmpHomeStacks.add(s);
+                mTmpHomeRootTasks.add(s);
             } else {
-                mTmpNormalStacks.add(s);
+                mTmpNormalRootTasks.add(s);
             }
         }
 
         int layer = 0;
         // Place home stacks to the bottom.
-        layer = adjustRootTaskLayer(t, mTmpHomeStacks, layer, false /* normalStacks */);
+        layer = adjustRootTaskLayer(t, mTmpHomeRootTasks, layer, false /* normalStacks */);
         // The home animation layer is between the home stacks and the normal stacks.
         final int layerForHomeAnimationLayer = layer++;
         mTmpLayerForSplitScreenDividerAnchor = layer++;
         mTmpLayerForAnimationLayer = layer++;
-        layer = adjustRootTaskLayer(t, mTmpNormalStacks, layer, true /* normalStacks */);
+        layer = adjustRootTaskLayer(t, mTmpNormalRootTasks, layer, true /* normalStacks */);
 
         // The boosted animation layer is between the normal stacks and the always on top
         // stacks.
         final int layerForBoostedAnimationLayer = layer++;
-        adjustRootTaskLayer(t, mTmpAlwaysOnTopStacks, layer, false /* normalStacks */);
+        adjustRootTaskLayer(t, mTmpAlwaysOnTopRootTasks, layer, false /* normalStacks */);
 
         t.setLayer(mHomeAppAnimationLayer, layerForHomeAnimationLayer);
         t.setLayer(mAppAnimationLayer, mTmpLayerForAnimationLayer);
@@ -743,7 +745,7 @@
         t.setLayer(mBoostedAppAnimationLayer, layerForBoostedAnimationLayer);
     }
 
-    private int adjustNormalStackLayer(Task s, int layer) {
+    private int adjustNormalRootTaskLayer(Task s, int layer) {
         if (s.inSplitScreenWindowingMode()) {
             // The split screen divider anchor is located above the split screen window.
             mTmpLayerForSplitScreenDividerAnchor = layer++;
@@ -773,7 +775,7 @@
             if (!stack.needsZBoost()) {
                 stack.assignLayer(t, startLayer++);
                 if (normalStacks) {
-                    startLayer = adjustNormalStackLayer(stack, startLayer);
+                    startLayer = adjustNormalRootTaskLayer(stack, startLayer);
                 }
             } else {
                 mTmpNeedsZBoostIndexes.add(i);
@@ -785,7 +787,7 @@
             final Task stack = stacks.get(mTmpNeedsZBoostIndexes.get(i));
             stack.assignLayer(t, startLayer++);
             if (normalStacks) {
-                startLayer = adjustNormalStackLayer(stack, startLayer);
+                startLayer = adjustNormalRootTaskLayer(stack, startLayer);
             }
         }
         return startLayer;
@@ -849,22 +851,22 @@
         }
     }
 
-    void onStackRemoved(Task stack) {
+    void onRootTaskRemoved(Task rootTask) {
         if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
-            Slog.v(TAG_ROOT_TASK, "removeStack: detaching " + stack + " from displayId="
+            Slog.v(TAG_ROOT_TASK, "removeStack: detaching " + rootTask + " from displayId="
                     + mDisplayContent.mDisplayId);
         }
-        if (mPreferredTopFocusableStack == stack) {
-            mPreferredTopFocusableStack = null;
+        if (mPreferredTopFocusableRootTask == rootTask) {
+            mPreferredTopFocusableRootTask = null;
         }
         mDisplayContent.releaseSelfIfNeeded();
-        onStackOrderChanged(stack);
+        onRootTaskOrderChanged(rootTask);
     }
 
-    void resetPreferredTopFocusableStackIfBelow(Task task) {
-        if (mPreferredTopFocusableStack != null
-                && mPreferredTopFocusableStack.compareTo(task) < 0) {
-            mPreferredTopFocusableStack = null;
+    void resetPreferredTopFocusableRootTaskIfBelow(Task task) {
+        if (mPreferredTopFocusableRootTask != null
+                && mPreferredTopFocusableRootTask.compareTo(task) < 0) {
+            mPreferredTopFocusableRootTask = null;
         }
     }
 
@@ -894,9 +896,9 @@
         }
     }
 
-    Task getStack(int rootTaskId) {
-        for (int i = getStackCount() - 1; i >= 0; --i) {
-            final Task stack = getStackAt(i);
+    Task getRootTask(int rootTaskId) {
+        for (int i = getRootTaskCount() - 1; i >= 0; --i) {
+            final Task stack = getRootTaskAt(i);
             if (stack.getRootTaskId() == rootTaskId) {
                 return stack;
             }
@@ -908,10 +910,10 @@
      * Returns an existing stack compatible with the windowing mode and activity type or creates one
      * if a compatible stack doesn't exist.
      *
-     * @see #getOrCreateStack(int, int, boolean, Intent, Task)
+     * @see #getOrCreateRootTask(int, int, boolean, Intent, Task)
      */
-    Task getOrCreateStack(int windowingMode, int activityType, boolean onTop) {
-        return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */,
+    Task getOrCreateRootTask(int windowingMode, int activityType, boolean onTop) {
+        return getOrCreateRootTask(windowingMode, activityType, onTop, null /* intent */,
                 null /* candidateTask */);
     }
 
@@ -921,17 +923,17 @@
      * For one level task, the candidate task would be reused to also be the root task or create
      * a new root task if no candidate task.
      *
-     * @see #getStack(int, int)
-     * @see #createStack(int, int, boolean)
+     * @see #getRootTask(int, int)
+     * @see #createRootTask(int, int, boolean)
      */
-    Task getOrCreateStack(int windowingMode, int activityType, boolean onTop,
+    Task getOrCreateRootTask(int windowingMode, int activityType, boolean onTop,
             Intent intent, Task candidateTask) {
         // Need to pass in a determined windowing mode to see if a new stack should be created,
         // so use its parent's windowing mode if it is undefined.
-        if (!alwaysCreateStack(
+        if (!alwaysCreateRootTask(
                 windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : getWindowingMode(),
                 activityType)) {
-            Task stack = getStack(windowingMode, activityType);
+            Task stack = getRootTask(windowingMode, activityType);
             if (stack != null) {
                 return stack;
             }
@@ -959,7 +961,7 @@
             }
             return stack;
         }
-        return createStack(windowingMode, activityType, onTop, null /*info*/, intent,
+        return createRootTask(windowingMode, activityType, onTop, null /*info*/, intent,
                 false /* createdByOrganizer */);
     }
 
@@ -967,9 +969,9 @@
      * Returns an existing stack compatible with the input params or creates one
      * if a compatible stack doesn't exist.
      *
-     * @see #getOrCreateStack(int, int, boolean)
+     * @see #getOrCreateRootTask(int, int, boolean)
      */
-    Task getOrCreateStack(@Nullable ActivityRecord r,
+    Task getOrCreateRootTask(@Nullable ActivityRecord r,
             @Nullable ActivityOptions options, @Nullable Task candidateTask, int activityType,
             boolean onTop) {
         // First preference is the windowing mode in the activity options if set.
@@ -979,24 +981,24 @@
         // UNDEFINED windowing mode is a valid result and means that the new stack will inherit
         // it's display's windowing mode.
         windowingMode = validateWindowingMode(windowingMode, r, candidateTask, activityType);
-        return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */,
+        return getOrCreateRootTask(windowingMode, activityType, onTop, null /* intent */,
                 candidateTask);
     }
 
     @VisibleForTesting
-    int getNextStackId() {
+    int getNextRootTaskId() {
         return mAtmService.mTaskSupervisor.getNextTaskIdForUser();
     }
 
-    Task createStack(int windowingMode, int activityType, boolean onTop) {
-        return createStack(windowingMode, activityType, onTop, null /* info */, null /* intent */,
-                false /* createdByOrganizer */);
+    Task createRootTask(int windowingMode, int activityType, boolean onTop) {
+        return createRootTask(windowingMode, activityType, onTop, null /* info */,
+                null /* intent */, false /* createdByOrganizer */);
     }
 
-    Task createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info,
+    Task createRootTask(int windowingMode, int activityType, boolean onTop, ActivityInfo info,
             Intent intent, boolean createdByOrganizer) {
-        return createStack(windowingMode, activityType, onTop, null /* info */, null /* intent */,
-                false /* createdByOrganizer */ , false /* deferTaskAppear */,
+        return createRootTask(windowingMode, activityType, onTop, null /* info */,
+                null /* intent */, false /* createdByOrganizer */, false /* deferTaskAppear */,
                 null /* launchCookie */);
     }
 
@@ -1022,7 +1024,7 @@
      *                           creating.
      * @return The newly created stack.
      */
-    Task createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info,
+    Task createRootTask(int windowingMode, int activityType, boolean onTop, ActivityInfo info,
             Intent intent, boolean createdByOrganizer, boolean deferTaskAppear,
             IBinder launchCookie) {
         if (activityType == ACTIVITY_TYPE_UNDEFINED && !createdByOrganizer) {
@@ -1034,7 +1036,7 @@
         if (activityType != ACTIVITY_TYPE_STANDARD && activityType != ACTIVITY_TYPE_UNDEFINED) {
             // For now there can be only one stack of a particular non-standard activity type on a
             // display. So, get that ignoring whatever windowing mode it is currently in.
-            Task stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
+            Task stack = getRootTask(WINDOWING_MODE_UNDEFINED, activityType);
             if (stack != null) {
                 throw new IllegalArgumentException("Stack=" + stack + " of activityType="
                         + activityType + " already on display=" + this + ". Can't have multiple.");
@@ -1054,8 +1056,8 @@
             getRootPinnedTask().dismissPip();
         }
 
-        final int stackId = getNextStackId();
-        return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent,
+        final int stackId = getNextRootTaskId();
+        return createRootTaskUnchecked(windowingMode, activityType, stackId, onTop, info, intent,
                 createdByOrganizer, deferTaskAppear, launchCookie);
     }
 
@@ -1065,15 +1067,15 @@
             // Only split-screen windowing modes can do this currently...
             return null;
         }
-        for (int i = getStackCount() - 1; i >= 0; --i) {
-            final Task t = getStackAt(i);
+        for (int i = getRootTaskCount() - 1; i >= 0; --i) {
+            final Task t = getRootTaskAt(i);
             if (!t.mCreatedByOrganizer || t.getRequestedOverrideWindowingMode() != windowingMode) {
                 continue;
             }
             // If not already set, pick a launch root which is not the one we are launching into.
             if (mLaunchRootTask == null) {
-                for (int j = 0, n = getStackCount(); j < n; ++j) {
-                    final Task tt = getStackAt(j);
+                for (int j = 0, n = getRootTaskCount(); j < n; ++j) {
+                    final Task tt = getRootTaskAt(j);
                     if (tt.mCreatedByOrganizer && tt != t) {
                         mLaunchRootTask = tt;
                         break;
@@ -1086,7 +1088,7 @@
     }
 
     @VisibleForTesting
-    Task createStackUnchecked(int windowingMode, int activityType, int stackId, boolean onTop,
+    Task createRootTaskUnchecked(int windowingMode, int activityType, int stackId, boolean onTop,
             ActivityInfo info, Intent intent, boolean createdByOrganizer, boolean deferTaskAppear,
             IBinder launchCookie) {
         if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) {
@@ -1123,13 +1125,13 @@
      * Get the preferred focusable stack in priority. If the preferred stack does not exist, find a
      * focusable and visible stack from the top of stacks in this display.
      */
-    Task getFocusedStack() {
-        if (mPreferredTopFocusableStack != null) {
-            return mPreferredTopFocusableStack;
+    Task getFocusedRootTask() {
+        if (mPreferredTopFocusableRootTask != null) {
+            return mPreferredTopFocusableRootTask;
         }
 
-        for (int i = getStackCount() - 1; i >= 0; --i) {
-            final Task stack = getStackAt(i);
+        for (int i = getRootTaskCount() - 1; i >= 0; --i) {
+            final Task stack = getRootTaskAt(i);
             if (stack.isFocusableAndVisible()) {
                 return stack;
             }
@@ -1138,22 +1140,22 @@
         return null;
     }
 
-    Task getNextFocusableStack(Task currentFocus, boolean ignoreCurrent) {
+    Task getNextFocusableRootTask(Task currentFocus, boolean ignoreCurrent) {
         final int currentWindowingMode = currentFocus != null
                 ? currentFocus.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
 
         Task candidate = null;
-        for (int i = getStackCount() - 1; i >= 0; --i) {
-            final Task stack = getStackAt(i);
-            if (ignoreCurrent && stack == currentFocus) {
+        for (int i = getRootTaskCount() - 1; i >= 0; --i) {
+            final Task rootTask = getRootTaskAt(i);
+            if (ignoreCurrent && rootTask == currentFocus) {
                 continue;
             }
-            if (!stack.isFocusableAndVisible()) {
+            if (!rootTask.isFocusableAndVisible()) {
                 continue;
             }
 
             if (currentWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
-                    && candidate == null && stack.inSplitScreenPrimaryWindowingMode()) {
+                    && candidate == null && rootTask.inSplitScreenPrimaryWindowingMode()) {
                 // If the currently focused stack is in split-screen secondary we save off the
                 // top primary split-screen stack as a candidate for focus because we might
                 // prefer focus to move to an other stack to avoid primary split-screen stack
@@ -1161,20 +1163,20 @@
                 // than the next split-screen stack. Assistant stack, I am looking at you...
                 // We only move the focus to the primary-split screen stack if there isn't a
                 // better alternative.
-                candidate = stack;
+                candidate = rootTask;
                 continue;
             }
-            if (candidate != null && stack.inSplitScreenSecondaryWindowingMode()) {
+            if (candidate != null && rootTask.inSplitScreenSecondaryWindowingMode()) {
                 // Use the candidate stack since we are now at the secondary split-screen.
                 return candidate;
             }
-            return stack;
+            return rootTask;
         }
         return candidate;
     }
 
     ActivityRecord getFocusedActivity() {
-        final Task focusedStack = getFocusedStack();
+        final Task focusedStack = getFocusedRootTask();
         if (focusedStack == null) {
             return null;
         }
@@ -1194,8 +1196,8 @@
         return resumedActivity;
     }
 
-    Task getLastFocusedStack() {
-        return mLastFocusedStack;
+    Task getLastFocusedRootTask() {
+        return mLastFocusedRootTask;
     }
 
     void updateLastFocusedRootTask(Task prevFocusedTask, String updateLastFocusedTaskReason) {
@@ -1203,7 +1205,7 @@
             return;
         }
 
-        final Task currentFocusedTask = getFocusedStack();
+        final Task currentFocusedTask = getFocusedRootTask();
         if (currentFocusedTask == prevFocusedTask) {
             return;
         }
@@ -1214,27 +1216,27 @@
             currentFocusedTask.mLastPausedActivity = null;
         }
 
-        mLastFocusedStack = prevFocusedTask;
+        mLastFocusedRootTask = prevFocusedTask;
         EventLogTags.writeWmFocusedStack(mRootWindowContainer.mCurrentUser,
                 mDisplayContent.mDisplayId,
                 currentFocusedTask == null ? -1 : currentFocusedTask.getRootTaskId(),
-                mLastFocusedStack == null ? -1 : mLastFocusedStack.getRootTaskId(),
+                mLastFocusedRootTask == null ? -1 : mLastFocusedRootTask.getRootTaskId(),
                 updateLastFocusedTaskReason);
     }
 
     boolean allResumedActivitiesComplete() {
-        for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityRecord r = getStackAt(stackNdx).getResumedActivity();
+        for (int stackNdx = getRootTaskCount() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityRecord r = getRootTaskAt(stackNdx).getResumedActivity();
             if (r != null && !r.isState(RESUMED)) {
                 return false;
             }
         }
-        final Task currentFocusedStack = getFocusedStack();
+        final Task currentFocusedStack = getFocusedRootTask();
         if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
             Slog.d(TAG_ROOT_TASK, "allResumedActivitiesComplete: mLastFocusedStack changing from="
-                    + mLastFocusedStack + " to=" + currentFocusedStack);
+                    + mLastFocusedRootTask + " to=" + currentFocusedStack);
         }
-        mLastFocusedStack = currentFocusedStack;
+        mLastFocusedRootTask = currentFocusedStack;
         return true;
     }
 
@@ -1249,10 +1251,10 @@
      * @param resuming    The resuming activity.
      * @return {@code true} if any activity was paused as a result of this call.
      */
-    boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming) {
+    boolean pauseBackTasks(boolean userLeaving, ActivityRecord resuming) {
         boolean someActivityPaused = false;
-        for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-            final Task stack = getStackAt(stackNdx);
+        for (int stackNdx = getRootTaskCount() - 1; stackNdx >= 0; --stackNdx) {
+            final Task stack = getRootTaskAt(stackNdx);
             final ActivityRecord resumedActivity = stack.getResumedActivity();
             if (resumedActivity != null
                     && (stack.getVisibility(resuming) != TASK_VISIBILITY_VISIBLE
@@ -1272,8 +1274,8 @@
     void findTaskLocked(final ActivityRecord r, final boolean isPreferredDisplayArea,
             RootWindowContainer.FindTaskResult result) {
         mTmpFindTaskResult.clear();
-        for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-            final Task stack = getStackAt(stackNdx);
+        for (int stackNdx = getRootTaskCount() - 1; stackNdx >= 0; --stackNdx) {
+            final Task stack = getRootTaskAt(stackNdx);
             if (!r.hasCompatibleActivityType(stack) && stack.isLeafTask()) {
                 ProtoLog.d(WM_DEBUG_TASKS, "Skipping stack: (mismatch activity/stack) "
                         + "%s", stack);
@@ -1367,7 +1369,7 @@
         // The focused task could be a non-resizeable fullscreen root task that is on top of the
         // other split-screen tasks, therefore had to dismiss split-screen, make sure the current
         // focused root task can still be on top after dismissal
-        final Task rootTask = getFocusedStack();
+        final Task rootTask = getFocusedRootTask();
         final Task toTop =
                 rootTask != null && !rootTask.inSplitScreenWindowingMode() ? rootTask : null;
         onSplitScreenModeDismissed(toTop);
@@ -1380,9 +1382,9 @@
             moveSplitScreenTasksToFullScreen();
         } finally {
             final Task topFullscreenStack = toTop != null
-                    ? toTop : getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
+                    ? toTop : getTopRootTaskInWindowingMode(WINDOWING_MODE_FULLSCREEN);
             final Task homeStack = getOrCreateRootHomeTask();
-            if (homeStack != null && ((topFullscreenStack != null && !isTopStack(homeStack))
+            if (homeStack != null && ((topFullscreenStack != null && !isTopRootTask(homeStack))
                     || toTop != null)) {
                 // Whenever split-screen is dismissed we want the home stack directly behind the
                 // current top fullscreen stack so it shows up when the top stack is finished.
@@ -1556,8 +1558,8 @@
         return windowingMode;
     }
 
-    boolean isTopStack(Task stack) {
-        return stack == getTopStack();
+    boolean isTopRootTask(Task stack) {
+        return stack == getTopRootTask();
     }
 
     ActivityRecord topRunningActivity() {
@@ -1575,15 +1577,15 @@
      */
     ActivityRecord topRunningActivity(boolean considerKeyguardState) {
         ActivityRecord topRunning = null;
-        final Task focusedStack = getFocusedStack();
+        final Task focusedStack = getFocusedRootTask();
         if (focusedStack != null) {
             topRunning = focusedStack.topRunningActivity();
         }
 
         // Look in other focusable stacks.
         if (topRunning == null) {
-            for (int i = getStackCount() - 1; i >= 0; --i) {
-                final Task stack = getStackAt(i);
+            for (int i = getRootTaskCount() - 1; i >= 0; --i) {
+                final Task stack = getRootTaskAt(i);
                 // Only consider focusable stacks other than the current focused one.
                 if (stack == focusedStack || !stack.isTopActivityFocusable()) {
                     continue;
@@ -1607,13 +1609,11 @@
         return topRunning;
     }
 
-    // TODO (b/157876447): switch to Task related name
-    protected int getStackCount() {
+    protected int getRootTaskCount() {
         return mChildren.size();
     }
 
-    // TODO (b/157876447): switch to Task related name
-    protected Task getStackAt(int index) {
+    protected Task getRootTaskAt(int index) {
         return mChildren.get(index);
     }
 
@@ -1633,7 +1633,7 @@
     Task getOrCreateRootHomeTask(boolean onTop) {
         Task homeTask = getRootHomeTask();
         if (homeTask == null && mDisplayContent.supportsSystemDecorations()) {
-            homeTask = createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, onTop);
+            homeTask = createRootTask(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, onTop);
         }
         return homeTask;
     }
@@ -1647,14 +1647,14 @@
      * Returns the topmost stack on the display that is compatible with the input windowing mode.
      * Null is no compatible stack on the display.
      */
-    Task getTopStackInWindowingMode(int windowingMode) {
-        return getStack(windowingMode, ACTIVITY_TYPE_UNDEFINED);
+    Task getTopRootTaskInWindowingMode(int windowingMode) {
+        return getRootTask(windowingMode, ACTIVITY_TYPE_UNDEFINED);
     }
 
-    void moveHomeStackToFront(String reason) {
-        final Task homeStack = getOrCreateRootHomeTask();
-        if (homeStack != null) {
-            homeStack.moveToFront(reason);
+    void moveHomeRootTaskToFront(String reason) {
+        final Task homeRootTask = getOrCreateRootHomeTask();
+        if (homeRootTask != null) {
+            homeRootTask.moveToFront(reason);
         }
     }
 
@@ -1665,7 +1665,7 @@
     void moveHomeActivityToTop(String reason) {
         final ActivityRecord top = getHomeActivity();
         if (top == null) {
-            moveHomeStackToFront(reason);
+            moveHomeRootTaskToFront(reason);
             return;
         }
         top.moveFocusableActivityToTop(reason);
@@ -1697,25 +1697,27 @@
 
     /**
      * Adjusts the {@param stack} behind the last visible stack in the display if necessary.
-     * Generally used in conjunction with {@link #moveStackBehindStack}.
+     * Generally used in conjunction with {@link #moveRootTaskBehindRootTask}.
      */
     // TODO(b/151575894): Remove special stack movement methods.
-    void moveStackBehindBottomMostVisibleStack(Task stack) {
-        if (stack.shouldBeVisible(null)) {
+    void moveRootTaskBehindBottomMostVisibleRootTask(Task rootTask) {
+        if (rootTask.shouldBeVisible(null)) {
             // Skip if the stack is already visible
             return;
         }
 
         // Move the stack to the bottom to not affect the following visibility checks
-        stack.getParent().positionChildAt(POSITION_BOTTOM, stack, false /* includingParents */);
+        rootTask.getParent().positionChildAt(POSITION_BOTTOM, rootTask,
+                false /* includingParents */);
 
         // Find the next position where the stack should be placed
-        final boolean isRootTask = stack.isRootTask();
-        final int numStacks = isRootTask ? getStackCount() : stack.getParent().getChildCount();
-        for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
-            final Task s = isRootTask ? getStackAt(stackNdx)
-                    : (Task) stack.getParent().getChildAt(stackNdx);
-            if (s == stack) {
+        final boolean isRootTask = rootTask.isRootTask();
+        final int numRootTasks =
+                isRootTask ? getRootTaskCount() : rootTask.getParent().getChildCount();
+        for (int rootTaskNdx = 0; rootTaskNdx < numRootTasks; rootTaskNdx++) {
+            final Task s = isRootTask ? getRootTaskAt(rootTaskNdx)
+                    : (Task) rootTask.getParent().getChildAt(rootTaskNdx);
+            if (s == rootTask) {
                 continue;
             }
             final int winMode = s.getWindowingMode();
@@ -1723,8 +1725,9 @@
                     || winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
             if (s.shouldBeVisible(null) && isValidWindowingMode) {
                 // Move the provided stack to behind this stack
-                final int position = Math.max(0, stackNdx - 1);
-                stack.getParent().positionChildAt(position, stack, false /*includingParents */);
+                final int position = Math.max(0, rootTaskNdx - 1);
+                rootTask.getParent().positionChildAt(position, rootTask,
+                        false /*includingParents */);
                 break;
             }
         }
@@ -1733,15 +1736,16 @@
     /**
      * Moves the {@param stack} behind the given {@param behindStack} if possible. If
      * {@param behindStack} is not currently in the display, then then the stack is moved to the
-     * back. Generally used in conjunction with {@link #moveStackBehindBottomMostVisibleStack}.
+     * back. Generally used in conjunction with
+     * {@link #moveRootTaskBehindBottomMostVisibleRootTask}.
      */
-    void moveStackBehindStack(Task stack, Task behindStack) {
-        if (behindStack == null || behindStack == stack) {
+    void moveRootTaskBehindRootTask(Task rootTask, Task behindRootTask) {
+        if (behindRootTask == null || behindRootTask == rootTask) {
             return;
         }
 
-        final WindowContainer parent = stack.getParent();
-        if (parent == null || parent != behindStack.getParent()) {
+        final WindowContainer parent = rootTask.getParent();
+        if (parent == null || parent != behindRootTask.getParent()) {
             return;
         }
 
@@ -1749,12 +1753,12 @@
         // list, so we need to adjust the insertion index to account for the removed index
         // TODO: Remove this logic when WindowContainer.positionChildAt() is updated to adjust the
         //       position internally
-        final int stackIndex = parent.mChildren.indexOf(stack);
-        final int behindStackIndex = parent.mChildren.indexOf(behindStack);
+        final int stackIndex = parent.mChildren.indexOf(rootTask);
+        final int behindStackIndex = parent.mChildren.indexOf(behindRootTask);
         final int insertIndex = stackIndex <= behindStackIndex
                 ? behindStackIndex - 1 : behindStackIndex;
         final int position = Math.max(0, insertIndex);
-        parent.positionChildAt(position, stack, false /* includingParents */);
+        parent.positionChildAt(position, rootTask, false /* includingParents */);
     }
 
     boolean hasPinnedTask() {
@@ -1765,20 +1769,20 @@
      * @return the stack currently above the {@param stack}. Can be null if the {@param stack} is
      * already top-most.
      */
-    static Task getStackAbove(Task stack) {
-        final WindowContainer wc = stack.getParent();
-        final int index = wc.mChildren.indexOf(stack) + 1;
+    static Task getRootTaskAbove(Task rootTask) {
+        final WindowContainer wc = rootTask.getParent();
+        final int index = wc.mChildren.indexOf(rootTask) + 1;
         return (index < wc.mChildren.size()) ? (Task) wc.mChildren.get(index) : null;
     }
 
     /** Returns true if the stack in the windowing mode is visible. */
-    boolean isStackVisible(int windowingMode) {
-        final Task stack = getTopStackInWindowingMode(windowingMode);
-        return stack != null && stack.isVisible();
+    boolean isRootTaskVisible(int windowingMode) {
+        final Task rootTask = getTopRootTaskInWindowingMode(windowingMode);
+        return rootTask != null && rootTask.isVisible();
     }
 
-    void removeStack(Task stack) {
-        removeChild(stack);
+    void removeRootTask(Task rootTask) {
+        removeChild(rootTask);
     }
 
     int getDisplayId() {
@@ -1794,27 +1798,27 @@
      * only used by the {@link RecentsAnimation} to determine whether to interrupt and cancel the
      * current animation when the system state changes.
      */
-    void registerStackOrderChangedListener(OnStackOrderChangedListener listener) {
-        if (!mStackOrderChangedCallbacks.contains(listener)) {
-            mStackOrderChangedCallbacks.add(listener);
+    void registerRootTaskOrderChangedListener(OnRootTaskOrderChangedListener listener) {
+        if (!mRootTaskOrderChangedCallbacks.contains(listener)) {
+            mRootTaskOrderChangedCallbacks.add(listener);
         }
     }
 
     /**
      * Removes a previously registered stack order change listener.
      */
-    void unregisterStackOrderChangedListener(OnStackOrderChangedListener listener) {
-        mStackOrderChangedCallbacks.remove(listener);
+    void unregisterRootTaskOrderChangedListener(OnRootTaskOrderChangedListener listener) {
+        mRootTaskOrderChangedCallbacks.remove(listener);
     }
 
     /**
-     * Notifies of a stack order change
+     * Notifies of a root task order change
      *
-     * @param stack The stack which triggered the order change
+     * @param rootTask The root task which triggered the order change
      */
-    void onStackOrderChanged(Task stack) {
-        for (int i = mStackOrderChangedCallbacks.size() - 1; i >= 0; i--) {
-            mStackOrderChangedCallbacks.get(i).onStackOrderChanged(stack);
+    void onRootTaskOrderChanged(Task rootTask) {
+        for (int i = mRootTaskOrderChangedCallbacks.size() - 1; i >= 0; i--) {
+            mRootTaskOrderChangedCallbacks.get(i).onRootTaskOrderChanged(rootTask);
         }
     }
 
@@ -1826,16 +1830,16 @@
     /**
      * Callback for when the order of the stacks in the display changes.
      */
-    interface OnStackOrderChangedListener {
-        void onStackOrderChanged(Task stack);
+    interface OnRootTaskOrderChangedListener {
+        void onRootTaskOrderChanged(Task rootTask);
     }
 
     void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
             boolean preserveWindows, boolean notifyClients, boolean userLeaving) {
         mAtmService.mTaskSupervisor.beginActivityVisibilityUpdate();
         try {
-            for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-                final Task stack = getStackAt(stackNdx);
+            for (int stackNdx = getRootTaskCount() - 1; stackNdx >= 0; --stackNdx) {
+                final Task stack = getRootTaskAt(stackNdx);
                 stack.ensureActivitiesVisible(starting, configChanges, preserveWindows,
                         notifyClients, userLeaving);
             }
@@ -1857,7 +1861,7 @@
      * @return last reparented stack, or {@code null} if the stacks had to be destroyed.
      */
     Task remove() {
-        mPreferredTopFocusableStack = null;
+        mPreferredTopFocusableRootTask = null;
         // TODO(b/153090332): Allow setting content removal mode per task display area
         final boolean destroyContentOnRemoval = mDisplayContent.shouldDestroyContentOnRemove();
         final TaskDisplayArea toDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
@@ -1869,13 +1873,13 @@
         // related WindowContainer will also be removed. So, we set display area as removed after
         // reparenting stack finished.
         // Keep the order from bottom to top.
-        int numStacks = getStackCount();
+        int numStacks = getRootTaskCount();
 
         final boolean splitScreenActivated = toDisplayArea.isSplitScreenModeActivated();
         final Task rootStack = splitScreenActivated ? toDisplayArea
-                .getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) : null;
+                .getTopRootTaskInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) : null;
         for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
-            final Task stack = getStackAt(stackNdx);
+            final Task stack = getRootTaskAt(stackNdx);
             // Always finish non-standard type stacks and stacks created by a organizer.
             // TODO: For stacks created by organizer, consider reparenting children tasks if the use
             //       case arises in the future.
@@ -1895,8 +1899,8 @@
             }
             // Stacks may be removed from this display. Ensure each stack will be processed
             // and the loop will end.
-            stackNdx -= numStacks - getStackCount();
-            numStacks = getStackCount();
+            stackNdx -= numStacks - getRootTaskCount();
+            numStacks = getRootTaskCount();
         }
         if (lastReparentedStack != null && splitScreenActivated) {
             if (!lastReparentedStack.supportsSplitScreenWindowingMode()) {
@@ -1935,18 +1939,19 @@
         pw.println(prefix + "TaskDisplayArea " + getName());
         final String doublePrefix = prefix + "  ";
         super.dump(pw, doublePrefix, dumpAll);
-        if (mPreferredTopFocusableStack != null) {
-            pw.println(doublePrefix + "mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
+        if (mPreferredTopFocusableRootTask != null) {
+            pw.println(doublePrefix + "mPreferredTopFocusableRootTask="
+                    + mPreferredTopFocusableRootTask);
         }
-        if (mLastFocusedStack != null) {
-            pw.println(doublePrefix + "mLastFocusedStack=" + mLastFocusedStack);
+        if (mLastFocusedRootTask != null) {
+            pw.println(doublePrefix + "mLastFocusedRootTask=" + mLastFocusedRootTask);
         }
         final String triplePrefix = doublePrefix + "  ";
         pw.println(doublePrefix + "Application tokens in top down Z order:");
-        for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-            final Task stack = getChildAt(stackNdx);
-            pw.println(doublePrefix + "* " + stack);
-            stack.dump(pw, triplePrefix, dumpAll);
+        for (int rootTaskNdx = getChildCount() - 1; rootTaskNdx >= 0; --rootTaskNdx) {
+            final Task rootTask = getChildAt(rootTaskNdx);
+            pw.println(doublePrefix + "* " + rootTask);
+            rootTask.dump(pw, triplePrefix, dumpAll);
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 8b2fa52..9d36b84 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -796,9 +796,9 @@
             @NonNull Rect inOutBounds) {
         final List<Rect> taskBoundsToCheck = new ArrayList<>();
         display.forAllTaskDisplayAreas(taskDisplayArea -> {
-            int numStacks = taskDisplayArea.getStackCount();
+            int numStacks = taskDisplayArea.getRootTaskCount();
             for (int sNdx = 0; sNdx < numStacks; ++sNdx) {
-                final Task task = taskDisplayArea.getStackAt(sNdx);
+                final Task task = taskDisplayArea.getRootTaskAt(sNdx);
                 if (!task.inFreeformWindowingMode()) {
                     continue;
                 }
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 009a7ef..e635219 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -478,7 +478,7 @@
         // We want to defer the task appear signal until the task is fully created and attached to
         // to the hierarchy so that the complete starting configuration is in the task info we send
         // over to the organizer.
-        final Task task = display.getDefaultTaskDisplayArea().createStack(windowingMode,
+        final Task task = display.getDefaultTaskDisplayArea().createRootTask(windowingMode,
                 ACTIVITY_TYPE_UNDEFINED, false /* onTop */, null /* info */, new Intent(),
                 true /* createdByOrganizer */, true /* deferTaskAppear */, launchCookie);
         task.setDeferTaskAppear(false /* deferTaskAppear */);
@@ -688,8 +688,8 @@
                 }
                 ArrayList<RunningTaskInfo> out = new ArrayList<>();
                 dc.forAllTaskDisplayAreas(taskDisplayArea -> {
-                    for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
-                        final Task task = taskDisplayArea.getStackAt(sNdx);
+                    for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) {
+                        final Task task = taskDisplayArea.getRootTaskAt(sNdx);
                         if (activityTypes != null
                                 && !ArrayUtils.contains(activityTypes, task.getActivityType())) {
                             continue;
diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java
index c976bc2..855dd7e 100644
--- a/services/core/java/com/android/server/wm/TaskPersister.java
+++ b/services/core/java/com/android/server/wm/TaskPersister.java
@@ -30,26 +30,28 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 
 import libcore.io.IoUtils;
 
 import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
-import java.io.StringWriter;
+import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -305,12 +307,9 @@
                 continue;
             }
 
-            BufferedReader reader = null;
             boolean deleteFile = false;
-            try {
-                reader = new BufferedReader(new FileReader(taskFile));
-                final XmlPullParser in = Xml.newPullParser();
-                in.setInput(reader);
+            try (InputStream is = new FileInputStream(taskFile)) {
+                final TypedXmlPullParser in = Xml.resolvePullParser(is);
 
                 int event;
                 while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
@@ -360,7 +359,6 @@
                 Slog.e(TAG, "Failing file: " + fileToString(taskFile));
                 deleteFile = true;
             } finally {
-                IoUtils.closeQuietly(reader);
                 if (deleteFile) {
                     if (DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName());
                     taskFile.delete();
@@ -513,11 +511,10 @@
             mService = service;
         }
 
-        private StringWriter saveToXml(Task task) throws Exception {
+        private byte[] saveToXml(Task task) throws Exception {
             if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
-            final XmlSerializer xmlSerializer = new FastXmlSerializer();
-            StringWriter stringWriter = new StringWriter();
-            xmlSerializer.setOutput(stringWriter);
+            final ByteArrayOutputStream os = new ByteArrayOutputStream();
+            final TypedXmlSerializer xmlSerializer = Xml.resolveSerializer(os);
 
             if (DEBUG) {
                 xmlSerializer.setFeature(
@@ -534,13 +531,13 @@
             xmlSerializer.endDocument();
             xmlSerializer.flush();
 
-            return stringWriter;
+            return os.toByteArray();
         }
 
         @Override
         public void process() {
             // Write out one task.
-            StringWriter stringWriter = null;
+            byte[] data = null;
             Task task = mTask;
             if (DEBUG) Slog.d(TAG, "Writing task=" + task);
             synchronized (mService.mGlobalLock) {
@@ -548,12 +545,12 @@
                     // Still there.
                     try {
                         if (DEBUG) Slog.d(TAG, "Saving task=" + task);
-                        stringWriter = saveToXml(task);
+                        data = saveToXml(task);
                     } catch (Exception e) {
                     }
                 }
             }
-            if (stringWriter != null) {
+            if (data != null) {
                 // Write out xml file while not holding mService lock.
                 FileOutputStream file = null;
                 AtomicFile atomicFile = null;
@@ -567,8 +564,7 @@
                     atomicFile = new AtomicFile(new File(userTasksDir,
                             String.valueOf(task.mTaskId) + TASK_FILENAME_SUFFIX));
                     file = atomicFile.startWrite();
-                    file.write(stringWriter.toString().getBytes());
-                    file.write('\n');
+                    file.write(data);
                     atomicFile.finishWrite(file);
                 } catch (IOException e) {
                     if (file != null) {
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 7d61c19..7809cbc 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -502,7 +502,8 @@
 
     private void findWallpaperTarget() {
         mFindResults.reset();
-        if (mDisplayContent.getDefaultTaskDisplayArea().isStackVisible(WINDOWING_MODE_FREEFORM)) {
+        if (mDisplayContent.getDefaultTaskDisplayArea()
+                .isRootTaskVisible(WINDOWING_MODE_FREEFORM)) {
             // In freeform mode we set the wallpaper as its own target, so we don't need an
             // additional window to make it visible.
             mFindResults.setUseTopWallpaperAsTarget(true);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index a1bb89d..cf6468d 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1139,18 +1139,15 @@
      * Called when this container or one of its descendants changed its requested orientation, and
      * wants this container to handle it or pass it to its parent.
      *
-     * @param freezeDisplayToken freeze this app window token if display needs to freeze
      * @param requestingContainer the container which orientation request has changed
      * @return {@code true} if handled; {@code false} otherwise.
      */
-    boolean onDescendantOrientationChanged(@Nullable IBinder freezeDisplayToken,
-            @Nullable WindowContainer requestingContainer) {
+    boolean onDescendantOrientationChanged(@Nullable WindowContainer requestingContainer) {
         final WindowContainer parent = getParent();
         if (parent == null) {
             return false;
         }
-        return parent.onDescendantOrientationChanged(freezeDisplayToken,
-                requestingContainer);
+        return parent.onDescendantOrientationChanged(requestingContainer);
     }
 
     /**
@@ -1224,14 +1221,13 @@
     }
 
     /**
-     * Calls {@link #setOrientation(int, IBinder, ActivityRecord)} with {@code null} to the last 2
+     * Calls {@link #setOrientation(int, IBinder, WindowContainer)} with {@code null} to the last 2
      * parameters.
      *
      * @param orientation the specified orientation.
      */
     void setOrientation(int orientation) {
-        setOrientation(orientation, null /* freezeDisplayToken */,
-                null /* ActivityRecord */);
+        setOrientation(orientation, null /* requestingContainer */);
     }
 
     /**
@@ -1240,14 +1236,10 @@
      *
      * @param orientation the specified orientation. Needs to be one of {@link
      *      android.content.pm.ActivityInfo.ScreenOrientation}.
-     * @param freezeDisplayToken uses this token to freeze display if orientation change is not
-     *                           done. Display will not be frozen if this is {@code null}, which
-     *                           should only happen in tests.
      * @param requestingContainer the container which orientation request has changed. Mostly used
      *                            to ensure it gets correct configuration.
      */
-    void setOrientation(int orientation, @Nullable IBinder freezeDisplayToken,
-            @Nullable WindowContainer requestingContainer) {
+    void setOrientation(int orientation, @Nullable WindowContainer requestingContainer) {
         if (mOrientation == orientation) {
             return;
         }
@@ -1259,7 +1251,7 @@
                 // Resolve the requested orientation.
                 onConfigurationChanged(parent.getConfiguration());
             }
-            onDescendantOrientationChanged(freezeDisplayToken, requestingContainer);
+            onDescendantOrientationChanged(requestingContainer);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java
index b0c5dbc..a5ebf9a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerConstants.java
+++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java
@@ -23,7 +23,7 @@
 import android.provider.DeviceConfig;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.wm.utils.DeviceConfigInterface;
+import com.android.server.utils.DeviceConfigInterface;
 
 import java.io.PrintWriter;
 import java.util.Objects;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d62df85..f8b5914 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -199,6 +199,7 @@
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.provider.Settings;
+import android.service.attestation.ImpressionToken;
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
 import android.sysprop.SurfaceFlingerProperties;
@@ -288,8 +289,8 @@
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
 import com.android.server.power.ShutdownThread;
+import com.android.server.utils.DeviceConfigInterface;
 import com.android.server.utils.PriorityDump;
-import com.android.server.wm.utils.DeviceConfigInterface;
 
 import java.io.BufferedWriter;
 import java.io.DataInputStream;
@@ -767,6 +768,8 @@
     final EmbeddedWindowController mEmbeddedWindowController;
     final AnrController mAnrController;
 
+    private final ImpressionAttestationController mImpressionAttestationController;
+
     @VisibleForTesting
     final class SettingsObserver extends ContentObserver {
         private final Uri mDisplayInversionEnabledUri =
@@ -1367,6 +1370,7 @@
         mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(
                 mContext.getResources());
 
+        mImpressionAttestationController = new ImpressionAttestationController(mContext);
         setGlobalShadowSettings();
         mAnrController = new AnrController(this);
         mStartingSurfaceController = new StartingSurfaceController(this);
@@ -2918,7 +2922,7 @@
     }
 
     void getStackBounds(int windowingMode, int activityType, Rect bounds) {
-        final Task stack = mRoot.getStack(windowingMode, activityType);
+        final Task stack = mRoot.getRootTask(windowingMode, activityType);
         if (stack != null) {
             stack.getBounds(bounds);
             return;
@@ -8428,4 +8432,64 @@
             SystemClock.sleep(durationMs);
         }
     }
+
+    @Override
+    public String[] getSupportedImpressionAlgorithms() {
+        return mImpressionAttestationController.getSupportedImpressionAlgorithms();
+    }
+
+    @Override
+    public boolean verifyImpressionToken(ImpressionToken impressionToken) {
+        return mImpressionAttestationController.verifyImpressionToken(impressionToken);
+    }
+
+    ImpressionToken generateImpressionToken(Session session, IWindow window,
+            Rect boundsInWindow, String hashAlgorithm) {
+        final SurfaceControl displaySurfaceControl;
+        final Rect boundsInDisplay = new Rect(boundsInWindow);
+        synchronized (mGlobalLock) {
+            final WindowState win = windowForClientLocked(session, window, false);
+            if (win == null) {
+                Slog.w(TAG, "Failed to generate impression token. Invalid window");
+                return null;
+            }
+
+            DisplayContent displayContent = win.getDisplayContent();
+            if (displayContent == null) {
+                Slog.w(TAG, "Failed to generate impression token. Window is not on a display");
+                return null;
+            }
+
+            displaySurfaceControl = displayContent.getSurfaceControl();
+            mImpressionAttestationController.calculateImpressionTokenBoundsLocked(win,
+                    boundsInWindow, boundsInDisplay);
+
+            if (boundsInDisplay.isEmpty()) {
+                Slog.w(TAG, "Failed to generate impression token. Bounds are not on screen");
+                return null;
+            }
+        }
+
+        // A screenshot of the entire display is taken rather than just the window. This is
+        // because if we take a screenshot of the window, it will not include content that might
+        // be covering it with the same uid. We want to make sure we include content that's
+        // covering to ensure we get as close as possible to what the user sees
+        final int uid = session.mUid;
+        SurfaceControl.LayerCaptureArgs args =
+                new SurfaceControl.LayerCaptureArgs.Builder(displaySurfaceControl)
+                        .setUid(uid)
+                        .setSourceCrop(boundsInDisplay)
+                        .build();
+
+        SurfaceControl.ScreenshotHardwareBuffer screenshotHardwareBuffer =
+                SurfaceControl.captureLayers(args);
+        if (screenshotHardwareBuffer == null
+                || screenshotHardwareBuffer.getHardwareBuffer() == null) {
+            Slog.w(TAG, "Failed to generate impression token. Failed to take screenshot");
+            return null;
+        }
+
+        return mImpressionAttestationController.generateImpressionToken(
+                screenshotHardwareBuffer.getHardwareBuffer(), boundsInWindow, hashAlgorithm);
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index ce61d50..05b1e425 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -101,4 +101,9 @@
     public boolean canProfileOwnerResetPasswordWhenLocked(int userId) {
         return false;
     }
+
+    public boolean hasKeyPair(String callerPackage, String alias) {
+        // STOPSHIP: implement delegation code in ArcDevicePolicyManagerWrapperService & nuke this.
+        return false;
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 81ce8ff..01b8c0b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -233,6 +233,7 @@
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.provider.Telephony;
+import android.security.AppUriAuthenticationPolicy;
 import android.security.IKeyChainAliasCallback;
 import android.security.IKeyChainService;
 import android.security.KeyChain;
@@ -987,29 +988,21 @@
     }
 
     /**
-     * Checks if the feature is supported and it's safe to execute the given {@code operation}.
-     *
-     * <p>Typically called at the beginning of each API method as:
-     *
-     * <pre><code>
-     *
-     * if (!canExecute(operation, permission)) return;
-     *
-     * </code></pre>
-     *
-     * @return {@code true} when it's safe to execute, {@code false} when the feature is not
-     * supported or the caller does not have the given {@code requiredPermission}.
+     * Checks if it's safe to execute the given {@code operation}.
      *
      * @throws UnsafeStateException if it's not safe to execute the operation.
      */
-    boolean canExecute(@DevicePolicyOperation int operation, @NonNull String requiredPermission) {
-        if (!mHasFeature && !hasCallingPermission(requiredPermission)) {
-            return false;
+    private void checkCanExecuteOrThrowUnsafe(@DevicePolicyOperation int operation) {
+        if (!canExecute(operation)) {
+            throw mSafetyChecker.newUnsafeStateException(operation);
         }
-        if (mSafetyChecker == null || mSafetyChecker.isDevicePolicyOperationSafe(operation)) {
-            return true;
-        }
-        throw mSafetyChecker.newUnsafeStateException(operation);
+    }
+
+    /**
+     * Returns whether it's safe to execute the given {@code operation}.
+     */
+    boolean canExecute(@DevicePolicyOperation int operation) {
+        return mSafetyChecker == null || mSafetyChecker.isDevicePolicyOperationSafe(operation);
     }
 
     /**
@@ -1162,6 +1155,10 @@
             return LocalServices.getService(PersistentDataBlockManagerInternal.class);
         }
 
+        AppOpsManager getAppOpsManager() {
+            return mContext.getSystemService(AppOpsManager.class);
+        }
+
         LockSettingsInternal getLockSettingsInternal() {
             return LocalServices.getService(LockSettingsInternal.class);
         }
@@ -4203,11 +4200,7 @@
             int maxRequiredComplexity = PASSWORD_COMPLEXITY_NONE;
             for (ActiveAdmin admin : admins) {
                 adminMetrics.add(admin.mPasswordPolicy.getMinMetrics());
-                if (isDeviceOwner(admin) || isProfileOwnerUncheckedLocked(admin.info.getComponent(),
-                        admin.getUserHandle().getIdentifier())) {
-                    maxRequiredComplexity = Math.max(maxRequiredComplexity,
-                            admin.mPasswordComplexity);
-                }
+                maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity);
             }
             return PasswordMetrics.validatePasswordMetrics(PasswordMetrics.merge(adminMetrics),
                     maxRequiredComplexity, false, metrics).isEmpty();
@@ -4308,25 +4301,29 @@
                     updatePasswordValidityCheckpointLocked(caller.getUserId(), calledOnParent);
                     updatePasswordQualityCacheForUserGroup(caller.getUserId());
                     saveSettingsLocked(caller.getUserId());
-                    //TODO: Log password complexity change if security logging is enabled.
                 });
             }
+            logPasswordComplexityRequiredIfSecurityLogEnabled(admin.info.getComponent(),
+                    caller.getUserId(), calledOnParent, passwordComplexity);
         }
         //TODO: Log metrics.
     }
 
+    private void logPasswordComplexityRequiredIfSecurityLogEnabled(ComponentName who, int userId,
+            boolean parent, int complexity) {
+        if (SecurityLog.isLoggingEnabled()) {
+            final int affectedUserId = parent ? getProfileParentId(userId) : userId;
+            SecurityLog.writeEvent(SecurityLog.TAG_PASSWORD_COMPLEXITY_REQUIRED,
+                    who.getPackageName(), userId, affectedUserId, complexity);
+        }
+    }
+
     private int getEffectivePasswordComplexityRequirementLocked(@UserIdInt int userHandle) {
         ensureLocked();
         List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle);
         int maxRequiredComplexity = PASSWORD_COMPLEXITY_NONE;
         for (ActiveAdmin admin : admins) {
-            final ComponentName adminComponent = admin.info.getComponent();
-            final int adminUser = admin.getUserHandle().getIdentifier();
-            // Password complexity is only taken into account from DO/PO
-            if (isDeviceOwner(adminComponent, adminUser)
-                    || isProfileOwnerUncheckedLocked(adminComponent, adminUser)) {
-                maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity);
-            }
+            maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity);
         }
         return maxRequiredComplexity;
     }
@@ -4351,6 +4348,21 @@
     }
 
     @Override
+    public int getAggregatedPasswordComplexityForUser(int userId) {
+        if (!mHasFeature) {
+            return PASSWORD_COMPLEXITY_NONE;
+        }
+
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
+
+        synchronized (getLockObject()) {
+            return getEffectivePasswordComplexityRequirementLocked(userId);
+        }
+    }
+
+
+    @Override
     public int getCurrentFailedPasswordAttempts(int userHandle, boolean parent) {
         if (!mLockPatternUtils.hasSecureLockScreen()) {
             return 0;
@@ -4830,10 +4842,6 @@
 
     @Override
     public void lockNow(int flags, boolean parent) {
-        if (!canExecute(DevicePolicyManager.OPERATION_LOCK_NOW, permission.LOCK_DEVICE)) {
-            return;
-        }
-
         final CallerIdentity caller = getCallerIdentity();
 
         final int callingUserId = caller.getUserId();
@@ -4846,6 +4854,7 @@
                     DeviceAdminInfo.USES_POLICY_FORCE_LOCK,
                     parent,
                     android.Manifest.permission.LOCK_DEVICE);
+            checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_LOCK_NOW);
             final long ident = mInjector.binderClearCallingIdentity();
             try {
                 adminComponent = admin == null ? null : admin.info.getComponent();
@@ -5027,7 +5036,8 @@
         final CallerIdentity caller = getCallerIdentity(who, callerPackage);
         Preconditions.checkCallAuthorization((caller.hasAdminComponent()
                 && (isProfileOwner(caller) || isDeviceOwner(caller)))
-                || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)));
+                || (caller.hasPackage() && (isCallerDelegate(caller, DELEGATION_CERT_INSTALL)
+                || isCredentialManagementApp(caller, alias, isUserSelectable))));
 
         final long id = mInjector.binderClearCallingIdentity();
         try {
@@ -5067,7 +5077,8 @@
         final CallerIdentity caller = getCallerIdentity(who, callerPackage);
         Preconditions.checkCallAuthorization((caller.hasAdminComponent()
                 && (isProfileOwner(caller) || isDeviceOwner(caller)))
-                || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)));
+                || (caller.hasPackage() && (isCallerDelegate(caller, DELEGATION_CERT_INSTALL)
+                || isCredentialManagementApp(caller, alias))));
 
         final long id = Binder.clearCallingIdentity();
         try {
@@ -5096,6 +5107,30 @@
     }
 
     @Override
+    public boolean hasKeyPair(String callerPackage, String alias) {
+        final CallerIdentity caller = getCallerIdentity(callerPackage);
+        Preconditions.checkCallAuthorization(canManageCertificates(caller));
+
+        return mInjector.binderWithCleanCallingIdentity(() -> {
+            try (KeyChainConnection keyChainConnection =
+                         KeyChain.bindAsUser(mContext, caller.getUserHandle())) {
+                return keyChainConnection.getService().containsKeyPair(alias);
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "Querying keypair", e);
+            } catch (InterruptedException e) {
+                Log.w(LOG_TAG, "Interrupted while querying keypair", e);
+                Thread.currentThread().interrupt();
+            }
+            return false;
+        });
+    }
+
+    private boolean canManageCertificates(CallerIdentity caller) {
+        return isProfileOwner(caller) || isDeviceOwner(caller)
+                || isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
+    }
+
+    @Override
     public boolean setKeyGrantForApp(ComponentName who, String callerPackage, String alias,
             String packageName, boolean hasGrant) {
         Preconditions.checkStringNotEmpty(alias, "Alias to grant cannot be empty");
@@ -5173,9 +5208,7 @@
          */
         if (hasProfileOwner(caller.getUserId())) {
             // Make sure that the caller is the profile owner or delegate.
-            Preconditions.checkCallAuthorization(
-                    isDeviceOwner(caller) || isProfileOwner(caller) || isCallerDelegate(
-                            caller, DELEGATION_CERT_INSTALL));
+            Preconditions.checkCallAuthorization(canManageCertificates(caller));
             // Verify that the managed profile is on an organization-owned device and as such
             // the profile owner can access Device IDs.
             if (isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId())) {
@@ -5249,7 +5282,8 @@
         } else {
             Preconditions.checkCallAuthorization((caller.hasAdminComponent()
                     && (isProfileOwner(caller) || isDeviceOwner(caller)))
-                    || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)));
+                    || (caller.hasPackage() && (isCallerDelegate(caller, DELEGATION_CERT_INSTALL)
+                    || isCredentialManagementApp(caller, alias))));
         }
 
         // As the caller will be granted access to the key, ensure no UID was specified, as
@@ -5345,7 +5379,8 @@
         final CallerIdentity caller = getCallerIdentity(who, callerPackage);
         Preconditions.checkCallAuthorization((caller.hasAdminComponent()
                 && (isProfileOwner(caller) || isDeviceOwner(caller)))
-                || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)));
+                || (caller.hasPackage() && (isCallerDelegate(caller, DELEGATION_CERT_INSTALL)
+                || isCredentialManagementApp(caller, alias))));
 
         final long id = mInjector.binderClearCallingIdentity();
         try (final KeyChainConnection keyChainConnection =
@@ -5780,6 +5815,70 @@
         }
     }
 
+    /**
+     * Check whether a caller application is the credential management app, which can access
+     * privileged APIs.
+     * <p>
+     * This is done by checking that the calling package is authorized to perform the app operation
+     * {@link android.app.AppOpsManager#OP_MANAGE_CREDENTIALS}. The alias provided must be contained
+     * in the aliases specified in the credential management app's authentication policy. The
+     * key pair to install must not be user selectable.
+     *
+     * @param caller the calling identity
+     * @return {@code true} if the calling process is the credential management app.
+     */
+    private boolean isCredentialManagementApp(CallerIdentity caller, String alias,
+            boolean isUserSelectable) {
+        // Should not be user selectable
+        if (isUserSelectable) {
+            Log.e(LOG_TAG, "The credential management app is not allowed to install a "
+                    + "user selectable key pair");
+            return false;
+        }
+        return isCredentialManagementApp(caller, alias);
+    }
+
+    /**
+     * Check whether a caller application is the credential mangement app, which can access
+     * privileged APIs.
+     * <p>
+     * This is done by checking that the calling package is authorized to perform the app operation
+     * {@link android.app.AppOpsManager#OP_MANAGE_CREDENTIALS}. The alias provided must be contained
+     * in the aliases specified in the credential management app's authentication policy.
+     *
+     * @param caller the calling identity
+     * @return {@code true} if the calling process is the credential management app.
+     */
+    private boolean isCredentialManagementApp(CallerIdentity caller, String alias) {
+        // Should include alias in authentication policy
+        try (KeyChainConnection connection = KeyChain.bindAsUser(mContext,
+                caller.getUserHandle())) {
+            if (!containsAlias(connection.getService().getCredentialManagementAppPolicy(), alias)) {
+                return false;
+            }
+        } catch (RemoteException | InterruptedException e) {
+            return false;
+        }
+
+        AppOpsManager appOpsManager = mInjector.getAppOpsManager();
+        return appOpsManager != null
+                ? appOpsManager.noteOpNoThrow(AppOpsManager.OP_MANAGE_CREDENTIALS, caller.getUid(),
+                caller.getPackageName(), null, null) == AppOpsManager.MODE_ALLOWED
+                : false;
+    }
+
+    private static boolean containsAlias(AppUriAuthenticationPolicy policy, String alias) {
+        for (Map.Entry<String, Map<Uri, String>> appsToUris :
+                policy.getAppAndUriMappings().entrySet()) {
+            for (Map.Entry<Uri, String> urisToAliases : appsToUris.getValue().entrySet()) {
+                if (urisToAliases.getValue().equals(alias)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     @Override
     public void setCertInstallerPackage(ComponentName who, String installerPackage)
             throws SecurityException {
@@ -7389,6 +7488,12 @@
                     admin.getPackageName(), userId, "set-device-owner");
 
             Slog.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId);
+
+            if (mInjector.userManagerIsHeadlessSystemUserMode()) {
+                Slog.i(LOG_TAG, "manageUser: " + admin + " on user " + userId);
+
+                manageUser(admin, admin, caller.getUserId(), null);
+            }
             return true;
         }
     }
@@ -9361,6 +9466,8 @@
         Preconditions.checkCallAuthorization(caller.getUserHandle().isSystem(),
                 "createAndManageUser was called from non-system user");
         Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+        checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_CREATE_AND_MANAGE_USER);
+
         final boolean ephemeral = (flags & DevicePolicyManager.MAKE_USER_EPHEMERAL) != 0;
         final boolean demo = (flags & DevicePolicyManager.MAKE_USER_DEMO) != 0
                 && UserManager.isDeviceInDemoMode(mContext);
@@ -9440,29 +9547,7 @@
 
         final long id = mInjector.binderClearCallingIdentity();
         try {
-            final String adminPkg = admin.getPackageName();
-            try {
-                // Install the profile owner if not present.
-                if (!mIPackageManager.isPackageAvailable(adminPkg, userHandle)) {
-                    mIPackageManager.installExistingPackageAsUser(adminPkg, userHandle,
-                            PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
-                            PackageManager.INSTALL_REASON_POLICY, null);
-                }
-            } catch (RemoteException e) {
-                // Does not happen, same process
-            }
-
-            // Set admin.
-            setActiveAdmin(profileOwner, true, userHandle);
-            final String ownerName = getProfileOwnerName(Process.myUserHandle().getIdentifier());
-            setProfileOwner(profileOwner, ownerName, userHandle);
-
-            synchronized (getLockObject()) {
-                DevicePolicyData policyData = getUserData(userHandle);
-                policyData.mInitBundle = adminExtras;
-                policyData.mAdminBroadcastPending = true;
-                saveSettingsLocked(userHandle);
-            }
+            manageUser(admin, profileOwner, userHandle, adminExtras);
 
             if ((flags & DevicePolicyManager.SKIP_SETUP_WIZARD) != 0) {
                 Settings.Secure.putIntForUser(mContext.getContentResolver(),
@@ -9483,12 +9568,53 @@
         }
     }
 
+    private void manageUser(ComponentName admin, ComponentName profileOwner,
+            @UserIdInt int userId, PersistableBundle adminExtras) {
+        // Check for permission
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(canManageUsers(caller));
+        Preconditions.checkCallAuthorization(
+                hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+        mInjector.binderWithCleanCallingIdentity(() ->
+                    manageUserNoCheck(admin, profileOwner, userId, adminExtras));
+    }
+
+    private void manageUserNoCheck(ComponentName admin, ComponentName profileOwner,
+            int user, PersistableBundle adminExtras) {
+
+        final String adminPkg = admin.getPackageName();
+        try {
+            // Install the profile owner if not present.
+            if (!mIPackageManager.isPackageAvailable(adminPkg, user)) {
+                mIPackageManager.installExistingPackageAsUser(adminPkg, user,
+                        PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
+                        PackageManager.INSTALL_REASON_POLICY, null);
+            }
+        } catch (RemoteException e) {
+            // Does not happen, same process
+        }
+
+        // Set admin.
+        setActiveAdmin(profileOwner, true, user);
+        final String ownerName = getProfileOwnerName(Process.myUserHandle().getIdentifier());
+        setProfileOwner(profileOwner, ownerName, user);
+
+        synchronized (getLockObject()) {
+            DevicePolicyData policyData = getUserData(user);
+            policyData.mInitBundle = adminExtras;
+            policyData.mAdminBroadcastPending = true;
+
+            saveSettingsLocked(user);
+        }
+    }
+
     @Override
     public boolean removeUser(ComponentName who, UserHandle userHandle) {
         Objects.requireNonNull(who, "ComponentName is null");
         Objects.requireNonNull(userHandle, "UserHandle is null");
         final CallerIdentity caller = getCallerIdentity(who);
         Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+        checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REMOVE_USER);
 
         return mInjector.binderWithCleanCallingIdentity(() -> {
             String restriction = isManagedProfile(userHandle.getIdentifier())
@@ -9522,6 +9648,7 @@
         Objects.requireNonNull(who, "ComponentName is null");
         final CallerIdentity caller = getCallerIdentity(who);
         Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+        checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SWITCH_USER);
 
         synchronized (getLockObject()) {
             long id = mInjector.binderClearCallingIdentity();
@@ -9546,6 +9673,7 @@
         Objects.requireNonNull(userHandle, "UserHandle is null");
         final CallerIdentity caller = getCallerIdentity(who);
         Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+        checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_START_USER_IN_BACKGROUND);
 
         final int userId = userHandle.getIdentifier();
         if (isManagedProfile(userId)) {
@@ -9579,6 +9707,7 @@
         Objects.requireNonNull(userHandle, "UserHandle is null");
         final CallerIdentity caller = getCallerIdentity(who);
         Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+        checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_STOP_USER);
 
         final int userId = userHandle.getIdentifier();
         if (isManagedProfile(userId)) {
@@ -11574,6 +11703,27 @@
         public ComponentName getProfileOwnerAsUser(int userHandle) {
             return DevicePolicyManagerService.this.getProfileOwnerAsUser(userHandle);
         }
+
+        @Override
+        public boolean isDeviceOrProfileOwnerInCallingUser(String packageName) {
+            return isDeviceOwnerInCallingUser(packageName)
+                    || isProfileOwnerInCallingUser(packageName);
+        }
+
+        private boolean isDeviceOwnerInCallingUser(String packageName) {
+            final ComponentName deviceOwnerInCallingUser =
+                    DevicePolicyManagerService.this.getDeviceOwnerComponent(
+                            /* callingUserOnly= */ true);
+            return deviceOwnerInCallingUser != null
+                    && packageName.equals(deviceOwnerInCallingUser.getPackageName());
+        }
+
+        private boolean isProfileOwnerInCallingUser(String packageName) {
+            final ComponentName profileOwnerInCallingUser =
+                    getProfileOwnerAsUser(UserHandle.getCallingUserId());
+            return profileOwnerInCallingUser != null
+                    && packageName.equals(profileOwnerInCallingUser.getPackageName());
+        }
     }
 
     private Intent createShowAdminSupportIntent(ComponentName admin, int userId) {
@@ -12135,8 +12285,6 @@
                 if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
                     return CODE_USER_SETUP_COMPLETED;
                 }
-            }  else {
-                // STOPSHIP Do proper check in split user mode
             }
             return CODE_OK;
         }
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 7c3b7a6..49a41f0 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -74,7 +74,14 @@
 
     @Override
     public void onStart() {
-        publishBinderService(Context.PEOPLE_SERVICE, mService);
+        onStart(/* isForTesting= */ false);
+    }
+
+    @VisibleForTesting
+    protected void onStart(boolean isForTesting) {
+        if (!isForTesting) {
+            publishBinderService(Context.PEOPLE_SERVICE, mService);
+        }
         publishLocalService(PeopleServiceInternal.class, new LocalService());
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
index e48b671..9f895c6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
@@ -35,6 +35,8 @@
 import android.os.HandlerThread;
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import androidx.test.InstrumentationRegistry;
@@ -181,7 +183,7 @@
 
         boolean parse() {
             try (FileInputStream stream = new FileInputStream(mFile)) {
-                XmlPullParser parser = Xml.newPullParser();
+                TypedXmlPullParser parser = Xml.newFastPullParser();
                 parser.setInput(stream, StandardCharsets.UTF_8.name());
                 int type;
                 while ((type = parser.next()) != XmlPullParser.START_TAG
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 0c2fab8..343b156 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -51,7 +51,6 @@
         "testng",
         "junit",
         "platform-compat-test-rules",
-
     ],
 
     aidl: {
@@ -117,6 +116,7 @@
         "utils/**/*.java",
         "utils/**/*.kt",
         "utils-mockito/**/*.kt",
+        ":services.core-sources-deviceconfig-interface",
     ],
     static_libs: [
         "junit",
@@ -133,6 +133,7 @@
         "utils/**/*.java",
         "utils/**/*.kt",
         "utils-mockito/**/*.kt",
+        ":services.core-sources-deviceconfig-interface",
     ],
     static_libs: [
         "junit",
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index b9c2e56..3f3d5e5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -17,7 +17,6 @@
 package com.android.server.accessibility;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -42,7 +41,6 @@
 import android.provider.Settings;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.view.Display;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
 import androidx.test.InstrumentationRegistry;
@@ -208,34 +206,6 @@
     }
 
     @SmallTest
-    public void testOnMagnificationScaleChanged_MagnificationCapabilitiesAll_showButton() {
-        // Request showing magnification button if the magnification capability is all mode.
-        mA11yms.mUserStates.get(
-                mA11yms.getCurrentUserIdLocked()).setMagnificationCapabilitiesLocked(
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
-
-        mA11yms.onMagnificationScaleChanged(Display.DEFAULT_DISPLAY,
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-
-        verify(mMockWindowMagnificationMgr).showMagnificationButton(Display.DEFAULT_DISPLAY,
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-    }
-
-    @SmallTest
-    public void testOnMagnificationScaleChanged_MagnificationCapabilitiesNotAll_NoAction() {
-        // Do nothing if the magnification capability is not all mode.
-        mA11yms.mUserStates.get(
-                mA11yms.getCurrentUserIdLocked()).setMagnificationCapabilitiesLocked(
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-
-        mA11yms.onMagnificationScaleChanged(Display.DEFAULT_DISPLAY,
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-
-        verify(mMockWindowMagnificationMgr, never()).showMagnificationButton(anyInt(),
-                anyInt());
-    }
-
-    @SmallTest
     public void testOnMagnificationTransitionFailed_capabilitiesIsAll_fallBackToPreviousMode() {
         final AccessibilityUserState userState = mA11yms.mUserStates.get(
                 mA11yms.getCurrentUserIdLocked());
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index e43a002..c038a0f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -54,7 +54,6 @@
 import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.accessibility.EventStreamTransformation;
 import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationRequestObserver;
-import com.android.server.accessibility.magnification.MagnificationGestureHandler.ScaleChangedListener;
 import com.android.server.testutils.OffsettableClock;
 import com.android.server.testutils.TestHandler;
 import com.android.server.wm.WindowManagerInternal;
@@ -125,7 +124,7 @@
     private Context mContext;
     FullScreenMagnificationController mFullScreenMagnificationController;
     @Mock
-    ScaleChangedListener mMockScaleChangedListener;
+    MagnificationGestureHandler.ScaleChangedListener mMockScaleChangedListener;
     @Mock
     MagnificationRequestObserver mMagnificationRequestObserver;
     @Mock
@@ -181,8 +180,8 @@
             boolean detectShortcutTrigger) {
         FullScreenMagnificationGestureHandler h = new FullScreenMagnificationGestureHandler(
                 mContext, mFullScreenMagnificationController, mMockScaleChangedListener,
-                detectTripleTap, detectShortcutTrigger, mWindowMagnificationPromptController,
-                DISPLAY_0);
+                detectTripleTap, detectShortcutTrigger,
+                mWindowMagnificationPromptController, DISPLAY_0);
         mHandler = new TestHandler(h.mDetectingState, mClock) {
             @Override
             protected String messageToString(Message m) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index aed590b..cba618b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -29,6 +29,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -106,8 +107,8 @@
                         mock(WindowMagnificationManager.Callback.class)));
         mMockConnection = new MockWindowMagnificationConnection(true);
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mMagnificationController = new MagnificationController(mService, new Object(), mContext,
-                mScreenMagnificationController, mWindowMagnificationManager);
+        mMagnificationController = spy(new MagnificationController(mService, new Object(), mContext,
+                mScreenMagnificationController, mWindowMagnificationManager));
     }
 
     @After
@@ -277,8 +278,32 @@
 
         verify(mWindowMagnificationManager).setScale(eq(TEST_DISPLAY), eq(newScale));
         verify(mWindowMagnificationManager).persistScale(eq(TEST_DISPLAY));
-        verify(mService).onMagnificationScaleChanged(eq(TEST_DISPLAY),
-                eq(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
+        verify(mMagnificationController).onMagnificationScaleChanged(eq(TEST_DISPLAY),
+                eq(MODE_WINDOW));
+    }
+
+    @Test
+    public void onMagnificationScaleChanged_capabilitiesAllMode_showMagnificationButton()
+            throws RemoteException {
+        mMagnificationController.setMagnificationCapabilities(
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+
+        mMagnificationController.onMagnificationScaleChanged(TEST_DISPLAY, MODE_WINDOW);
+
+        verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
+                eq(MODE_WINDOW));
+    }
+
+    @Test
+    public void onMagnificationScaleChanged_capabilitiesNotAllMode_notShowMagnificationButton()
+            throws RemoteException {
+        mMagnificationController.setMagnificationCapabilities(
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+
+        mMagnificationController.onMagnificationScaleChanged(TEST_DISPLAY, MODE_WINDOW);
+
+        verify(mWindowMagnificationManager, never()).showMagnificationButton(eq(TEST_DISPLAY),
+                eq(MODE_WINDOW));
     }
 
     private void setMagnificationEnabled(int mode) throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index 41b6e98..9f930da 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -32,7 +32,6 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.accessibility.EventStreamTransformation;
-import com.android.server.accessibility.magnification.MagnificationGestureHandler.ScaleChangedListener;
 import com.android.server.accessibility.utils.TouchEventGenerator;
 
 import org.junit.After;
@@ -74,7 +73,8 @@
                 mock(WindowMagnificationManager.Callback.class));
         mMockConnection = new MockWindowMagnificationConnection();
         mWindowMagnificationGestureHandler = new WindowMagnificationGestureHandler(
-                mContext, mWindowMagnificationManager, mock(ScaleChangedListener.class),
+                mContext, mWindowMagnificationManager, mock(
+                MagnificationGestureHandler.ScaleChangedListener.class),
                 /** detectTripleTap= */true,   /** detectShortcutTrigger= */true, DISPLAY_0);
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
         mWindowMagnificationGestureHandler.setNext(strictMock(EventStreamTransformation.class));
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java
index 3aa5a80..d58d71f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java
@@ -23,6 +23,8 @@
 
 import android.app.admin.FactoryResetProtectionPolicy;
 import android.os.Parcel;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -98,7 +100,7 @@
             throws Exception {
         ByteArrayOutputStream outStream = serialize(policy);
         ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray());
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new InputStreamReader(inStream));
         assertThat(parser.next()).isEqualTo(XmlPullParser.START_TAG);
 
@@ -109,7 +111,7 @@
             throws Exception {
         ByteArrayOutputStream outStream = serialize(policy);
         ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray());
-        XmlPullParser parser = mock(XmlPullParser.class);
+        TypedXmlPullParser parser = mock(TypedXmlPullParser.class);
         when(parser.next()).thenThrow(XmlPullParserException.class);
         parser.setInput(new InputStreamReader(inStream));
 
@@ -120,7 +122,7 @@
     private ByteArrayOutputStream serialize(FactoryResetProtectionPolicy policy)
             throws IOException {
         ByteArrayOutputStream outStream = new ByteArrayOutputStream();
-        final XmlSerializer outXml = new FastXmlSerializer();
+        final TypedXmlSerializer outXml = Xml.newFastSerializer();
         outXml.setOutput(outStream, StandardCharsets.UTF_8.name());
         outXml.startDocument(null, true);
         outXml.startTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java
index 0a9aad7..1308a3e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java
@@ -29,6 +29,8 @@
 import android.app.admin.FreezePeriod;
 import android.app.admin.SystemUpdatePolicy;
 import android.os.Parcel;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -471,7 +473,7 @@
 
         // Test XML serialization
         ByteArrayOutputStream outStream = new ByteArrayOutputStream();
-        final XmlSerializer outXml = new FastXmlSerializer();
+        final TypedXmlSerializer outXml = Xml.newFastSerializer();
         outXml.setOutput(outStream, StandardCharsets.UTF_8.name());
         outXml.startDocument(null, true);
         outXml.startTag(null, "ota");
@@ -481,7 +483,7 @@
         outXml.flush();
 
         ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray());
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new InputStreamReader(inStream));
         assertThat(parser.next()).isEqualTo(XmlPullParser.START_TAG);
         checkFreezePeriods(SystemUpdatePolicy.restoreFromXml(parser), expectedPeriods);
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 325ba11..cb5ca04 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -16,49 +16,97 @@
 
 package com.android.server.display;
 
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
+
+import static com.android.server.display.DisplayModeDirector.Vote.PRIORITY_FLICKER;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
+import android.content.ContentResolver;
 import android.content.Context;
+import android.content.ContextWrapper;
+import android.database.ContentObserver;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
 import android.os.Handler;
 import android.os.Looper;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
 
-import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
 import com.android.server.display.DisplayModeDirector.BrightnessObserver;
 import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs;
 import com.android.server.display.DisplayModeDirector.Vote;
+import com.android.server.testutils.FakeDeviceConfigInterface;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DisplayModeDirectorTest {
     // The tolerance within which we consider something approximately equals.
+    private static final String TAG = "DisplayModeDirectorTest";
+    private static final boolean DEBUG = false;
     private static final float FLOAT_TOLERANCE = 0.01f;
 
     private Context mContext;
+    private FakesInjector mInjector;
+    private Handler mHandler;
+    @Rule
+    public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+        final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext);
+        when(mContext.getContentResolver()).thenReturn(resolver);
+        mInjector = new FakesInjector();
+        mHandler = new Handler(Looper.getMainLooper());
     }
 
     private DisplayModeDirector createDirectorFromRefreshRateArray(
             float[] refreshRates, int baseModeId) {
         DisplayModeDirector director =
-                new DisplayModeDirector(mContext, new Handler(Looper.getMainLooper()));
+                new DisplayModeDirector(mContext, mHandler, mInjector);
         int displayId = 0;
         Display.Mode[] modes = new Display.Mode[refreshRates.length];
         for (int i = 0; i < refreshRates.length; i++) {
@@ -159,9 +207,9 @@
     }
 
     @Test
-    public void testBrightnessHasLowerPriorityThanUser() {
-        assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE);
-        assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_SIZE);
+    public void testFlickerHasLowerPriorityThanUser() {
+        assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE);
+        assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_SIZE);
 
         int displayId = 0;
         DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
@@ -169,7 +217,7 @@
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
         votesByDisplay.put(displayId, votes);
         votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
-        votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
+        votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
         director.injectVotesByDisplay(votesByDisplay);
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
@@ -177,7 +225,7 @@
 
         votes.clear();
         votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
-        votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
+        votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90));
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
@@ -185,7 +233,7 @@
 
         votes.clear();
         votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90));
-        votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
+        votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
@@ -193,7 +241,7 @@
 
         votes.clear();
         votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 60));
-        votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
+        votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90));
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
@@ -202,10 +250,10 @@
 
     @Test
     public void testAppRequestRefreshRateRange() {
-        // Confirm that the app request range doesn't include low brightness or min refresh rate
-        // settings, but does include everything else.
+        // Confirm that the app request range doesn't include flicker or min refresh rate settings,
+        // but does include everything else.
         assertTrue(
-                Vote.PRIORITY_LOW_BRIGHTNESS < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
+                PRIORITY_FLICKER < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
         assertTrue(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE
                 < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
         assertTrue(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE
@@ -216,7 +264,7 @@
         SparseArray<Vote> votes = new SparseArray<>();
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
         votesByDisplay.put(displayId, votes);
-        votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
+        votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
         director.injectVotesByDisplay(votesByDisplay);
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
@@ -310,7 +358,7 @@
         SparseArray<Vote> votes = new SparseArray<>();
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
         votesByDisplay.put(displayId, votes);
-        votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(0, 60));
+        votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(0, 60));
         votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, Vote.forRefreshRates(60, 90));
         votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90));
         votes.put(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, Vote.forRefreshRates(60, 60));
@@ -398,4 +446,343 @@
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         assertThat(desiredSpecs.allowGroupSwitching).isTrue();
     }
+
+    @Test
+    public void testBrightnessObserverGetsUpdatedRefreshRatesForZone() {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
+        SensorManager sensorManager = createMockSensorManager(createLightSensor());
+
+        final int initialRefreshRate = 60;
+        mInjector.getDeviceConfig().setRefreshRateInLowZone(initialRefreshRate);
+        director.start(sensorManager);
+        assertThat(director.getBrightnessObserver().getRefreshRateInLowZone())
+                .isEqualTo(initialRefreshRate);
+
+        final int updatedRefreshRate = 90;
+        mInjector.getDeviceConfig().setRefreshRateInLowZone(updatedRefreshRate);
+        // Need to wait for the property change to propagate to the main thread.
+        waitForIdleSync();
+        assertThat(director.getBrightnessObserver().getRefreshRateInLowZone())
+                .isEqualTo(updatedRefreshRate);
+    }
+
+    @Test
+    public void testBrightnessObserverThresholdsInZone() {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
+        SensorManager sensorManager = createMockSensorManager(createLightSensor());
+
+        final int[] initialDisplayThresholds = { 10 };
+        final int[] initialAmbientThresholds = { 20 };
+
+        final FakeDeviceConfig config = mInjector.getDeviceConfig();
+        config.setLowDisplayBrightnessThresholds(initialDisplayThresholds);
+        config.setLowAmbientBrightnessThresholds(initialAmbientThresholds);
+        director.start(sensorManager);
+
+        assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds())
+                .isEqualTo(initialDisplayThresholds);
+        assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds())
+                .isEqualTo(initialAmbientThresholds);
+
+        final int[] updatedDisplayThresholds = { 9, 14 };
+        final int[] updatedAmbientThresholds = { -1, 19 };
+        config.setLowDisplayBrightnessThresholds(updatedDisplayThresholds);
+        config.setLowAmbientBrightnessThresholds(updatedAmbientThresholds);
+        // Need to wait for the property change to propagate to the main thread.
+        waitForIdleSync();
+        assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds())
+                .isEqualTo(updatedDisplayThresholds);
+        assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds())
+                .isEqualTo(updatedAmbientThresholds);
+    }
+
+    @Test
+    public void testLockFpsForLowZone() throws Exception {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+        setPeakRefreshRate(90);
+        director.getSettingsObserver().setDefaultRefreshRate(90);
+        director.getBrightnessObserver().setDefaultDisplayState(true);
+
+        final FakeDeviceConfig config = mInjector.getDeviceConfig();
+        config.setRefreshRateInLowZone(90);
+        config.setLowDisplayBrightnessThresholds(new int[] { 10 });
+        config.setLowAmbientBrightnessThresholds(new int[] { 20 });
+
+        Sensor lightSensor = createLightSensor();
+        SensorManager sensorManager = createMockSensorManager(lightSensor);
+
+        director.start(sensorManager);
+
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
+                .registerListener(
+                        listenerCaptor.capture(),
+                        eq(lightSensor),
+                        anyInt(),
+                        any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        setBrightness(10);
+        // Sensor reads 20 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/));
+
+        Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+        assertVoteForRefreshRateLocked(vote, 90 /*fps*/);
+
+        setBrightness(125);
+        // Sensor reads 1000 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000 /*lux*/));
+
+        vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+        assertThat(vote).isNull();
+    }
+
+    @Test
+    public void testLockFpsForHighZone() throws Exception {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+        setPeakRefreshRate(90 /*fps*/);
+        director.getSettingsObserver().setDefaultRefreshRate(90);
+        director.getBrightnessObserver().setDefaultDisplayState(true);
+
+        final FakeDeviceConfig config = mInjector.getDeviceConfig();
+        config.setRefreshRateInHighZone(60);
+        config.setHighDisplayBrightnessThresholds(new int[] { 255 });
+        config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
+
+        Sensor lightSensor = createLightSensor();
+        SensorManager sensorManager = createMockSensorManager(lightSensor);
+
+        director.start(sensorManager);
+
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
+                .registerListener(
+                        listenerCaptor.capture(),
+                        eq(lightSensor),
+                        anyInt(),
+                        any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        setBrightness(100);
+        // Sensor reads 2000 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
+
+        Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+        assertThat(vote).isNull();
+
+        setBrightness(255);
+        // Sensor reads 9000 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000));
+
+        vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+        assertVoteForRefreshRateLocked(vote, 60 /*fps*/);
+    }
+
+    private void assertVoteForRefreshRateLocked(Vote vote, float refreshRate) {
+        assertThat(vote).isNotNull();
+        final DisplayModeDirector.RefreshRateRange expectedRange =
+                new DisplayModeDirector.RefreshRateRange(refreshRate, refreshRate);
+        assertThat(vote.refreshRateRange).isEqualTo(expectedRange);
+    }
+
+    private static class FakeDeviceConfig extends FakeDeviceConfigInterface {
+        @Override
+        public String getProperty(String namespace, String name) {
+            Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace));
+            return super.getProperty(namespace, name);
+        }
+
+        @Override
+        public void addOnPropertiesChangedListener(
+                String namespace,
+                Executor executor,
+                DeviceConfig.OnPropertiesChangedListener listener) {
+            Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace));
+            super.addOnPropertiesChangedListener(namespace, executor, listener);
+        }
+
+        void setRefreshRateInLowZone(int fps) {
+            putPropertyAndNotify(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_LOW_ZONE,
+                    String.valueOf(fps));
+        }
+
+        void setLowDisplayBrightnessThresholds(int[] brightnessThresholds) {
+            String thresholds = toPropertyValue(brightnessThresholds);
+
+            if (DEBUG) {
+                Slog.e(TAG, "Brightness Thresholds = " + thresholds);
+            }
+
+            putPropertyAndNotify(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS,
+                    thresholds);
+        }
+
+        void setLowAmbientBrightnessThresholds(int[] ambientThresholds) {
+            String thresholds = toPropertyValue(ambientThresholds);
+
+            if (DEBUG) {
+                Slog.e(TAG, "Ambient Thresholds = " + thresholds);
+            }
+
+            putPropertyAndNotify(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS,
+                    thresholds);
+        }
+
+        void setRefreshRateInHighZone(int fps) {
+            putPropertyAndNotify(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_HIGH_ZONE,
+                    String.valueOf(fps));
+        }
+
+        void setHighDisplayBrightnessThresholds(int[] brightnessThresholds) {
+            String thresholds = toPropertyValue(brightnessThresholds);
+
+            if (DEBUG) {
+                Slog.e(TAG, "Brightness Thresholds = " + thresholds);
+            }
+
+            putPropertyAndNotify(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS,
+                    thresholds);
+        }
+
+        void setHighAmbientBrightnessThresholds(int[] ambientThresholds) {
+            String thresholds = toPropertyValue(ambientThresholds);
+
+            if (DEBUG) {
+                Slog.e(TAG, "Ambient Thresholds = " + thresholds);
+            }
+
+            putPropertyAndNotify(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS,
+                    thresholds);
+        }
+
+        @NonNull
+        private static String toPropertyValue(@NonNull int[] intArray) {
+            return Arrays.stream(intArray)
+                    .mapToObj(Integer::toString)
+                    .collect(Collectors.joining(","));
+        }
+    }
+
+    private void setBrightness(int brightness) {
+        Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS,
+                brightness);
+        mInjector.notifyBrightnessChanged();
+        waitForIdleSync();
+    }
+
+    private void setPeakRefreshRate(float fps) {
+        Settings.System.putFloat(mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE,
+                 fps);
+        mInjector.notifyPeakRefreshRateChanged();
+        waitForIdleSync();
+    }
+
+    private static SensorManager createMockSensorManager(Sensor... sensors) {
+        SensorManager sensorManager = Mockito.mock(SensorManager.class);
+        when(sensorManager.getSensorList(anyInt())).then((invocation) -> {
+            List<Sensor> requestedSensors = new ArrayList<>();
+            int type = invocation.getArgument(0);
+            for (Sensor sensor : sensors) {
+                if (sensor.getType() == type || type == Sensor.TYPE_ALL) {
+                    requestedSensors.add(sensor);
+                }
+            }
+            return requestedSensors;
+        });
+
+        when(sensorManager.getDefaultSensor(anyInt())).then((invocation) -> {
+            int type = invocation.getArgument(0);
+            for (Sensor sensor : sensors) {
+                if (sensor.getType() == type) {
+                    return sensor;
+                }
+            }
+            return null;
+        });
+        return sensorManager;
+    }
+
+    private static Sensor createLightSensor() {
+        try {
+            return TestUtils.createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT);
+        } catch (Exception e) {
+            // There's nothing we can do if this fails, just throw a RuntimeException so that we
+            // don't have to mark every function that might call this as throwing Exception
+            throw new RuntimeException("Failed to create a light sensor", e);
+        }
+    }
+
+    private void waitForIdleSync() {
+        mHandler.runWithScissors(() -> { }, 500 /*timeout*/);
+    }
+
+    static class FakesInjector implements DisplayModeDirector.Injector {
+        private final FakeDeviceConfig mDeviceConfig;
+        private ContentObserver mBrightnessObserver;
+        private ContentObserver mPeakRefreshRateObserver;
+
+        FakesInjector() {
+            mDeviceConfig = new FakeDeviceConfig();
+        }
+
+        @NonNull
+        public FakeDeviceConfig getDeviceConfig() {
+            return mDeviceConfig;
+        }
+
+        @Override
+        public void registerBrightnessObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            if (mBrightnessObserver != null) {
+                throw new IllegalStateException("Tried to register a second brightness observer");
+            }
+            mBrightnessObserver = observer;
+        }
+
+        @Override
+        public void unregisterBrightnessObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            mBrightnessObserver = null;
+        }
+
+        void notifyBrightnessChanged() {
+            if (mBrightnessObserver != null) {
+                mBrightnessObserver.dispatchChange(false /*selfChange*/, DISPLAY_BRIGHTNESS_URI);
+            }
+        }
+
+        @Override
+        public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            mPeakRefreshRateObserver = observer;
+        }
+
+        void notifyPeakRefreshRateChanged() {
+            if (mPeakRefreshRateObserver != null) {
+                mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/,
+                        PEAK_REFRESH_RATE_URI);
+            }
+        }
+
+        @Override
+        public boolean isDeviceInteractive(@NonNull Context context) {
+            return true;
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
index 9ef7557..20f9b70 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
@@ -27,6 +27,8 @@
 
 import android.content.om.OverlayInfo;
 import android.text.TextUtils;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -403,7 +405,7 @@
 
     private int countXmlTags(String xml, String tagToLookFor) throws Exception {
         int count = 0;
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new StringReader(xml));
         int event = parser.getEventType();
         while (event != XmlPullParser.END_DOCUMENT) {
@@ -418,7 +420,7 @@
     private int countXmlAttributesWhere(String xml, String tag, String attr, String value)
             throws Exception {
         int count = 0;
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new StringReader(xml));
         int event = parser.getEventType();
         while (event != XmlPullParser.END_DOCUMENT) {
diff --git a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
index 9213e1f..a112b14 100644
--- a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
@@ -69,7 +69,7 @@
         when(mCallback.asBinder()).thenReturn(new Binder());
 
         PeopleService service = new PeopleService(mContext);
-        service.onStart();
+        service.onStart(/* isForTesting= */ true);
 
         mServiceInternal = LocalServices.getService(PeopleServiceInternal.class);
         mLocalService = (PeopleService.LocalService) mServiceInternal;
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
index bbb83b6..11d00f0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -29,6 +29,8 @@
 import android.util.Slog;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import androidx.test.runner.AndroidJUnit4;
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
index 89c3d50..90658055 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
@@ -25,6 +25,8 @@
 import android.content.pm.PackageParser;
 import android.content.pm.Signature;
 import android.util.TypedXmlPullParser;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import androidx.test.InstrumentationRegistry;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 194ae05..23fcf70 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -98,6 +98,8 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.frameworks.servicestests.R;
@@ -8695,7 +8697,7 @@
 
         // Write ShareTargets to Xml
         ByteArrayOutputStream outStream = new ByteArrayOutputStream();
-        final XmlSerializer outXml = new FastXmlSerializer();
+        final TypedXmlSerializer outXml = Xml.newFastSerializer();
         outXml.setOutput(outStream, StandardCharsets.UTF_8.name());
         outXml.startDocument(null, true);
         for (int i = 0; i < expectedValues.size(); i++) {
@@ -8706,7 +8708,7 @@
 
         // Read ShareTargets from Xml
         ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray());
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new InputStreamReader(inStream));
         List<ShareTargetInfo> shareTargets = new ArrayList<>();
         int type;
diff --git a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
index 0f9bf2f..8bccce1 100644
--- a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
+++ b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
@@ -367,7 +367,7 @@
          * for you if needed; if you already have this information around, it can
          * be much more efficient to supply it here.
          * 
-         * @return Returns an XmlPullParser allowing you to parse out the XML
+         * @return Returns an TypedXmlPullParser allowing you to parse out the XML
          * data.  Returns null if the xml resource could not be found for any
          * reason.
          */
diff --git a/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java b/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java
index 1d62e01..7ac4938 100644
--- a/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java
@@ -21,6 +21,8 @@
 import android.app.usage.CacheQuotaHint;
 import android.test.AndroidTestCase;
 import android.util.Pair;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
 
 import com.android.internal.util.FastXmlSerializer;
 
@@ -37,12 +39,12 @@
 @RunWith(JUnit4.class)
 public class CacheQuotaStrategyTest extends AndroidTestCase {
     StringWriter mWriter;
-    FastXmlSerializer mOut;
+    TypedXmlSerializer mOut;
 
     @Before
     public void setUp() throws Exception {
         mWriter = new StringWriter();
-        mOut = new FastXmlSerializer();
+        mOut = Xml.newFastSerializer();
         mOut.setOutput(mWriter);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/FakeDeviceConfigInterface.java b/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
similarity index 92%
rename from services/tests/wmtests/src/com/android/server/wm/utils/FakeDeviceConfigInterface.java
rename to services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
index 2904a5b..a67f645 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/FakeDeviceConfigInterface.java
+++ b/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.wm.utils;
+package com.android.server.testutils;
 
 import android.annotation.NonNull;
 import android.provider.DeviceConfig;
@@ -22,6 +22,7 @@
 import android.util.Pair;
 
 import com.android.internal.util.Preconditions;
+import com.android.server.utils.DeviceConfigInterface;
 
 import java.lang.reflect.Constructor;
 import java.util.HashMap;
@@ -122,6 +123,19 @@
     }
 
     @Override
+    public float getFloat(String namespace, String name, float defaultValue) {
+        String value = getProperty(namespace, name);
+        if (value == null) {
+            return defaultValue;
+        }
+        try {
+            return Float.parseFloat(value);
+        } catch (NumberFormatException e) {
+            return defaultValue;
+        }
+    }
+
+    @Override
     public boolean getBoolean(String namespace, String name, boolean defaultValue) {
         String value = getProperty(namespace, name);
         return value != null ? Boolean.parseBoolean(value) : defaultValue;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index c507928..4a4d9bc 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -54,6 +54,8 @@
 import android.util.ArraySet;
 import android.util.IntArray;
 import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.internal.util.FastXmlSerializer;
@@ -267,7 +269,7 @@
                     mIpm, approvalLevel);
 
             // approved services aren't in xml
-            XmlPullParser parser = Xml.newPullParser();
+            TypedXmlPullParser parser = Xml.newFastPullParser();
             parser.setInput(new BufferedInputStream(new ByteArrayInputStream(new byte[]{})),
                     null);
             writeExpectedValuesToSettings(approvalLevel);
@@ -335,7 +337,7 @@
             String testComponent = "user.test.component/C1";
             String resolvedValue =
                     (approvalLevel == APPROVAL_BY_COMPONENT) ? testComponent : testPackage;
-            XmlPullParser parser =
+            TypedXmlPullParser parser =
                     getParserWithEntries(service, getXmlEntry(resolvedValue, 0, true));
 
             service.readXml(parser, null, true, 10);
@@ -357,7 +359,7 @@
             String resolvedValue =
                     (approvalLevel == APPROVAL_BY_COMPONENT) ? testComponent : testPackage;
             String xmlEntry = getXmlEntry(resolvedValue, 0, true, false);
-            XmlPullParser parser = getParserWithEntries(service, xmlEntry);
+            TypedXmlPullParser parser = getParserWithEntries(service, xmlEntry);
 
             service.readXml(parser, null, true, 0);
 
@@ -384,7 +386,7 @@
         ManagedServices service2 =
                 new TestManagedServices(
                         getContext(), mLock, mUserProfiles, mIpm, APPROVAL_BY_COMPONENT);
-        XmlSerializer serializer = new FastXmlSerializer();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         BufferedOutputStream outStream = new BufferedOutputStream(baos);
         serializer.setOutput(outStream, "utf-8");
@@ -396,7 +398,7 @@
         serializer.endDocument();
         outStream.flush();
 
-        final XmlPullParser parser = Xml.newPullParser();
+        final TypedXmlPullParser parser = Xml.newFastPullParser();
         BufferedInputStream input = new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray()));
 
@@ -536,7 +538,7 @@
                     service,
                     Collections.singletonList(service.getPackageName(resolvedValue10)),
                     10);
-            XmlPullParser parser =
+            TypedXmlPullParser parser =
                     getParserWithEntries(
                             service,
                             getXmlEntry(resolvedValue0, 0, true),
@@ -544,7 +546,7 @@
             service.readXml(parser, null, false, UserHandle.USER_ALL);
 
             // Write backup.
-            XmlSerializer serializer = new FastXmlSerializer();
+            TypedXmlSerializer serializer = Xml.newFastSerializer();
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
             serializer.startDocument(null, true);
@@ -557,7 +559,7 @@
             service.setPackageOrComponentEnabled(resolvedValue10, 10, true, false);
 
             // Parse backup via restore.
-            XmlPullParser restoreParser = Xml.newPullParser();
+            TypedXmlPullParser restoreParser = Xml.newFastPullParser();
             restoreParser.setInput(
                     new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), null);
             restoreParser.nextTag();
@@ -608,7 +610,7 @@
                 addExpectedServices(service, entriesExpectedToHaveServices, userInfo.id);
             }
 
-            XmlSerializer serializer = new FastXmlSerializer();
+            TypedXmlSerializer serializer = Xml.newFastSerializer();
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
             serializer.startDocument(null, true);
@@ -618,7 +620,7 @@
             serializer.endDocument();
             serializer.flush();
 
-            XmlPullParser parser = Xml.newPullParser();
+            TypedXmlPullParser parser = Xml.newFastPullParser();
             parser.setInput(new BufferedInputStream(
                     new ByteArrayInputStream(baos.toByteArray())), null);
             parser.nextTag();
@@ -640,7 +642,7 @@
                     mIpm, approvalLevel);
             loadXml(service);
 
-            XmlSerializer serializer = new FastXmlSerializer();
+            TypedXmlSerializer serializer = Xml.newFastSerializer();
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
             serializer.startDocument(null, true);
@@ -666,7 +668,7 @@
                     mIpm, approvalLevel);
             loadXml(service);
 
-            XmlSerializer serializer = new FastXmlSerializer();
+            TypedXmlSerializer serializer = Xml.newFastSerializer();
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
             serializer.startDocument(null, true);
@@ -674,7 +676,7 @@
             serializer.endDocument();
             serializer.flush();
 
-            XmlPullParser parser = Xml.newPullParser();
+            TypedXmlPullParser parser = Xml.newFastPullParser();
             byte[] rawOutput = baos.toByteArray();
             parser.setInput(new BufferedInputStream(
                     new ByteArrayInputStream(rawOutput)), null);
@@ -1387,7 +1389,7 @@
 
     private void loadXml(ManagedServices service) throws Exception {
         String xmlString = createXml(service);
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xmlString.getBytes())), null);
         parser.nextTag();
@@ -1423,7 +1425,7 @@
         return xml.toString();
     }
 
-    private XmlPullParser getParserWithEntries(ManagedServices service, String... xmlEntries)
+    private TypedXmlPullParser getParserWithEntries(ManagedServices service, String... xmlEntries)
             throws Exception {
         final StringBuffer xml = new StringBuffer();
         xml.append("<" + service.getConfig().xmlTag + ">\n");
@@ -1432,7 +1434,7 @@
         }
         xml.append("</" + service.getConfig().xmlTag + ">");
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.toString().getBytes())), null);
         parser.nextTag();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index f649911..8c2038b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -36,6 +36,8 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.IntArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.server.UiServiceTestCase;
@@ -126,7 +128,7 @@
                 + "<service_listing approved=\"b/b\" user=\"10\" primary=\"true\" />"
                 + "</enabled_assistants>";
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.toString().getBytes())), null);
         parser.nextTag();
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 09a4289..6083237 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -160,6 +160,8 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 import android.widget.RemoteViews;
 
@@ -3446,7 +3448,7 @@
                 new BufferedInputStream(new ByteArrayInputStream(upgradeXml.getBytes())),
                 false,
                 UserHandle.USER_ALL);
-        verify(mSnoozeHelper, times(1)).readXml(any(XmlPullParser.class), anyLong());
+        verify(mSnoozeHelper, times(1)).readXml(any(TypedXmlPullParser.class), anyLong());
     }
 
     @Test
@@ -3887,12 +3889,12 @@
         NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
         channel.setSound(Uri.EMPTY, null);
 
-        XmlSerializer serializer = new FastXmlSerializer();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         channel.writeXmlForBackup(serializer, getContext());
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         NotificationChannel restored = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
@@ -3915,7 +3917,7 @@
         NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
         channel.setVibrationPattern(new long[0]);
 
-        XmlSerializer serializer = new FastXmlSerializer();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         channel.writeXml(serializer);
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 7ec8689..98c4a2d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -99,6 +99,8 @@
 import android.util.ArraySet;
 import android.util.Pair;
 import android.util.StatsEvent;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import androidx.test.InstrumentationRegistry;
@@ -280,7 +282,7 @@
     private ByteArrayOutputStream writeXmlAndPurge(
             String pkg, int uid, boolean forBackup, int userId, String... channelIds)
             throws Exception {
-        XmlSerializer serializer = new FastXmlSerializer();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         serializer.startDocument(null, true);
@@ -300,7 +302,7 @@
 
     private void loadByteArrayXml(byte[] byteArray, boolean forRestore, int userId)
             throws Exception {
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null);
         parser.nextTag();
         mHelper.readXml(parser, forRestore, userId);
@@ -717,7 +719,7 @@
         mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG_N_MR1}, new int[]{
                 UID_N_MR1});
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
                 null);
         parser.nextTag();
@@ -772,7 +774,7 @@
                 + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" visibility=\""
                 + Notification.VISIBILITY_PRIVATE + "\" />\n"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())),
                 null);
         parser.nextTag();
@@ -2559,7 +2561,7 @@
                 + " importance=\"3\"/>"
                 + "</package>"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
                 null);
         parser.nextTag();
@@ -2580,7 +2582,7 @@
                 + " importance=\"3\"/>"
                 + "</package>"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
                 null);
         parser.nextTag();
@@ -2612,7 +2614,7 @@
                 + " importance=\"3\"/>"
                 + "</package>"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
                 null);
         parser.nextTag();
@@ -2753,7 +2755,7 @@
                 + "<channel id=\"b\" name=\"b\" importance=\"3\"/>"
                 + "</package>"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
                 null);
         parser.nextTag();
@@ -2779,7 +2781,7 @@
                 + "<channel id=\"c\" name=\"c\" importance=\"3\"/>"
                 + "</package>"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
                 null);
         parser.nextTag();
@@ -3047,7 +3049,7 @@
         toAdd.add(new Pair(PKG_O, UID_O));
         mHelper.updateDefaultApps(UserHandle.getUserId(UID_O), null, toAdd);
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
                 null);
         parser.nextTag();
@@ -3121,7 +3123,7 @@
                 + "<channel id=\"" + extraChannel1 + "\" name=\"hi\" importance=\"3\"/>"
                 + "</package>"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
                 null);
         parser.nextTag();
@@ -3154,12 +3156,12 @@
                 + "</ranking>";
 
         // trigger a restore for both users
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xmlUser0.getBytes())),
                 null);
         parser.nextTag();
         mHelper.readXml(parser, true, 0);
-        parser = Xml.newPullParser();
+        parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xmlUser10.getBytes())),
                 null);
         parser.nextTag();
@@ -3242,7 +3244,7 @@
                 + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"foo:placeholder_id\"/>"
                 + "</package>"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
                 null);
         parser.nextTag();
@@ -3261,7 +3263,7 @@
                 + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"other\"/>"
                 + "</package>"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
                 null);
         parser.nextTag();
@@ -3280,7 +3282,7 @@
                 + "<channel id=\"id\" name=\"hi\" importance=\"3\"/>"
                 + "</package>"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
                 null);
         parser.nextTag();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 3deeea2..35b224a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -43,6 +43,8 @@
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.IntArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -95,7 +97,7 @@
                 + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" "
                 + "pkg=\"pkg\" key=\"key2\" time=\"" + max_time_str + "\"/>"
                 + "</snoozed-notifications>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml_string.getBytes())), null);
         mSnoozeHelper.readXml(parser, 1);
@@ -114,12 +116,12 @@
                 + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" "
                 + "pkg=\"pkg\" key=\"key2\" time=\"" + max_time_str + "\"/>"
                 + "</snoozed-notifications>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml_string.getBytes())), null);
         mSnoozeHelper.readXml(parser, 1);
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        XmlSerializer serializer = new FastXmlSerializer();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         serializer.startDocument(null, true);
         mSnoozeHelper.writeXml(serializer);
@@ -137,7 +139,7 @@
                 + "<context version=\"1\" user-id=\"0\" notification=\"notification\" "
                 + "pkg=\"pkg\" key=\"key2\" id=\"uri\"/>"
                 + "</snoozed-notifications>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml_string.getBytes())), null);
         mSnoozeHelper.readXml(parser, 1);
@@ -151,7 +153,7 @@
             throws XmlPullParserException, IOException {
         NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
         mSnoozeHelper.snooze(r, 999999999);
-        XmlSerializer serializer = new FastXmlSerializer();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         serializer.startDocument(null, true);
@@ -159,7 +161,7 @@
         serializer.endDocument();
         serializer.flush();
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), "utf-8");
         mSnoozeHelper.readXml(parser, 1);
@@ -175,7 +177,7 @@
         NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
         mSnoozeHelper.snooze(r, 0);
        // Thread.sleep(100);
-        XmlSerializer serializer = new FastXmlSerializer();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         serializer.startDocument(null, true);
@@ -183,7 +185,7 @@
         serializer.endDocument();
         serializer.flush();
         Thread.sleep(10);
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), "utf-8");
         mSnoozeHelper.readXml(parser, 2);
@@ -227,7 +229,7 @@
                 + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" "
                 + "pkg=\"pkg\" key=\"key2\" time=\"" + 15+ "\"/>"
                 + "</snoozed-notifications>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml_string.getBytes())), null);
         mSnoozeHelper.readXml(parser, 4);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index 013a994..5262465 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -30,6 +30,8 @@
 import android.service.notification.ZenModeConfig.EventInfo;
 import android.service.notification.ZenPolicy;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -199,7 +201,7 @@
         rule.name = "name";
         rule.snoozing = true;
 
-        XmlSerializer out = new FastXmlSerializer();
+        TypedXmlSerializer out = Xml.newFastSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         out.setOutput(new BufferedOutputStream(baos), "utf-8");
         out.startDocument(null, true);
@@ -208,7 +210,7 @@
         out.endTag(null, tag);
         out.endDocument();
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 3430dbd..cfdd246 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -95,6 +95,8 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.StatsEvent;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.internal.R;
@@ -189,14 +191,14 @@
                 + "&amp;end=7.0&amp;exitAtAlarm=true\"/>"
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
         return new XmlResourceParserImpl(parser);
     }
 
     private ByteArrayOutputStream writeXmlAndPurge(Integer version) throws Exception {
-        XmlSerializer serializer = new FastXmlSerializer();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         serializer.startDocument(null, true);
@@ -209,7 +211,7 @@
 
     private ByteArrayOutputStream writeXmlAndPurgeForUser(Integer version, int userId)
             throws Exception {
-        XmlSerializer serializer = new FastXmlSerializer();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         serializer.startDocument(null, true);
@@ -222,8 +224,8 @@
         return baos;
     }
 
-    private XmlPullParser getParserForByteStream(ByteArrayOutputStream baos) throws Exception {
-        XmlPullParser parser = Xml.newPullParser();
+    private TypedXmlPullParser getParserForByteStream(ByteArrayOutputStream baos) throws Exception {
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(
                 new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
@@ -837,7 +839,7 @@
         ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy();
 
         ByteArrayOutputStream baos = writeXmlAndPurge(null);
-        XmlPullParser parser = getParserForByteStream(baos);
+        TypedXmlPullParser parser = getParserForByteStream(baos);
         mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL);
 
         assertEquals("Config mismatch: current vs expected: "
@@ -962,7 +964,7 @@
         mZenModeHelperSpy.mConfigs.put(11, newConfig11);
 
         // Parse backup data.
-        XmlPullParser parser = getParserForByteStream(baos);
+        TypedXmlPullParser parser = getParserForByteStream(baos);
         mZenModeHelperSpy.readXml(parser, true, 10);
         mZenModeHelperSpy.readXml(parser, true, 11);
 
@@ -980,7 +982,7 @@
         ZenModeConfig original = mZenModeHelperSpy.mConfig.copy();
 
         ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM);
-        XmlPullParser parser = getParserForByteStream(baos);
+        TypedXmlPullParser parser = getParserForByteStream(baos);
         mZenModeHelperSpy.readXml(parser, true, UserHandle.USER_SYSTEM);
 
         assertEquals("Config mismatch: current vs original: "
@@ -1000,7 +1002,7 @@
         ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM);
 
         // Restore data for user 10.
-        XmlPullParser parser = getParserForByteStream(baos);
+        TypedXmlPullParser parser = getParserForByteStream(baos);
         mZenModeHelperSpy.readXml(parser, true, 10);
 
         ZenModeConfig actual = mZenModeHelperSpy.mConfigs.get(10);
@@ -1045,7 +1047,7 @@
         ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy();
 
         ByteArrayOutputStream baos = writeXmlAndPurge(null);
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
@@ -1086,7 +1088,7 @@
         ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy();
 
         ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM);
-        XmlPullParser parser = getParserForByteStream(baos);
+        TypedXmlPullParser parser = getParserForByteStream(baos);
         mZenModeHelperSpy.readXml(parser, true, UserHandle.USER_SYSTEM);
 
         ZenModeConfig.ZenRule original = expected.automaticRules.get(ruleId);
@@ -1113,7 +1115,7 @@
 
         // set previous version
         ByteArrayOutputStream baos = writeXmlAndPurge(5);
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
@@ -1133,7 +1135,7 @@
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
@@ -1149,7 +1151,7 @@
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
 
-        parser = Xml.newPullParser();
+        parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
@@ -1168,7 +1170,7 @@
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
@@ -1187,7 +1189,7 @@
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
@@ -1206,7 +1208,7 @@
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
 
-        parser = Xml.newPullParser();
+        parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
@@ -1222,7 +1224,7 @@
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
 
-        parser = Xml.newPullParser();
+        parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
@@ -1242,7 +1244,7 @@
 
         // set previous version
         ByteArrayOutputStream baos = writeXmlAndPurge(5);
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
@@ -1278,7 +1280,7 @@
 
         // set previous version
         ByteArrayOutputStream baos = writeXmlAndPurge(5);
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
@@ -1330,7 +1332,7 @@
 
         // set previous version
         ByteArrayOutputStream baos = writeXmlAndPurge(5);
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
@@ -1399,7 +1401,7 @@
 
         // set previous version
         ByteArrayOutputStream baos = writeXmlAndPurge(5);
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
@@ -1628,12 +1630,12 @@
     }
 
     /**
-     * Wrapper to use XmlPullParser as XmlResourceParser for Resources.getXml()
+     * Wrapper to use TypedXmlPullParser as XmlResourceParser for Resources.getXml()
      */
     final class XmlResourceParserImpl implements XmlResourceParser {
-        private XmlPullParser parser;
+        private TypedXmlPullParser parser;
 
-        public XmlResourceParserImpl(XmlPullParser parser) {
+        public XmlResourceParserImpl(TypedXmlPullParser parser) {
             this.parser = parser;
         }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index 9a6eb1c..99bd0d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -68,16 +68,16 @@
                 mRootWindowContainer.getDefaultDisplay().getDefaultTaskDisplayArea();
         final Task stack =
                 new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build();
-        final Task prevFocusedStack = taskDisplayAreas.getFocusedStack();
+        final Task prevFocusedStack = taskDisplayAreas.getFocusedRootTask();
 
         stack.moveToFront("moveStackToFront");
         // After moving the stack to front, the previous focused should be the last focused.
         assertTrue(stack.isFocusedStackOnDisplay());
-        assertEquals(prevFocusedStack, taskDisplayAreas.getLastFocusedStack());
+        assertEquals(prevFocusedStack, taskDisplayAreas.getLastFocusedRootTask());
 
         stack.moveToBack("moveStackToBack", null /* task */);
         // After moving the stack to back, the stack should be the last focused.
-        assertEquals(stack, taskDisplayAreas.getLastFocusedStack());
+        assertEquals(stack, taskDisplayAreas.getLastFocusedRootTask());
     }
 
     /**
@@ -88,7 +88,7 @@
     public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() {
         // Create a pinned stack and move to front.
         final Task pinnedStack = mRootWindowContainer.getDefaultTaskDisplayArea()
-                .createStack(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
+                .createRootTask(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
         final Task pinnedTask = new TaskBuilder(mAtm.mTaskSupervisor)
                 .setParentTask(pinnedStack).build();
         new ActivityBuilder(mAtm).setActivityFlags(FLAG_ALWAYS_FOCUSABLE)
@@ -108,7 +108,7 @@
     }
 
     /**
-     * Test {@link TaskDisplayArea#mPreferredTopFocusableStack} will be cleared when
+     * Test {@link TaskDisplayArea#mPreferredTopFocusableRootTask} will be cleared when
      * the stack is removed or moved to back, and the focused stack will be according to z-order.
      */
     @Test
@@ -128,7 +128,7 @@
         assertTrue(stack1.isFocusedStackOnDisplay());
 
         // Stack2 should be focused after removing stack1.
-        stack1.getDisplayArea().removeStack(stack1);
+        stack1.getDisplayArea().removeRootTask(stack1);
         assertTrue(stack2.isFocusedStackOnDisplay());
     }
 
@@ -155,12 +155,12 @@
         display.remove();
 
         // The removed display should have no focused stack and its home stack should never resume.
-        assertNull(display.getFocusedStack());
+        assertNull(display.getFocusedRootTask());
         verify(homeStack, never()).resumeTopActivityUncheckedLocked(any(), any());
     }
 
     private Task createFullscreenStackWithSimpleActivityAt(DisplayContent display) {
-        final Task fullscreenStack = display.getDefaultTaskDisplayArea().createStack(
+        final Task fullscreenStack = display.getDefaultTaskDisplayArea().createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP);
         final Task fullscreenTask = new TaskBuilder(mAtm.mTaskSupervisor)
                 .setParentTask(fullscreenStack).build();
@@ -191,7 +191,7 @@
 
         // Move stack with activity to top.
         stack.moveToFront("testStackToFront");
-        assertEquals(stack, display.getFocusedStack());
+        assertEquals(stack, display.getFocusedRootTask());
         assertEquals(activity, display.topRunningActivity());
         assertNull(display.topRunningActivity(true /* considerKeyguardState */));
 
@@ -207,7 +207,7 @@
         // Move empty stack to front. The running activity in focusable stack which below the
         // empty stack should be returned.
         emptyStack.moveToFront("emptyStackToFront");
-        assertEquals(stack, display.getFocusedStack());
+        assertEquals(stack, display.getFocusedRootTask());
         assertTopRunningActivity(showWhenLockedActivity, display);
     }
 
@@ -222,7 +222,7 @@
     @Test
     public void testAlwaysOnTopStackLocation() {
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        final Task alwaysOnTopStack = taskDisplayArea.createStack(WINDOWING_MODE_FREEFORM,
+        final Task alwaysOnTopStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
         final ActivityRecord activity = new ActivityBuilder(mAtm)
                 .setTask(alwaysOnTopStack).build();
@@ -232,31 +232,31 @@
         assertTrue(alwaysOnTopStack.isAlwaysOnTop());
         // Ensure always on top state is synced to the children of the stack.
         assertTrue(alwaysOnTopStack.getTopNonFinishingActivity().isAlwaysOnTop());
-        assertEquals(alwaysOnTopStack, taskDisplayArea.getTopStack());
+        assertEquals(alwaysOnTopStack, taskDisplayArea.getTopRootTask());
 
-        final Task pinnedStack = taskDisplayArea.createStack(
+        final Task pinnedStack = taskDisplayArea.createRootTask(
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         assertEquals(pinnedStack, taskDisplayArea.getRootPinnedTask());
-        assertEquals(pinnedStack, taskDisplayArea.getTopStack());
+        assertEquals(pinnedStack, taskDisplayArea.getTopRootTask());
 
-        final Task anotherAlwaysOnTopStack = taskDisplayArea.createStack(
+        final Task anotherAlwaysOnTopStack = taskDisplayArea.createRootTask(
                 WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         anotherAlwaysOnTopStack.setAlwaysOnTop(true);
         taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopStack,
                 false /* includingParents */);
         assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
-        int topPosition = taskDisplayArea.getStackCount() - 1;
+        int topPosition = taskDisplayArea.getRootTaskCount() - 1;
         // Ensure the new alwaysOnTop stack is put below the pinned stack, but on top of the
         // existing alwaysOnTop stack.
-        assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 1));
+        assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getRootTaskAt(topPosition - 1));
 
-        final Task nonAlwaysOnTopStack = taskDisplayArea.createStack(
+        final Task nonAlwaysOnTopStack = taskDisplayArea.createRootTask(
                 WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         assertEquals(taskDisplayArea, nonAlwaysOnTopStack.getDisplayArea());
-        topPosition = taskDisplayArea.getStackCount() - 1;
+        topPosition = taskDisplayArea.getRootTaskCount() - 1;
         // Ensure the non-alwaysOnTop stack is put below the three alwaysOnTop stacks, but above the
         // existing other non-alwaysOnTop stacks.
-        assertEquals(nonAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 3));
+        assertEquals(nonAlwaysOnTopStack, taskDisplayArea.getRootTaskAt(topPosition - 3));
 
         anotherAlwaysOnTopStack.setAlwaysOnTop(false);
         taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopStack,
@@ -264,37 +264,37 @@
         assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
         // Ensure, when always on top is turned off for a stack, the stack is put just below all
         // other always on top stacks.
-        assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 2));
+        assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getRootTaskAt(topPosition - 2));
         anotherAlwaysOnTopStack.setAlwaysOnTop(true);
 
         // Ensure always on top state changes properly when windowing mode changes.
         anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
-        assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 2));
+        assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getRootTaskAt(topPosition - 2));
         anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
         assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
-        assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 1));
+        assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getRootTaskAt(topPosition - 1));
 
-        final Task dreamStack = taskDisplayArea.createStack(
+        final Task dreamStack = taskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM, true /* onTop */);
         assertEquals(taskDisplayArea, dreamStack.getDisplayArea());
         assertTrue(dreamStack.isAlwaysOnTop());
-        topPosition = taskDisplayArea.getStackCount() - 1;
+        topPosition = taskDisplayArea.getRootTaskCount() - 1;
         // Ensure dream shows above all activities, including PiP
-        assertEquals(dreamStack, taskDisplayArea.getTopStack());
-        assertEquals(pinnedStack, taskDisplayArea.getStackAt(topPosition - 1));
+        assertEquals(dreamStack, taskDisplayArea.getTopRootTask());
+        assertEquals(pinnedStack, taskDisplayArea.getRootTaskAt(topPosition - 1));
 
-        final Task assistStack = taskDisplayArea.createStack(
+        final Task assistStack = taskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
         assertEquals(taskDisplayArea, assistStack.getDisplayArea());
         assertFalse(assistStack.isAlwaysOnTop());
-        topPosition = taskDisplayArea.getStackCount() - 1;
+        topPosition = taskDisplayArea.getRootTaskCount() - 1;
 
         // Ensure Assistant shows as a non-always-on-top activity when config_assistantOnTopOfDream
         // is false and on top of everything when true.
         final boolean isAssistantOnTop = mContext.getResources()
                 .getBoolean(com.android.internal.R.bool.config_assistantOnTopOfDream);
-        assertEquals(assistStack, taskDisplayArea.getStackAt(
+        assertEquals(assistStack, taskDisplayArea.getRootTaskAt(
                     isAssistantOnTop ? topPosition : topPosition - 4));
     }
 
@@ -312,13 +312,13 @@
 
     private void removeStackTests(Runnable runnable) {
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        final Task stack1 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+        final Task stack1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, ON_TOP);
-        final Task stack2 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+        final Task stack2 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, ON_TOP);
-        final Task stack3 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+        final Task stack3 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, ON_TOP);
-        final Task stack4 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+        final Task stack4 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, ON_TOP);
         final Task task1 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(stack1).build();
         final Task task2 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(stack2).build();
@@ -333,7 +333,7 @@
 
         // Removing stacks from the display while removing stacks.
         doAnswer(invocation -> {
-            taskDisplayArea.removeStack(stack2);
+            taskDisplayArea.removeRootTask(stack2);
             return true;
         }).when(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any());
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 53ade0e..28c3184 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -71,6 +71,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.isA;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
 
@@ -154,7 +155,7 @@
         final Task rootTask = activity.getRootTask();
         rootTask.removeChild(task, null /*reason*/);
         // parentTask should be gone on task removal.
-        assertNull(mAtm.mRootWindowContainer.getStack(rootTask.mTaskId));
+        assertNull(mAtm.mRootWindowContainer.getRootTask(rootTask.mTaskId));
     }
 
     @Test
@@ -376,6 +377,27 @@
     }
 
     @Test
+    public void testDestroyedActivityNotScheduleConfigChanged() throws RemoteException {
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setCreateTask(true)
+                .setConfigChanges(CONFIG_ORIENTATION)
+                .build();
+        final Task task = activity.getTask();
+        activity.setState(DESTROYED, "Testing");
+
+        final Configuration newConfig = new Configuration(task.getConfiguration());
+        newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
+                ? ORIENTATION_LANDSCAPE
+                : ORIENTATION_PORTRAIT;
+        task.onRequestedOverrideConfigurationChanged(newConfig);
+
+        ensureActivityConfiguration(activity);
+
+        verify(mAtm.getLifecycleManager(), never())
+                .scheduleTransaction(any(), any(), isA(ActivityConfigurationChangeItem.class));
+    }
+
+    @Test
     public void testSetRequestedOrientationUpdatesConfiguration() throws Exception {
         final ActivityRecord activity = new ActivityBuilder(mAtm)
                 .setCreateTask(true)
@@ -401,7 +423,7 @@
 
         // Mimic the behavior that display doesn't handle app's requested orientation.
         final DisplayContent dc = activity.getTask().getDisplayContent();
-        doReturn(false).when(dc).onDescendantOrientationChanged(any(), any());
+        doReturn(false).when(dc).onDescendantOrientationChanged(any());
         doReturn(false).when(dc).handlesOrientationChangeFromDescendant();
 
         final int requestedOrientation;
@@ -853,7 +875,7 @@
         topRootableTask.moveToFront("test");
         assertTrue(topRootableTask.isTopStackInDisplayArea());
         assertEquals(topRootableTask, topActivityOnNonTopDisplay.getDisplayArea()
-                .mPreferredTopFocusableStack);
+                .mPreferredTopFocusableRootTask);
 
         final ActivityRecord secondaryDisplayActivity =
                 createActivityOnDisplay(false /* defaultDisplay */, null /* process */);
@@ -861,7 +883,7 @@
         topRootableTask.moveToFront("test");
         assertTrue(topRootableTask.isTopStackInDisplayArea());
         assertEquals(topRootableTask,
-                secondaryDisplayActivity.getDisplayArea().mPreferredTopFocusableStack);
+                secondaryDisplayActivity.getDisplayArea().mPreferredTopFocusableRootTask);
 
         // The global top focus activity is on secondary display now.
         // Finish top activity on default display and verify the next preferred top focusable stack
@@ -870,7 +892,7 @@
         topActivityOnNonTopDisplay.finishIfPossible(0 /* resultCode */, null /* resultData */,
                 null /* resultGrants */, "test", false /* oomAdj */);
         assertEquals(task, task.getTopMostTask());
-        assertEquals(task, activity.getDisplayArea().mPreferredTopFocusableStack);
+        assertEquals(task, activity.getDisplayArea().mPreferredTopFocusableRootTask);
     }
 
     /**
@@ -1275,7 +1297,7 @@
     @Test
     public void testDestroyIfPossible() {
         final ActivityRecord activity = createActivityWithTask();
-        doReturn(false).when(mRootWindowContainer).resumeFocusedStacksTopActivities();
+        doReturn(false).when(mRootWindowContainer).resumeFocusedTasksTopActivities();
         activity.destroyIfPossible("test");
 
         assertEquals(DESTROYING, activity.getState());
@@ -1297,7 +1319,7 @@
             homeStack.removeChild(t, "test");
         }, true /* traverseTopToBottom */);
         activity.finishing = true;
-        doReturn(false).when(mRootWindowContainer).resumeFocusedStacksTopActivities();
+        doReturn(false).when(mRootWindowContainer).resumeFocusedTasksTopActivities();
 
         // Try to destroy the last activity above the home stack.
         activity.destroyIfPossible("test");
@@ -1658,7 +1680,7 @@
         final int rotatedOrentation = r.getConfiguration().orientation == ORIENTATION_PORTRAIT
                 ? SCREEN_ORIENTATION_LANDSCAPE
                 : SCREEN_ORIENTATION_PORTRAIT;
-        doReturn(false).when(r).onDescendantOrientationChanged(any(), any());
+        doReturn(false).when(r).onDescendantOrientationChanged(any());
         r.setOrientation(rotatedOrentation);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 8ccbb8f..8e3e668 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -48,7 +48,7 @@
 import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE;
 import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
 import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
-import static com.android.server.wm.TaskDisplayArea.getStackAbove;
+import static com.android.server.wm.TaskDisplayArea.getRootTaskAbove;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
@@ -74,6 +74,8 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.server.wm.TaskDisplayArea.OnRootTaskOrderChangedListener;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -154,7 +156,7 @@
         organizer.setMoveToSecondaryOnEnter(false);
 
         // Create primary splitscreen stack.
-        final Task primarySplitScreen = mDefaultTaskDisplayArea.createStack(
+        final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         // Assert windowing mode.
@@ -179,7 +181,7 @@
     public void testMoveToPrimarySplitScreenThenMoveToBack() {
         TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm);
         // This time, start with a fullscreen activitystack
-        final Task primarySplitScreen = mDefaultTaskDisplayArea.createStack(
+        final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         primarySplitScreen.reparent(organizer.mPrimary, POSITION_TOP,
@@ -205,11 +207,11 @@
         TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm);
         // Set up split-screen with primary on top and secondary containing the home task below
         // another stack.
-        final Task primaryTask = mDefaultTaskDisplayArea.createStack(
+        final Task primaryTask = mDefaultTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final Task homeRoot = mDefaultTaskDisplayArea.getStack(
+        final Task homeRoot = mDefaultTaskDisplayArea.getRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
-        final Task secondaryTask = mDefaultTaskDisplayArea.createStack(
+        final Task secondaryTask = mDefaultTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         mDefaultTaskDisplayArea.positionChildAt(POSITION_TOP, organizer.mPrimary,
                 false /* includingParents */);
@@ -257,7 +259,7 @@
 
     @Test
     public void testStackInheritsDisplayWindowingMode() {
-        final Task primarySplitScreen = mDefaultTaskDisplayArea.createStack(
+        final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
@@ -272,7 +274,7 @@
 
     @Test
     public void testStackOverridesDisplayWindowingMode() {
-        final Task primarySplitScreen = mDefaultTaskDisplayArea.createStack(
+        final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
@@ -365,7 +367,7 @@
         verify(stack2).positionChildAtBottom(any(), eq(false) /* includingParents */);
 
         // Also move display to back because there is only one stack left.
-        taskDisplayArea.removeStack(stack1);
+        taskDisplayArea.removeRootTask(stack1);
         stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.getTopMostTask());
         verify(stack2).positionChildAtBottom(any(), eq(true) /* includingParents */);
     }
@@ -747,8 +749,8 @@
 
         // Ensure that we don't move the home stack if it is already behind the top fullscreen stack
         int homeStackIndex = mDefaultTaskDisplayArea.getIndexOf(homeStack);
-        assertEquals(fullscreenStack, getStackAbove(homeStack));
-        mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(homeStack);
+        assertEquals(fullscreenStack, getRootTaskAbove(homeStack));
+        mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack);
         assertEquals(homeStackIndex, mDefaultTaskDisplayArea.getIndexOf(homeStack));
     }
 
@@ -765,8 +767,8 @@
 
         // Ensure that we don't move the home stack if it is already behind the top fullscreen stack
         int homeStackIndex = mDefaultTaskDisplayArea.getIndexOf(homeStack);
-        assertEquals(fullscreenStack, getStackAbove(homeStack));
-        mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(homeStack);
+        assertEquals(fullscreenStack, getRootTaskAbove(homeStack));
+        mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack);
         assertEquals(homeStackIndex, mDefaultTaskDisplayArea.getIndexOf(homeStack));
     }
 
@@ -783,8 +785,8 @@
 
         // Ensure we don't move the home stack if it is already on top
         int homeStackIndex = mDefaultTaskDisplayArea.getIndexOf(homeStack);
-        assertNull(getStackAbove(homeStack));
-        mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(homeStack);
+        assertNull(getRootTaskAbove(homeStack));
+        mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack);
         assertEquals(homeStackIndex, mDefaultTaskDisplayArea.getIndexOf(homeStack));
     }
 
@@ -807,9 +809,9 @@
 
         // Ensure that we move the home stack behind the bottom most fullscreen stack, ignoring the
         // pinned stack
-        assertEquals(fullscreenStack1, getStackAbove(homeStack));
-        mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(homeStack);
-        assertEquals(fullscreenStack2, getStackAbove(homeStack));
+        assertEquals(fullscreenStack1, getRootTaskAbove(homeStack));
+        mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack);
+        assertEquals(fullscreenStack2, getRootTaskAbove(homeStack));
     }
 
     @Test
@@ -830,9 +832,9 @@
 
         // Ensure that we move the home stack behind the bottom most non-translucent fullscreen
         // stack
-        assertEquals(fullscreenStack1, getStackAbove(homeStack));
-        mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(homeStack);
-        assertEquals(fullscreenStack1, getStackAbove(homeStack));
+        assertEquals(fullscreenStack1, getRootTaskAbove(homeStack));
+        mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack);
+        assertEquals(fullscreenStack1, getRootTaskAbove(homeStack));
     }
 
     @Test
@@ -852,7 +854,7 @@
 
         // Ensure we don't move the home stack behind itself
         int homeStackIndex = mDefaultTaskDisplayArea.getIndexOf(homeStack);
-        mDefaultTaskDisplayArea.moveStackBehindStack(homeStack, homeStack);
+        mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, homeStack);
         assertEquals(homeStackIndex, mDefaultTaskDisplayArea.getIndexOf(homeStack));
     }
 
@@ -873,14 +875,14 @@
         final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
 
-        mDefaultTaskDisplayArea.moveStackBehindStack(homeStack, fullscreenStack1);
-        assertEquals(fullscreenStack1, getStackAbove(homeStack));
-        mDefaultTaskDisplayArea.moveStackBehindStack(homeStack, fullscreenStack2);
-        assertEquals(fullscreenStack2, getStackAbove(homeStack));
-        mDefaultTaskDisplayArea.moveStackBehindStack(homeStack, fullscreenStack4);
-        assertEquals(fullscreenStack4, getStackAbove(homeStack));
-        mDefaultTaskDisplayArea.moveStackBehindStack(homeStack, fullscreenStack2);
-        assertEquals(fullscreenStack2, getStackAbove(homeStack));
+        mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, fullscreenStack1);
+        assertEquals(fullscreenStack1, getRootTaskAbove(homeStack));
+        mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, fullscreenStack2);
+        assertEquals(fullscreenStack2, getRootTaskAbove(homeStack));
+        mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, fullscreenStack4);
+        assertEquals(fullscreenStack4, getRootTaskAbove(homeStack));
+        mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, fullscreenStack2);
+        assertEquals(fullscreenStack2, getRootTaskAbove(homeStack));
     }
 
     @Test
@@ -889,7 +891,7 @@
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
         final Task pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        assertEquals(pinnedStack, getStackAbove(homeStack));
+        assertEquals(pinnedStack, getRootTaskAbove(homeStack));
 
         final Task alwaysOnTopStack = createStackForShouldBeVisibleTest(
                 mDefaultTaskDisplayArea, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
@@ -897,13 +899,13 @@
         alwaysOnTopStack.setAlwaysOnTop(true);
         assertTrue(alwaysOnTopStack.isAlwaysOnTop());
         // Ensure (non-pinned) always on top stack is put below pinned stack.
-        assertEquals(pinnedStack, getStackAbove(alwaysOnTopStack));
+        assertEquals(pinnedStack, getRootTaskAbove(alwaysOnTopStack));
 
         final Task nonAlwaysOnTopStack = createStackForShouldBeVisibleTest(
                 mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
         // Ensure non always on top stack is put below always on top stacks.
-        assertEquals(alwaysOnTopStack, getStackAbove(nonAlwaysOnTopStack));
+        assertEquals(alwaysOnTopStack, getRootTaskAbove(nonAlwaysOnTopStack));
 
         final Task alwaysOnTopStack2 = createStackForShouldBeVisibleTest(
                 mDefaultTaskDisplayArea, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
@@ -911,21 +913,21 @@
         alwaysOnTopStack2.setAlwaysOnTop(true);
         assertTrue(alwaysOnTopStack2.isAlwaysOnTop());
         // Ensure newly created always on top stack is placed above other all always on top stacks.
-        assertEquals(pinnedStack, getStackAbove(alwaysOnTopStack2));
+        assertEquals(pinnedStack, getRootTaskAbove(alwaysOnTopStack2));
 
         alwaysOnTopStack2.setAlwaysOnTop(false);
         // Ensure, when always on top is turned off for a stack, the stack is put just below all
         // other always on top stacks.
-        assertEquals(alwaysOnTopStack, getStackAbove(alwaysOnTopStack2));
+        assertEquals(alwaysOnTopStack, getRootTaskAbove(alwaysOnTopStack2));
         alwaysOnTopStack2.setAlwaysOnTop(true);
 
         // Ensure always on top state changes properly when windowing mode changes.
         alwaysOnTopStack2.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         assertFalse(alwaysOnTopStack2.isAlwaysOnTop());
-        assertEquals(alwaysOnTopStack, getStackAbove(alwaysOnTopStack2));
+        assertEquals(alwaysOnTopStack, getRootTaskAbove(alwaysOnTopStack2));
         alwaysOnTopStack2.setWindowingMode(WINDOWING_MODE_FREEFORM);
         assertTrue(alwaysOnTopStack2.isAlwaysOnTop());
-        assertEquals(pinnedStack, getStackAbove(alwaysOnTopStack2));
+        assertEquals(pinnedStack, getRootTaskAbove(alwaysOnTopStack2));
     }
 
     @Test
@@ -980,7 +982,8 @@
             int windowingMode, int activityType, boolean onTop, boolean twoLevelTask) {
         final Task task;
         if (activityType == ACTIVITY_TYPE_HOME) {
-            task = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+            task = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_FULLSCREEN,
+                    ACTIVITY_TYPE_HOME);
             mDefaultTaskDisplayArea.positionChildAt(onTop ? POSITION_TOP : POSITION_BOTTOM, task,
                     false /* includingParents */);
         } else if (twoLevelTask) {
@@ -1224,12 +1227,12 @@
     @Test
     public void testStackOrderChangedOnRemoveStack() {
         final Task task = new TaskBuilder(mSupervisor).build();
-        StackOrderChangedListener listener = new StackOrderChangedListener();
-        mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener);
+        RootTaskOrderChangedListener listener = new RootTaskOrderChangedListener();
+        mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(listener);
         try {
-            mDefaultTaskDisplayArea.removeStack(task);
+            mDefaultTaskDisplayArea.removeRootTask(task);
         } finally {
-            mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener);
+            mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(listener);
         }
         assertTrue(listener.mChanged);
     }
@@ -1237,31 +1240,31 @@
     @Test
     public void testStackOrderChangedOnAddPositionStack() {
         final Task task = new TaskBuilder(mSupervisor).build();
-        mDefaultTaskDisplayArea.removeStack(task);
+        mDefaultTaskDisplayArea.removeRootTask(task);
 
-        StackOrderChangedListener listener = new StackOrderChangedListener();
-        mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener);
+        RootTaskOrderChangedListener listener = new RootTaskOrderChangedListener();
+        mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(listener);
         try {
             task.mReparenting = true;
             mDefaultTaskDisplayArea.addChild(task, 0);
         } finally {
-            mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener);
+            mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(listener);
         }
         assertTrue(listener.mChanged);
     }
 
     @Test
     public void testStackOrderChangedOnPositionStack() {
-        StackOrderChangedListener listener = new StackOrderChangedListener();
+        RootTaskOrderChangedListener listener = new RootTaskOrderChangedListener();
         try {
             final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
                     mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                     true /* onTop */);
-            mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener);
+            mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(listener);
             mDefaultTaskDisplayArea.positionChildAt(POSITION_BOTTOM, fullscreenStack1,
                     false /*includingParents*/);
         } finally {
-            mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener);
+            mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(listener);
         }
         assertTrue(listener.mChanged);
     }
@@ -1447,12 +1450,12 @@
         assertEquals(expected, task.shouldSleepActivities());
     }
 
-    private static class StackOrderChangedListener
-            implements TaskDisplayArea.OnStackOrderChangedListener {
+    private static class RootTaskOrderChangedListener
+            implements OnRootTaskOrderChangedListener {
         public boolean mChanged = false;
 
         @Override
-        public void onStackOrderChanged(Task stack) {
+        public void onRootTaskOrderChanged(Task rootTask) {
             mChanged = true;
         }
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
index c799f29..ce96771 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
@@ -33,8 +33,8 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.server.wm.ActivityTaskSupervisor.PendingActivityLaunch;
 import com.android.server.wm.ActivityStarter.Factory;
+import com.android.server.wm.ActivityTaskSupervisor.PendingActivityLaunch;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -77,8 +77,8 @@
                 .setCreateTask(true)
                 .build();
         final int startFlags = random.nextInt();
-        final Task stack = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea()
-                .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final Task rootTask = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         final WindowProcessController wpc = new WindowProcessController(mAtm,
                 mAtm.mContext.getApplicationInfo(), "name", 12345,
                 UserHandle.getUserId(12345), mock(Object.class),
@@ -86,7 +86,7 @@
         wpc.setThread(mock(IApplicationThread.class));
 
         mController.addPendingActivityLaunch(
-                new PendingActivityLaunch(activity, source, startFlags, stack, wpc, null));
+                new PendingActivityLaunch(activity, source, startFlags, rootTask, wpc, null));
         final boolean resume = random.nextBoolean();
         mController.doPendingActivityLaunches(resume);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 565bf8b..ef2e889 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -326,7 +326,7 @@
      * Creates a {@link ActivityStarter} with default parameters and necessary mocks.
      *
      * @param launchFlags The intent flags to launch activity.
-     * @param mockGetLaunchStack Whether to mock {@link RootWindowContainer#getLaunchStack} for
+     * @param mockGetLaunchStack Whether to mock {@link RootWindowContainer#getLaunchRootTask} for
      *                           always launching to the testing stack. Set to false when allowing
      *                           the activity can be launched to any stack that is decided by real
      *                           implementation.
@@ -342,14 +342,14 @@
         if (mockGetLaunchStack) {
             // Instrument the stack and task used.
             final Task stack = mRootWindowContainer.getDefaultTaskDisplayArea()
-                    .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+                    .createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                             true /* onTop */);
 
             // Direct starter to use spy stack.
             doReturn(stack).when(mRootWindowContainer)
-                    .getLaunchStack(any(), any(), any(), anyBoolean());
-            doReturn(stack).when(mRootWindowContainer)
-                    .getLaunchStack(any(), any(), any(), anyBoolean(), any(), anyInt(), anyInt());
+                    .getLaunchRootTask(any(), any(), any(), anyBoolean());
+            doReturn(stack).when(mRootWindowContainer).getLaunchRootTask(any(), any(), any(),
+                    anyBoolean(), any(), anyInt(), anyInt());
         }
 
         // Set up mock package manager internal and make sure no unmocked methods are called
@@ -513,8 +513,8 @@
 
     private void assertNoTasks(DisplayContent display) {
         display.forAllTaskDisplayAreas(taskDisplayArea -> {
-            for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
-                final Task stack = taskDisplayArea.getStackAt(sNdx);
+            for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) {
+                final Task stack = taskDisplayArea.getRootTaskAt(sNdx);
                 assertFalse(stack.hasChild());
             }
         });
@@ -806,7 +806,7 @@
                 new TestDisplayContent.Builder(mAtm, 1000, 1500)
                         .setPosition(POSITION_BOTTOM).build();
         final TaskDisplayArea secondaryTaskContainer = secondaryDisplay.getDefaultTaskDisplayArea();
-        final Task stack = secondaryTaskContainer.createStack(
+        final Task stack = secondaryTaskContainer.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         // Create an activity record on the top of secondary display.
@@ -828,7 +828,7 @@
         assertEquals(START_DELIVERED_TO_TOP, result);
 
         // Ensure secondary display only creates one stack.
-        verify(secondaryTaskContainer, times(1)).createStack(anyInt(), anyInt(), anyBoolean());
+        verify(secondaryTaskContainer, times(1)).createRootTask(anyInt(), anyInt(), anyBoolean());
     }
 
     /**
@@ -848,11 +848,11 @@
                 false /* includingParents */);
         final TaskDisplayArea secondaryTaskContainer = secondaryDisplay.getDefaultTaskDisplayArea();
         final ActivityRecord singleTaskActivity = createSingleTaskActivityOn(
-                secondaryTaskContainer.createStack(WINDOWING_MODE_FULLSCREEN,
+                secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN,
                         ACTIVITY_TYPE_STANDARD, false /* onTop */));
 
         // Create another activity on top of the secondary display.
-        final Task topStack = secondaryTaskContainer.createStack(WINDOWING_MODE_FULLSCREEN,
+        final Task topStack = secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
         final Task topTask = new TaskBuilder(mSupervisor).setParentTask(topStack).build();
         new ActivityBuilder(mAtm).setTask(topTask).build();
@@ -870,7 +870,7 @@
         assertEquals(START_TASK_TO_FRONT, result);
 
         // Ensure secondary display only creates two stacks.
-        verify(secondaryTaskContainer, times(2)).createStack(anyInt(), anyInt(), anyBoolean());
+        verify(secondaryTaskContainer, times(2)).createRootTask(anyInt(), anyInt(), anyBoolean());
         // The metrics logger should receive the same result and non-null options.
         verify(mActivityMetricsLogger).notifyActivityLaunched(any() /* launchingState */,
                 eq(result), eq(singleTaskActivity), notNull() /* options */);
@@ -938,7 +938,7 @@
         // Create a secondary display at bottom.
         final TestDisplayContent secondaryDisplay = addNewDisplayContentAt(POSITION_BOTTOM);
         final TaskDisplayArea secondaryTaskContainer = secondaryDisplay.getDefaultTaskDisplayArea();
-        secondaryTaskContainer.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+        secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
 
         // Put an activity on default display as the top focused activity.
@@ -1053,7 +1053,7 @@
         final ActivityStarter starter = prepareStarter(0 /* flags */);
         starter.mStartActivity = new ActivityBuilder(mAtm).build();
         final Task task = new TaskBuilder(mAtm.mTaskSupervisor)
-                .setParentTask(mAtm.mRootWindowContainer.getDefaultTaskDisplayArea().createStack(
+                .setParentTask(mAtm.mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
                 .setUserId(10)
                 .build();
@@ -1117,13 +1117,13 @@
 
         final Task stack = spy(
                 mRootWindowContainer.getDefaultTaskDisplayArea()
-                        .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+                        .createRootTask(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
                                 /* onTop */true));
 
         stack.addChild(targetRecord);
 
         doReturn(stack).when(mRootWindowContainer)
-                .getLaunchStack(any(), any(), any(), anyBoolean(), any(), anyInt(), anyInt());
+                .getLaunchRootTask(any(), any(), any(), anyBoolean(), any(), anyInt(), anyInt());
 
         starter.mStartActivity = new ActivityBuilder(mAtm).build();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 6ca69bf..8cc515e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -196,7 +196,7 @@
     @Test
     public void testCancelRemoteAnimationWhenFreeze() {
         final DisplayContent dc = createNewDisplay(Display.STATE_ON);
-        doReturn(false).when(dc).onDescendantOrientationChanged(any(), any());
+        doReturn(false).when(dc).onDescendantOrientationChanged(any());
         final WindowState exitingAppWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
                 dc, "exiting app");
         final ActivityRecord exitingActivity= exitingAppWindow.mActivityRecord;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
index 5828d02..59b12e4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
@@ -366,6 +366,30 @@
         assertTrue(child.getConfiguration().windowConfiguration.getMaxBounds().isEmpty());
     }
 
+    @Test
+    public void testOnRequestedOverrideConfigurationChangedOverrideMaxBounds() {
+        final TestConfigurationContainer root =
+                new TestConfigurationContainer(true /* providesMaxBounds */);
+        final Rect bounds = new Rect(0, 0, 10, 10);
+        final TestConfigurationContainer child = new TestConfigurationContainer();
+        root.addChild(child);
+        final Configuration configuration = new Configuration();
+        configuration.windowConfiguration.setBounds(bounds);
+
+        root.onRequestedOverrideConfigurationChanged(configuration);
+
+        assertEquals(bounds, root.getBounds());
+        assertEquals(bounds, root.getConfiguration().windowConfiguration.getBounds());
+        assertEquals(bounds, child.getBounds());
+        assertEquals(bounds, child.getConfiguration().windowConfiguration.getBounds());
+
+        assertEquals(bounds, root.getMaxBounds());
+        assertEquals(bounds, root.getConfiguration().windowConfiguration.getMaxBounds());
+        assertEquals(bounds, child.getMaxBounds());
+        assertEquals(bounds, child.getConfiguration().windowConfiguration.getMaxBounds());
+    }
+
+
     /**
      * Contains minimal implementation of {@link ConfigurationContainer}'s abstract behavior needed
      * for testing.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
index 06a6882..bc91c70 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
@@ -87,7 +87,7 @@
         final Task task = new TaskBuilder(mSupervisor)
                 .setTaskDisplayArea(mTaskDisplayArea).setCreateActivity(true).build();
         final ActivityRecord activity = task.getTopNonFinishingActivity();
-        doReturn(true).when(mDisplayContent).onDescendantOrientationChanged(any(), any());
+        doReturn(true).when(mDisplayContent).onDescendantOrientationChanged(any());
         activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
 
         // Display is portrait, DisplayAreaGroup inherits that
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
index 39bf8eb..5a47493 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
@@ -94,9 +94,9 @@
 
     @Test
     public void testTaskDisplayArea_taskPositionChanged_updatesTaskDisplayAreaPosition() {
-        final Task stack1 = mTaskDisplayArea1.createStack(
+        final Task stack1 = mTaskDisplayArea1.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final Task stack2 = mTaskDisplayArea2.createStack(
+        final Task stack2 = mTaskDisplayArea2.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         // Initial order
@@ -155,11 +155,11 @@
                 .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(group2)
                         .setTaskDisplayAreas(Lists.newArrayList(taskDisplayArea5)))
                 .build(wms);
-        final Task stack1 = taskDisplayArea1.createStack(
+        final Task stack1 = taskDisplayArea1.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final Task stack3 = taskDisplayArea3.createStack(
+        final Task stack3 = taskDisplayArea3.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final Task stack4 = taskDisplayArea4.createStack(
+        final Task stack4 = taskDisplayArea4.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         // Initial order
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 59b2d4f..025c5a6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -468,14 +468,14 @@
 
         activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
-        verify(tda).onDescendantOrientationChanged(any(), any());
-        verify(mDisplayContent, never()).onDescendantOrientationChanged(any(), any());
+        verify(tda).onDescendantOrientationChanged(any());
+        verify(mDisplayContent, never()).onDescendantOrientationChanged(any());
 
         tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
         activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
 
-        verify(tda, times(2)).onDescendantOrientationChanged(any(), any());
-        verify(mDisplayContent).onDescendantOrientationChanged(any(), any());
+        verify(tda, times(2)).onDescendantOrientationChanged(any());
+        verify(mDisplayContent).onDescendantOrientationChanged(any());
     }
 
     private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index d921718..2053bfb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -839,12 +839,12 @@
         final DisplayContent newDisplay = createNewDisplay();
 
         final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin");
-        final Task stack = mDisplayContent.getTopStack();
+        final Task stack = mDisplayContent.getTopRootTask();
         final ActivityRecord activity = stack.topRunningActivity();
         doReturn(true).when(activity).shouldBeVisibleUnchecked();
 
         final WindowState appWin1 = createWindow(null, TYPE_APPLICATION, newDisplay, "appWin1");
-        final Task stack1 = newDisplay.getTopStack();
+        final Task stack1 = newDisplay.getTopRootTask();
         final ActivityRecord activity1 = stack1.topRunningActivity();
         doReturn(true).when(activity1).shouldBeVisibleUnchecked();
         appWin.setHasSurface(true);
@@ -886,7 +886,7 @@
         doReturn(true).when(freeformStack).isVisible();
         freeformStack.getTopChild().setBounds(100, 100, 300, 400);
 
-        assertTrue(dc.getDefaultTaskDisplayArea().isStackVisible(WINDOWING_MODE_FREEFORM));
+        assertTrue(dc.getDefaultTaskDisplayArea().isRootTaskVisible(WINDOWING_MODE_FREEFORM));
 
         freeformStack.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
         stack.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_PORTRAIT);
@@ -1202,6 +1202,17 @@
         assertTrue(mNavBarWindow.getParent().isAnimating(WindowContainer.AnimationFlags.PARENTS,
                 ANIMATION_TYPE_FIXED_TRANSFORM));
 
+        // If the visibility of insets state is changed, the rotated state should be updated too.
+        final InsetsState rotatedState = app.getFixedRotationTransformInsetsState();
+        final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
+        assertEquals(state.getSource(ITYPE_STATUS_BAR).isVisible(),
+                rotatedState.getSource(ITYPE_STATUS_BAR).isVisible());
+        state.getSource(ITYPE_STATUS_BAR).setVisible(
+                !rotatedState.getSource(ITYPE_STATUS_BAR).isVisible());
+        mDisplayContent.getInsetsStateController().notifyInsetsChanged();
+        assertEquals(state.getSource(ITYPE_STATUS_BAR).isVisible(),
+                rotatedState.getSource(ITYPE_STATUS_BAR).isVisible());
+
         final Rect outFrame = new Rect();
         final Rect outInsets = new Rect();
         final Rect outStableInsets = new Rect();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
index eaedd58..554160c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
@@ -27,6 +27,8 @@
 
 import android.annotation.Nullable;
 import android.platform.test.annotations.Presubmit;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 import android.view.Display;
 import android.view.DisplayAddress;
@@ -245,7 +247,7 @@
     private String getStoredDisplayAttributeValue(TestStorage storage, String attr)
             throws Exception {
         try (InputStream stream = storage.openRead()) {
-            XmlPullParser parser = Xml.newPullParser();
+            TypedXmlPullParser parser = Xml.newFastPullParser();
             parser.setInput(stream, StandardCharsets.UTF_8.name());
             int type;
             while ((type = parser.next()) != XmlPullParser.START_TAG
diff --git a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java
index c3e1922..dfc2e35 100644
--- a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java
@@ -31,7 +31,7 @@
 
 import com.android.internal.R;
 import com.android.internal.util.Preconditions;
-import com.android.server.wm.utils.FakeDeviceConfigInterface;
+import com.android.server.testutils.FakeDeviceConfigInterface;
 
 import org.junit.After;
 import org.junit.Test;
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index cd428e1..a99e40c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -294,11 +294,11 @@
 
         mController.registerModifier(positioner);
 
-        doNothing().when(mRootWindowContainer).moveStackToTaskDisplayArea(anyInt(), any(),
+        doNothing().when(mRootWindowContainer).moveRootTaskToTaskDisplayArea(anyInt(), any(),
                 anyBoolean());
         mController.layoutTask(task, null /* windowLayout */);
-        verify(mRootWindowContainer, times(1)).moveStackToTaskDisplayArea(eq(task.getRootTaskId()),
-                eq(preferredTaskDisplayArea), anyBoolean());
+        verify(mRootWindowContainer, times(1)).moveRootTaskToTaskDisplayArea(
+                eq(task.getRootTaskId()), eq(preferredTaskDisplayArea), anyBoolean());
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index d701a9d..b7d8638 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -115,7 +115,7 @@
                 .thenReturn(mTestDisplay);
 
         Task stack = mTestDisplay.getDefaultTaskDisplayArea()
-                .createStack(TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true);
+                .createRootTask(TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true);
         mTestTask = new TaskBuilder(mSupervisor).setComponent(TEST_COMPONENT).setParentTask(stack)
                 .build();
         mTestTask.mUserId = TEST_USER_ID;
@@ -337,7 +337,7 @@
     public void testClearsRecordsOfTheUserOnUserCleanUp() {
         mTarget.saveTask(mTestTask);
 
-        Task stack = mTestDisplay.getDefaultTaskDisplayArea().createStack(
+        Task stack = mTestDisplay.getDefaultTaskDisplayArea().createRootTask(
                 TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true);
         final Task anotherTaskOfTheSameUser = new TaskBuilder(mSupervisor)
                 .setComponent(ALTERNATIVE_COMPONENT)
@@ -349,7 +349,7 @@
         anotherTaskOfTheSameUser.setHasBeenVisible(true);
         mTarget.saveTask(anotherTaskOfTheSameUser);
 
-        stack = mTestDisplay.getDefaultTaskDisplayArea().createStack(TEST_WINDOWING_MODE,
+        stack = mTestDisplay.getDefaultTaskDisplayArea().createRootTask(TEST_WINDOWING_MODE,
                 ACTIVITY_TYPE_STANDARD, /* onTop */ true);
         final Task anotherTaskOfDifferentUser = new TaskBuilder(mSupervisor)
                 .setComponent(TEST_COMPONENT)
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 7812934..4892ef3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -724,7 +724,7 @@
         mRecentTasks.setParameters(-1 /* min */, 3 /* max */, -1 /* ms */);
 
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        final Task alwaysOnTopTask = taskDisplayArea.createStack(WINDOWING_MODE_MULTI_WINDOW,
+        final Task alwaysOnTopTask = taskDisplayArea.createRootTask(WINDOWING_MODE_MULTI_WINDOW,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
         alwaysOnTopTask.setAlwaysOnTop(true);
 
@@ -838,7 +838,7 @@
 
         Task stack = mTasks.get(2).getRootTask();
         stack.moveToFront("", mTasks.get(2));
-        doReturn(stack).when(mAtm.mRootWindowContainer).getTopDisplayFocusedStack();
+        doReturn(stack).when(mAtm.mRootWindowContainer).getTopDisplayFocusedRootTask();
 
         // Simulate the reset from the timeout
         mRecentTasks.resetFreezeTaskListReorderingOnTimeout();
@@ -858,7 +858,7 @@
         mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
 
         final Task homeStack = mTaskContainer.getRootHomeTask();
-        final Task aboveHomeStack = mTaskContainer.createStack(
+        final Task aboveHomeStack = mTaskContainer.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         // Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all
@@ -875,10 +875,10 @@
     public void testBehindHomeStackTasks_expectTaskTrimmed() {
         mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
 
-        final Task behindHomeStack = mTaskContainer.createStack(
+        final Task behindHomeStack = mTaskContainer.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         final Task homeStack = mTaskContainer.getRootHomeTask();
-        final Task aboveHomeStack = mTaskContainer.createStack(
+        final Task aboveHomeStack = mTaskContainer.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         // Add a number of tasks (beyond the max) but ensure that only the task in the stack behind
@@ -897,17 +897,17 @@
     public void testOtherDisplayTasks_expectNoTrim() {
         mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
 
-        final Task homeStack = mTaskContainer.getRootHomeTask();
+        final Task homeTask = mTaskContainer.getRootHomeTask();
         final DisplayContent otherDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
-        final Task otherDisplayStack = otherDisplay.getDefaultTaskDisplayArea()
-                .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final Task otherDisplayRootTask = otherDisplay.getDefaultTaskDisplayArea().createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         // Add a number of tasks (beyond the max) on each display, ensure that the tasks are not
         // removed
-        mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task1").setParentTask(otherDisplayStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(otherDisplayStack).build());
-        mRecentTasks.add(createTaskBuilder(".HomeTask2").setParentTask(homeStack).build());
+        mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeTask).build());
+        mRecentTasks.add(createTaskBuilder(".Task1").setParentTask(otherDisplayRootTask).build());
+        mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(otherDisplayRootTask).build());
+        mRecentTasks.add(createTaskBuilder(".HomeTask2").setParentTask(homeTask).build());
 
         triggerTrimAndAssertNoTasksTrimmed();
     }
@@ -1103,9 +1103,9 @@
     private void assertNotRestoreTask(Runnable action) {
         // Verify stack count doesn't change because task with fullscreen mode and standard type
         // would have its own stack.
-        final int originalStackCount = mTaskContainer.getStackCount();
+        final int originalStackCount = mTaskContainer.getRootTaskCount();
         action.run();
-        assertEquals(originalStackCount, mTaskContainer.getStackCount());
+        assertEquals(originalStackCount, mTaskContainer.getRootTaskCount());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 40f73b1..5c39bd0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -322,7 +322,7 @@
         assertEquals(landActivity.findMainWindow(), win1);
 
         // Ensure that the display is in Landscape
-        landActivity.onDescendantOrientationChanged(landActivity.token, landActivity);
+        landActivity.onDescendantOrientationChanged(landActivity);
         assertEquals(Configuration.ORIENTATION_LANDSCAPE,
                 mDefaultDisplay.getConfiguration().orientation);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 5ff3ff5..63ee5d0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -89,7 +89,7 @@
     @Test
     public void testRecentsActivityVisiblility() {
         TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        Task recentsStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+        Task recentsStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_RECENTS, true /* onTop */);
         ActivityRecord recentActivity = new ActivityBuilder(mAtm)
                 .setComponent(mRecentsComponent)
@@ -118,7 +118,7 @@
     public void testPreloadRecentsActivity() {
         TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
         final Task homeStack =
-                defaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+                defaultTaskDisplayArea.getRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
         defaultTaskDisplayArea.positionChildAt(POSITION_TOP, homeStack,
                 false /* includingParents */);
         ActivityRecord topRunningHomeActivity = homeStack.topRunningActivity();
@@ -150,7 +150,7 @@
         mAtm.startRecentsActivity(recentsIntent, 0 /* eventTime */,
                 null /* recentsAnimationRunner */);
 
-        Task recentsStack = defaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN,
+        Task recentsStack = defaultTaskDisplayArea.getRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_RECENTS);
         assertThat(recentsStack).isNotNull();
 
@@ -179,7 +179,7 @@
     public void testRestartRecentsActivity() throws Exception {
         // Have a recents activity that is not attached to its process (ActivityRecord.app = null).
         TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        Task recentsStack = defaultTaskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+        Task recentsStack = defaultTaskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_RECENTS, true /* onTop */);
         ActivityRecord recentActivity = new ActivityBuilder(mAtm).setComponent(
                 mRecentsComponent).setCreateTask(true).setParentTask(recentsStack).build();
@@ -251,21 +251,21 @@
     @Test
     public void testCancelAnimationOnVisibleStackOrderChange() {
         TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        Task fullscreenStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+        Task fullscreenStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
         new ActivityBuilder(mAtm)
                 .setComponent(new ComponentName(mContext.getPackageName(), "App1"))
                 .setCreateTask(true)
                 .setParentTask(fullscreenStack)
                 .build();
-        Task recentsStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+        Task recentsStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_RECENTS, true /* onTop */);
         new ActivityBuilder(mAtm)
                 .setComponent(mRecentsComponent)
                 .setCreateTask(true)
                 .setParentTask(recentsStack)
                 .build();
-        Task fullscreenStack2 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+        Task fullscreenStack2 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
         new ActivityBuilder(mAtm)
                 .setComponent(new ComponentName(mContext.getPackageName(), "App2"))
@@ -292,21 +292,21 @@
     @Test
     public void testKeepAnimationOnHiddenStackOrderChange() {
         TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        Task fullscreenStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+        Task fullscreenStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
         new ActivityBuilder(mAtm)
                 .setComponent(new ComponentName(mContext.getPackageName(), "App1"))
                 .setCreateTask(true)
                 .setParentTask(fullscreenStack)
                 .build();
-        Task recentsStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+        Task recentsStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_RECENTS, true /* onTop */);
         new ActivityBuilder(mAtm)
                 .setComponent(mRecentsComponent)
                 .setCreateTask(true)
                 .setParentTask(recentsStack)
                 .build();
-        Task fullscreenStack2 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+        Task fullscreenStack2 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
         new ActivityBuilder(mAtm)
                 .setComponent(new ComponentName(mContext.getPackageName(), "App2"))
@@ -329,7 +329,7 @@
     public void testMultipleUserHomeActivity_findUserHomeTask() {
         TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultDisplay()
                 .getDefaultTaskDisplayArea();
-        Task homeStack = taskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED,
+        Task homeStack = taskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED,
                 ACTIVITY_TYPE_HOME);
         ActivityRecord otherUserHomeActivity = new ActivityBuilder(mAtm)
                 .setParentTask(homeStack)
@@ -338,7 +338,7 @@
                 .build();
         otherUserHomeActivity.getTask().mUserId = TEST_USER_ID;
 
-        Task fullscreenStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+        Task fullscreenStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
         new ActivityBuilder(mAtm)
                 .setComponent(new ComponentName(mContext.getPackageName(), "App1"))
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 7ca364cf..f79e4cc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -94,7 +94,7 @@
 
     @Before
     public void setUp() throws Exception {
-        mFullscreenStack = mRootWindowContainer.getDefaultTaskDisplayArea().createStack(
+        mFullscreenStack = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         doNothing().when(mAtm).updateSleepIfNeededLocked();
     }
@@ -128,7 +128,7 @@
         ensureStackPlacement(mFullscreenStack, firstActivity, secondActivity);
 
         // Move first activity to pinned stack.
-        mRootWindowContainer.moveActivityToPinnedStack(firstActivity, "initialMove");
+        mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, "initialMove");
 
         final TaskDisplayArea taskDisplayArea = mFullscreenStack.getDisplayArea();
         Task pinnedStack = taskDisplayArea.getRootPinnedTask();
@@ -137,11 +137,11 @@
         ensureStackPlacement(mFullscreenStack, secondActivity);
 
         // Move second activity to pinned stack.
-        mRootWindowContainer.moveActivityToPinnedStack(secondActivity, "secondMove");
+        mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "secondMove");
 
         // Need to get stacks again as a new instance might have been created.
         pinnedStack = taskDisplayArea.getRootPinnedTask();
-        mFullscreenStack = taskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN,
+        mFullscreenStack = taskDisplayArea.getRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD);
         // Ensure stacks have swapped tasks.
         ensureStackPlacement(pinnedStack, secondActivity);
@@ -166,7 +166,7 @@
 
 
         // Move first activity to pinned stack.
-        mRootWindowContainer.moveActivityToPinnedStack(secondActivity, "initialMove");
+        mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "initialMove");
 
         assertTrue(firstActivity.mRequestForceTransition);
     }
@@ -237,9 +237,9 @@
         doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
 
         doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay();
-        doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack();
+        doReturn(isFocusedStack ? stack : null).when(display).getFocusedRootTask();
         TaskDisplayArea defaultTaskDisplayArea = display.getDefaultTaskDisplayArea();
-        doReturn(isFocusedStack ? stack : null).when(defaultTaskDisplayArea).getFocusedStack();
+        doReturn(isFocusedStack ? stack : null).when(defaultTaskDisplayArea).getFocusedRootTask();
         mRootWindowContainer.applySleepTokens(true);
         verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
         verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
@@ -282,19 +282,19 @@
     public void testRemovingStackOnAppCrash() {
         final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
                 .getDefaultTaskDisplayArea();
-        final int originalStackCount = defaultTaskDisplayArea.getStackCount();
-        final Task stack = defaultTaskDisplayArea.createStack(
+        final int originalStackCount = defaultTaskDisplayArea.getRootTaskCount();
+        final Task stack = defaultTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
         final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(stack).build();
 
-        assertEquals(originalStackCount + 1, defaultTaskDisplayArea.getStackCount());
+        assertEquals(originalStackCount + 1, defaultTaskDisplayArea.getRootTaskCount());
 
         // Let's pretend that the app has crashed.
         firstActivity.app.setThread(null);
         mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test");
 
         // Verify that the stack was removed.
-        assertEquals(originalStackCount, defaultTaskDisplayArea.getStackCount());
+        assertEquals(originalStackCount, defaultTaskDisplayArea.getRootTaskCount());
     }
 
     /**
@@ -305,34 +305,34 @@
     public void testRemovingStackOnAppCrash_multipleDisplayAreas() {
         final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
                 .getDefaultTaskDisplayArea();
-        final int originalStackCount = defaultTaskDisplayArea.getStackCount();
-        final Task stack = defaultTaskDisplayArea.createStack(
+        final int originalStackCount = defaultTaskDisplayArea.getRootTaskCount();
+        final Task stack = defaultTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
         final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(stack).build();
-        assertEquals(originalStackCount + 1, defaultTaskDisplayArea.getStackCount());
+        assertEquals(originalStackCount + 1, defaultTaskDisplayArea.getRootTaskCount());
 
         final DisplayContent dc = defaultTaskDisplayArea.getDisplayContent();
         final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
                 dc, mRootWindowContainer.mWmService, "TestTaskDisplayArea", FEATURE_VENDOR_FIRST);
-        final Task secondStack = secondTaskDisplayArea.createStack(
+        final Task secondStack = secondTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
         new ActivityBuilder(mAtm).setTask(secondStack).setUseProcess(firstActivity.app).build();
-        assertEquals(1, secondTaskDisplayArea.getStackCount());
+        assertEquals(1, secondTaskDisplayArea.getRootTaskCount());
 
         // Let's pretend that the app has crashed.
         firstActivity.app.setThread(null);
         mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test");
 
         // Verify that the stacks were removed.
-        assertEquals(originalStackCount, defaultTaskDisplayArea.getStackCount());
-        assertEquals(0, secondTaskDisplayArea.getStackCount());
+        assertEquals(originalStackCount, defaultTaskDisplayArea.getRootTaskCount());
+        assertEquals(0, secondTaskDisplayArea.getRootTaskCount());
     }
 
     @Test
     public void testFocusability() {
         final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
                 .getDefaultTaskDisplayArea();
-        final Task stack = defaultTaskDisplayArea.createStack(
+        final Task stack = defaultTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build();
 
@@ -345,7 +345,7 @@
         assertFalse(stack.isTopActivityFocusable());
         assertFalse(activity.isFocusable());
 
-        final Task pinnedStack = defaultTaskDisplayArea.createStack(
+        final Task pinnedStack = defaultTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm)
                 .setTask(pinnedStack).build();
@@ -371,7 +371,7 @@
     public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() {
         // Create primary split-screen stack with a task and an activity.
         final Task primaryStack = mRootWindowContainer.getDefaultTaskDisplayArea()
-                .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+                .createRootTask(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
                         true /* onTop */);
         final Task task = new TaskBuilder(mSupervisor).setParentTask(primaryStack).build();
         final ActivityRecord r = new ActivityBuilder(mAtm).setTask(task).build();
@@ -381,7 +381,7 @@
         final ActivityOptions options = ActivityOptions.makeBasic();
         options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
         final Task result =
-                mRootWindowContainer.getLaunchStack(r, options, task, true /* onTop */);
+                mRootWindowContainer.getLaunchRootTask(r, options, task, true /* onTop */);
 
         // Assert that the primary stack is returned.
         assertEquals(primaryStack, result);
@@ -410,7 +410,7 @@
                 false);
 
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        verify(taskDisplayArea).moveHomeStackToFront(contains(reason));
+        verify(taskDisplayArea).moveHomeRootTaskToFront(contains(reason));
     }
 
     /**
@@ -421,7 +421,7 @@
     public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() {
         // Create stack/task on default display.
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        final Task targetStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+        final Task targetStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, false /* onTop */);
         final Task targetTask = new TaskBuilder(mSupervisor).setParentTask(targetStack).build();
 
@@ -429,7 +429,7 @@
         final TestDisplayContent secondDisplay = addNewDisplayContentAt(
                 DisplayContent.POSITION_TOP);
         final Task stack = secondDisplay.getDefaultTaskDisplayArea()
-                .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
+                .createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
         final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
         new ActivityBuilder(mAtm).setTask(task).build();
 
@@ -437,7 +437,7 @@
         mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
                 false);
 
-        verify(taskDisplayArea, never()).moveHomeStackToFront(contains(reason));
+        verify(taskDisplayArea, never()).moveHomeRootTaskToFront(contains(reason));
     }
 
     /**
@@ -448,7 +448,7 @@
     public void testResumeActivityWhenNonTopmostStackIsTopFocused() {
         // Create a stack at bottom.
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        final Task targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+        final Task targetStack = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, false /* onTop */));
         final Task task = new TaskBuilder(mSupervisor).setParentTask(targetStack).build();
         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
@@ -457,10 +457,10 @@
         // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
         // is the current top focused stack.
         assertFalse(targetStack.isTopStackInDisplayArea());
-        doReturn(targetStack).when(mRootWindowContainer).getTopDisplayFocusedStack();
+        doReturn(targetStack).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
 
         // Use the stack as target to resume.
-        mRootWindowContainer.resumeFocusedStacksTopActivities(
+        mRootWindowContainer.resumeFocusedTasksTopActivities(
                 targetStack, activity, null /* targetOptions */);
 
         // Verify the target stack should resume its activity.
@@ -477,14 +477,14 @@
         mFullscreenStack.removeIfPossible();
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
         taskDisplayArea.getRootHomeTask().removeIfPossible();
-        taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+        taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
 
         doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any());
 
         mAtm.setBooted(true);
 
         // Trigger resume on all displays
-        mRootWindowContainer.resumeFocusedStacksTopActivities();
+        mRootWindowContainer.resumeFocusedTasksTopActivities();
 
         // Verify that home activity was started on the default display
         verify(mRootWindowContainer).resumeHomeActivity(any(), any(), eq(taskDisplayArea));
@@ -499,14 +499,14 @@
         mFullscreenStack.removeIfPossible();
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
         taskDisplayArea.getRootHomeTask().removeIfPossible();
-        taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+        taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
 
         // Create an activity on secondary display.
         final TestDisplayContent secondDisplay = addNewDisplayContentAt(
                 DisplayContent.POSITION_TOP);
-        final Task stack = secondDisplay.getDefaultTaskDisplayArea()
-                .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
+        final Task rootTask = secondDisplay.getDefaultTaskDisplayArea().createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
         new ActivityBuilder(mAtm).setTask(task).build();
 
         doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any());
@@ -514,7 +514,7 @@
         mAtm.setBooted(true);
 
         // Trigger resume on all displays
-        mRootWindowContainer.resumeFocusedStacksTopActivities();
+        mRootWindowContainer.resumeFocusedTasksTopActivities();
 
         // Verify that home activity was started on the default display
         verify(mRootWindowContainer).resumeHomeActivity(any(), any(), eq(taskDisplayArea));
@@ -528,7 +528,7 @@
     public void testResumeActivityLingeringTransition() {
         // Create a stack at top.
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        final Task targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+        final Task targetStack = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, false /* onTop */));
         final Task task = new TaskBuilder(mSupervisor).setParentTask(targetStack).build();
         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
@@ -538,7 +538,7 @@
         assertTrue(targetStack.isTopStackInDisplayArea());
 
         // Use the stack as target to resume.
-        mRootWindowContainer.resumeFocusedStacksTopActivities();
+        mRootWindowContainer.resumeFocusedTasksTopActivities();
 
         // Verify the lingering app transition is being executed because it's already resumed
         verify(targetStack, times(1)).executeAppTransition(any());
@@ -548,7 +548,7 @@
     public void testResumeActivityLingeringTransition_notExecuted() {
         // Create a stack at bottom.
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        final Task targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+        final Task targetStack = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, false /* onTop */));
         final Task task = new TaskBuilder(mSupervisor).setParentTask(targetStack).build();
         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
@@ -557,10 +557,10 @@
 
         // Assume the stack is at the topmost position
         assertFalse(targetStack.isTopStackInDisplayArea());
-        doReturn(targetStack).when(mRootWindowContainer).getTopDisplayFocusedStack();
+        doReturn(targetStack).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
 
         // Use the stack as target to resume.
-        mRootWindowContainer.resumeFocusedStacksTopActivities();
+        mRootWindowContainer.resumeFocusedTasksTopActivities();
 
         // Verify the lingering app transition is being executed because it's already resumed
         verify(targetStack, never()).executeAppTransition(any());
@@ -586,9 +586,9 @@
 
         mRootWindowContainer.startHomeOnAllDisplays(0, "testStartHome");
 
-        assertTrue(mRootWindowContainer.getDefaultDisplay().getTopStack().isActivityTypeHome());
-        assertNotNull(secondDisplay.getTopStack());
-        assertTrue(secondDisplay.getTopStack().isActivityTypeHome());
+        assertTrue(mRootWindowContainer.getDefaultDisplay().getTopRootTask().isActivityTypeHome());
+        assertNotNull(secondDisplay.getTopRootTask());
+        assertTrue(secondDisplay.getTopRootTask().isActivityTypeHome());
     }
 
     /**
@@ -840,7 +840,7 @@
     }
 
     /**
-     * Test that {@link RootWindowContainer#getLaunchStack} with the real caller id will get the
+     * Test that {@link RootWindowContainer#getLaunchRootTask} with the real caller id will get the
      * expected stack when requesting the activity launch on the secondary display.
      */
     @Test
@@ -861,7 +861,7 @@
         options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
         doReturn(true).when(mSupervisor).canPlaceEntityOnDisplay(secondaryDisplay.mDisplayId,
                 300 /* test realCallerPid */, 300 /* test realCallerUid */, r.info);
-        final Task result = mRootWindowContainer.getLaunchStack(r, options,
+        final Task result = mRootWindowContainer.getLaunchRootTask(r, options,
                 null /* task */, true /* onTop */, null, 300 /* test realCallerPid */,
                 300 /* test realCallerUid */);
 
@@ -881,7 +881,7 @@
         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
 
         // Make sure the root task is valid and can be reused on default display.
-        final Task stack = mRootWindowContainer.getValidLaunchStackInTaskDisplayArea(
+        final Task stack = mRootWindowContainer.getValidLaunchRootTaskInTaskDisplayArea(
                 mRootWindowContainer.getDefaultTaskDisplayArea(), activity, task,
                 null /* options */, null /* launchParams */);
         assertEquals(task, stack);
@@ -889,7 +889,7 @@
 
     @Test
     public void testSwitchUser_missingHomeRootTask() {
-        doReturn(mFullscreenStack).when(mRootWindowContainer).getTopDisplayFocusedStack();
+        doReturn(mFullscreenStack).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
 
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
         Task homeStack = taskDisplayArea.getRootHomeTask();
@@ -904,7 +904,7 @@
         mRootWindowContainer.switchUser(otherUser, null);
 
         assertNotNull(taskDisplayArea.getRootHomeTask());
-        assertEquals(taskDisplayArea.getTopStack(), taskDisplayArea.getRootHomeTask());
+        assertEquals(taskDisplayArea.getTopRootTask(), taskDisplayArea.getRootHomeTask());
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 42193c8..9af2b96 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -92,7 +92,7 @@
     public void testAllPausedActivitiesComplete() {
         DisplayContent displayContent = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY);
         TaskDisplayArea taskDisplayArea = displayContent.getDefaultTaskDisplayArea();
-        Task stack = taskDisplayArea.getStackAt(0);
+        Task stack = taskDisplayArea.getRootTaskAt(0);
         ActivityRecord activity = createActivityRecord(displayContent);
         stack.mPausingActivity = activity;
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index 95e344d..6821d47 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -71,7 +71,7 @@
         final int numTasks = 10;
         int activeTime = 0;
         for (int i = 0; i < numTasks; i++) {
-            createTask(display.getDefaultTaskDisplayArea().getStackAt(i % numStacks),
+            createTask(display.getDefaultTaskDisplayArea().getRootTaskAt(i % numStacks),
                     ".Task" + i, i, activeTime++, null);
         }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index a4bf594..da00198 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -698,7 +698,7 @@
 
         // Update with new activity requested orientation and recompute bounds with no previous
         // size compat cache.
-        verify(mTask).onDescendantOrientationChanged(any(), same(newActivity));
+        verify(mTask).onDescendantOrientationChanged(same(newActivity));
         verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt());
 
         final Rect displayBounds = display.getBounds();
@@ -739,7 +739,7 @@
 
         // Update with new activity requested orientation and recompute bounds with no previous
         // size compat cache.
-        verify(mTask).onDescendantOrientationChanged(any(), same(newActivity));
+        verify(mTask).onDescendantOrientationChanged(same(newActivity));
         verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt());
 
         final Rect displayBounds = display.getBounds();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 2db736e..536ef48 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -326,7 +326,7 @@
         // Set the default focused TDA.
         display.setLastFocusedTaskDisplayArea(taskDisplayArea);
         spyOn(taskDisplayArea);
-        final Task homeStack = taskDisplayArea.getStack(
+        final Task homeStack = taskDisplayArea.getRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
         spyOn(homeStack);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 28ba710..d80f816 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -181,33 +181,33 @@
         final Task newStack = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, mDisplayContent);
         final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea();
-        doReturn(newStack).when(taskDisplayArea).createStack(anyInt(), anyInt(), anyBoolean(),
+        doReturn(newStack).when(taskDisplayArea).createRootTask(anyInt(), anyInt(), anyBoolean(),
                 any(), any(), anyBoolean());
 
         final int type = ACTIVITY_TYPE_STANDARD;
-        assertGetOrCreateStack(WINDOWING_MODE_FULLSCREEN, type, candidateTask,
+        assertGetOrCreateRootTask(WINDOWING_MODE_FULLSCREEN, type, candidateTask,
                 true /* reuseCandidate */);
-        assertGetOrCreateStack(WINDOWING_MODE_UNDEFINED, type, candidateTask,
+        assertGetOrCreateRootTask(WINDOWING_MODE_UNDEFINED, type, candidateTask,
                 true /* reuseCandidate */);
-        assertGetOrCreateStack(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, type, candidateTask,
+        assertGetOrCreateRootTask(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, type, candidateTask,
                 true /* reuseCandidate */);
-        assertGetOrCreateStack(WINDOWING_MODE_FREEFORM, type, candidateTask,
+        assertGetOrCreateRootTask(WINDOWING_MODE_FREEFORM, type, candidateTask,
                 true /* reuseCandidate */);
-        assertGetOrCreateStack(WINDOWING_MODE_MULTI_WINDOW, type, candidateTask,
+        assertGetOrCreateRootTask(WINDOWING_MODE_MULTI_WINDOW, type, candidateTask,
                 true /* reuseCandidate */);
-        assertGetOrCreateStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, type, candidateTask,
+        assertGetOrCreateRootTask(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, type, candidateTask,
                 false /* reuseCandidate */);
-        assertGetOrCreateStack(WINDOWING_MODE_PINNED, type, candidateTask,
+        assertGetOrCreateRootTask(WINDOWING_MODE_PINNED, type, candidateTask,
                 true /* reuseCandidate */);
 
         final int windowingMode = WINDOWING_MODE_FULLSCREEN;
-        assertGetOrCreateStack(windowingMode, ACTIVITY_TYPE_HOME, candidateTask,
+        assertGetOrCreateRootTask(windowingMode, ACTIVITY_TYPE_HOME, candidateTask,
                 false /* reuseCandidate */);
-        assertGetOrCreateStack(windowingMode, ACTIVITY_TYPE_RECENTS, candidateTask,
+        assertGetOrCreateRootTask(windowingMode, ACTIVITY_TYPE_RECENTS, candidateTask,
                 false /* reuseCandidate */);
-        assertGetOrCreateStack(windowingMode, ACTIVITY_TYPE_ASSISTANT, candidateTask,
+        assertGetOrCreateRootTask(windowingMode, ACTIVITY_TYPE_ASSISTANT, candidateTask,
                 false /* reuseCandidate */);
-        assertGetOrCreateStack(windowingMode, ACTIVITY_TYPE_DREAM, candidateTask,
+        assertGetOrCreateRootTask(windowingMode, ACTIVITY_TYPE_DREAM, candidateTask,
                 false /* reuseCandidate */);
     }
 
@@ -250,9 +250,9 @@
         final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
                 mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
                 FEATURE_VENDOR_FIRST);
-        final Task firstStack = firstTaskDisplayArea.createStack(
+        final Task firstStack = firstTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final Task secondStack = secondTaskDisplayArea.createStack(
+        final Task secondStack = secondTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
         final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
                 .setTask(firstStack).build();
@@ -281,7 +281,7 @@
     @Test
     public void testIgnoreOrientationRequest() {
         final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
-        final Task stack = taskDisplayArea.createStack(
+        final Task stack = taskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build();
 
@@ -326,10 +326,10 @@
         assertFalse(defaultTaskDisplayArea.mChildren.contains(task));
     }
 
-    private void assertGetOrCreateStack(int windowingMode, int activityType, Task candidateTask,
+    private void assertGetOrCreateRootTask(int windowingMode, int activityType, Task candidateTask,
             boolean reuseCandidate) {
         final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea();
-        final Task stack = taskDisplayArea.getOrCreateStack(windowingMode, activityType,
+        final Task stack = taskDisplayArea.getOrCreateRootTask(windowingMode, activityType,
                 false /* onTop */, null /* intent */, candidateTask /* candidateTask */);
         assertEquals(reuseCandidate, stack == candidateTask);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 4f55322..269ce5d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -1566,13 +1566,13 @@
 
     private ActivityRecord createSourceActivity(TestDisplayContent display) {
         final Task stack = display.getDefaultTaskDisplayArea()
-                .createStack(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true);
+                .createRootTask(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true);
         return new ActivityBuilder(mAtm).setTask(stack).build();
     }
 
     private void addFreeformTaskTo(TestDisplayContent display, Rect bounds) {
         final Task stack = display.getDefaultTaskDisplayArea()
-                .createStack(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true);
+                .createRootTask(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true);
         stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
         final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
         // Just work around the unnecessary adjustments for bounds.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index d080fa0..4a5ff58 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -70,6 +70,8 @@
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 import android.util.DisplayMetrics;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 import android.view.DisplayInfo;
 
@@ -175,7 +177,7 @@
     public void testFitWithinBounds() {
         final Rect parentBounds = new Rect(10, 10, 200, 200);
         TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
-        Task stack = taskDisplayArea.createStack(WINDOWING_MODE_FREEFORM,
+        Task stack = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
         Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
         final Configuration parentConfig = stack.getConfiguration();
@@ -495,7 +497,7 @@
     @Test
     public void testInsetDisregardedWhenFreeformOverlapsNavBar() {
         TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
-        Task stack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+        Task stack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
         DisplayInfo displayInfo = new DisplayInfo();
         mAtm.mContext.getDisplay().getDisplayInfo(displayInfo);
@@ -1025,9 +1027,9 @@
         final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
                 mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
                 FEATURE_VENDOR_FIRST);
-        final Task firstStack = firstTaskDisplayArea.createStack(
+        final Task firstStack = firstTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final Task secondStack = secondTaskDisplayArea.createStack(
+        final Task secondStack = secondTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
         final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
                 .setTask(firstStack).build();
@@ -1064,12 +1066,12 @@
 
         activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
         assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation());
-        verify(display).onDescendantOrientationChanged(any(), same(task));
+        verify(display).onDescendantOrientationChanged(same(task));
         reset(display);
 
         display.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, task.getOrientation());
-        verify(display).onDescendantOrientationChanged(any(), same(task));
+        verify(display).onDescendantOrientationChanged(same(task));
     }
 
     private Task getTestTask() {
@@ -1081,7 +1083,7 @@
             Rect expectedConfigBounds) {
 
         TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
-        Task stack = taskDisplayArea.createStack(windowingMode, ACTIVITY_TYPE_STANDARD,
+        Task stack = taskDisplayArea.createRootTask(windowingMode, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
         Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
 
@@ -1097,7 +1099,7 @@
 
     private byte[] serializeToBytes(Task r) throws Exception {
         try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
-            final XmlSerializer serializer = Xml.newSerializer();
+            final TypedXmlSerializer serializer = Xml.newFastSerializer();
             serializer.setOutput(os, "UTF-8");
             serializer.startDocument(null, true);
             serializer.startTag(null, TASK_TAG);
@@ -1112,7 +1114,7 @@
 
     private Task restoreFromBytes(byte[] in) throws IOException, XmlPullParserException {
         try (Reader reader = new InputStreamReader(new ByteArrayInputStream(in))) {
-            final XmlPullParser parser = Xml.newPullParser();
+            final TypedXmlPullParser parser = Xml.newFastPullParser();
             parser.setInput(reader);
             assertEquals(XmlPullParser.START_TAG, parser.next());
             assertEquals(TASK_TAG, parser.getName());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 3d8adbd..c4bcfdb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -56,7 +56,6 @@
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.view.IRemoteAnimationFinishedCallback;
@@ -787,12 +786,11 @@
         final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = spy(builder.build());
 
-        final IBinder binder = mock(IBinder.class);
         final ActivityRecord activityRecord = mock(ActivityRecord.class);
         final TestWindowContainer child = root.addChildWindow();
 
-        child.setOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED, binder, activityRecord);
-        verify(root).onDescendantOrientationChanged(binder, activityRecord);
+        child.setOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED, activityRecord);
+        verify(root).onDescendantOrientationChanged(activityRecord);
     }
 
     @Test
@@ -814,7 +812,7 @@
         final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
 
         final DisplayContent newDc = createNewDisplay();
-        stack.getDisplayArea().removeStack(stack);
+        stack.getDisplayArea().removeRootTask(stack);
         newDc.getDefaultTaskDisplayArea().addChild(stack, POSITION_TOP);
 
         verify(stack).onDisplayChanged(newDc);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java
index 5210011..7a0ef0d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java
@@ -32,7 +32,7 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.server.wm.utils.FakeDeviceConfigInterface;
+import com.android.server.testutils.FakeDeviceConfigInterface;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 8fe65eb..dba157e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -387,7 +387,7 @@
     public void testSetIgnoreOrientationRequest_taskDisplayArea() {
         removeGlobalMinSizeRestriction();
         final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
-        final Task stack = taskDisplayArea.createStack(
+        final Task stack = taskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build();
         taskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -425,7 +425,7 @@
     public void testSetIgnoreOrientationRequest_displayContent() {
         removeGlobalMinSizeRestriction();
         final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
-        final Task stack = taskDisplayArea.createStack(
+        final Task stack = taskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build();
         mDisplayContent.setFocusedApp(activity);
@@ -743,8 +743,8 @@
     private List<Task> getTasksCreatedByOrganizer(DisplayContent dc) {
         ArrayList<Task> out = new ArrayList<>();
         dc.forAllTaskDisplayAreas(taskDisplayArea -> {
-            for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
-                final Task t = taskDisplayArea.getStackAt(sNdx);
+            for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) {
+                final Task t = taskDisplayArea.getRootTaskAt(sNdx);
                 if (t.mCreatedByOrganizer) out.add(t);
             }
         });
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 6c046bd..4c49c6e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -797,7 +797,7 @@
                 mTask = new TaskBuilder(mService.mTaskSupervisor)
                         .setComponent(mComponent)
                         .setParentTask(mParentTask).build();
-            } else if (mTask == null && mParentTask != null && DisplayContent.alwaysCreateStack(
+            } else if (mTask == null && mParentTask != null && DisplayContent.alwaysCreateRootTask(
                     mParentTask.getWindowingMode(), mParentTask.getActivityType())) {
                 // The stack can be the task root.
                 mTask = mParentTask;
@@ -871,7 +871,7 @@
                     activity.processName, activity.info.applicationInfo.uid);
 
             // Resume top activities to make sure all other signals in the system are connected.
-            mService.mRootWindowContainer.resumeFocusedStacksTopActivities();
+            mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
             return activity;
         }
     }
@@ -995,7 +995,7 @@
 
             // Create parent task.
             if (mParentTask == null && mCreateParentTask) {
-                mParentTask = mTaskDisplayArea.createStack(
+                mParentTask = mTaskDisplayArea.createRootTask(
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
             }
             if (mParentTask != null && !Mockito.mockingDetails(mParentTask).isSpy()) {
@@ -1020,9 +1020,9 @@
             }
 
             Task task;
-            final int taskId = mTaskId >= 0 ? mTaskId : mTaskDisplayArea.getNextStackId();
+            final int taskId = mTaskId >= 0 ? mTaskId : mTaskDisplayArea.getNextRootTaskId();
             if (mParentTask == null) {
-                task = mTaskDisplayArea.createStackUnchecked(
+                task = mTaskDisplayArea.createRootTaskUnchecked(
                         mWindowingMode, mActivityType, taskId, mOnTop, mActivityInfo, mIntent,
                         false /* createdByOrganizer */, false /* deferTaskAppear */,
                         null /* launchCookie */);
@@ -1114,8 +1114,8 @@
                     mSecondary.mRemoteToken.toWindowContainerToken());
             DisplayContent dc = mService.mRootWindowContainer.getDisplayContent(mDisplayId);
             dc.forAllTaskDisplayAreas(taskDisplayArea -> {
-                for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
-                    final Task stack = taskDisplayArea.getStackAt(sNdx);
+                for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) {
+                    final Task stack = taskDisplayArea.getRootTaskAt(sNdx);
                     if (!WindowConfiguration.isSplitScreenWindowingMode(stack.getWindowingMode())) {
                         stack.reparent(mSecondary, POSITION_BOTTOM);
                     }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index dad18ff..baee473 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -53,9 +53,13 @@
     public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
 
     /**
-     * Extra included in {@link #ACTION_CARRIER_CONFIG_CHANGED} to indicate whether this is a
-     * rebroadcast on unlock. Defaults to {@code false} if not specified.
-     * @hide
+     * {@link #ACTION_CARRIER_CONFIG_CHANGED} is broadcast once on device bootup and then again when
+     * the device is unlocked. Direct-Boot-aware applications may use the first broadcast as an
+     * early signal that the carrier config has been loaded, but other applications will only
+     * receive the second broadcast, when the device is unlocked.
+     *
+     * This extra is included in {@link #ACTION_CARRIER_CONFIG_CHANGED} to indicate whether this is
+     * a rebroadcast on unlock.
      */
     public static final String EXTRA_REBROADCAST_ON_UNLOCK =
             "android.telephony.extra.REBROADCAST_ON_UNLOCK";
@@ -2029,8 +2033,16 @@
             "allow_hold_call_during_emergency_bool";
 
     /**
-     * Flag indicating whether the carrier supports RCS presence indication for
-     * User Capability Exchange (UCE).  When presence is supported, the device should use the
+     * Flag indicating whether or not the carrier supports the periodic exchange of phone numbers
+     * in the user's address book with the carrier's presence server in order to retrieve the RCS
+     * capabilities for each contact used in the RCS User Capability Exchange (UCE) procedure. See
+     * RCC.71, section 3 for more information.
+     * <p>
+     * The flag {@link Ims#KEY_ENABLE_PRESENCE_PUBLISH_BOOL} must also be enabled if this flag is
+     * enabled, as sending a periodic SIP PUBLISH with this device's RCS capabilities is a
+     * requirement for capability exchange to begin.
+     * <p>
+     * When presence is supported, the device should use the
      * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE} bit mask and set the
      * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE_VT_CAPABLE} bit to indicate
      * whether each contact supports video calling.  The UI is made aware that presence is enabled
@@ -3846,12 +3858,27 @@
         public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL =
                 KEY_PREFIX + "ims_single_registration_required_bool";
 
+        /**
+         * A boolean flag specifying whether or not this carrier supports the device notifying the
+         * network of its RCS capabilities using the SIP PUBLISH procedure defined for User
+         * Capability Exchange (UCE). See RCC.71, section 3 for more information.
+         * <p>
+         * If this key's value is set to false, the procedure for RCS contact capability exchange
+         * via SIP SUBSCRIBE/NOTIFY will also be disabled internally, and
+         * {@link #KEY_USE_RCS_PRESENCE_BOOL} must also be set to false to ensure apps do not
+         * improperly think that capability exchange via SIP PUBLISH is enabled.
+         * <p> The default value for this key is {@code false}.
+         */
+        public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL =
+                KEY_PREFIX + "enable_presence_publish_bool";
+
         private Ims() {}
 
         private static PersistableBundle getDefaults() {
             PersistableBundle defaults = new PersistableBundle();
             defaults.putInt(KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT, 4000);
             defaults.putBoolean(KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false);
+            defaults.putBoolean(KEY_ENABLE_PRESENCE_PUBLISH_BOOL, false);
             return defaults;
         }
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 9288f79..d963970 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -8105,6 +8105,7 @@
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isInEmergencySmsMode() {
         try {
diff --git a/telephony/java/android/telephony/ims/DelegateMessageCallback.java b/telephony/java/android/telephony/ims/DelegateMessageCallback.java
index beec4a6..0d82a54 100644
--- a/telephony/java/android/telephony/ims/DelegateMessageCallback.java
+++ b/telephony/java/android/telephony/ims/DelegateMessageCallback.java
@@ -17,6 +17,7 @@
 package android.telephony.ims;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.telephony.ims.stub.SipDelegate;
 
 /**
@@ -30,6 +31,7 @@
  * </ul>
  * @hide
  */
+@SystemApi
 public interface DelegateMessageCallback {
 
     /**
diff --git a/telephony/java/android/telephony/ims/DelegateRegistrationState.java b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
index 4facfa7..3558a9b 100644
--- a/telephony/java/android/telephony/ims/DelegateRegistrationState.java
+++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
@@ -18,14 +18,14 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArraySet;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 
@@ -34,6 +34,7 @@
  * ImsService.
  * @hide
  */
+@SystemApi
 public final class DelegateRegistrationState implements Parcelable {
 
     /**
@@ -114,14 +115,14 @@
     })
     public @interface DeregisteringReason {}
 
-    private final ArrayList<String> mRegisteredTags = new ArrayList<>();
-    private final ArrayList<FeatureTagState> mDeregisteringTags = new ArrayList<>();
-    private final ArrayList<FeatureTagState> mDeregisteredTags = new ArrayList<>();
+    private ArraySet<String> mRegisteredTags = new ArraySet<>();
+    private final ArraySet<FeatureTagState> mDeregisteringTags = new ArraySet<>();
+    private final ArraySet<FeatureTagState> mDeregisteredTags = new ArraySet<>();
 
     /**
      * Builder used to create new instances of {@link DelegateRegistrationState}.
      */
-    public static class Builder {
+    public static final class Builder {
 
         private final DelegateRegistrationState mState;
 
@@ -135,10 +136,8 @@
          * @param featureTag The IMS media feature tag included in the current IMS registration.
          * @return The in-progress Builder instance for RegistrationState.
          */
-        public Builder addRegisteredFeatureTag(@NonNull String featureTag) {
-            if (!mState.mRegisteredTags.contains(featureTag)) {
-                mState.mRegisteredTags.add(featureTag);
-            }
+        public @NonNull Builder addRegisteredFeatureTag(@NonNull String featureTag) {
+            mState.mRegisteredTags.add(featureTag);
             return this;
         }
 
@@ -148,7 +147,8 @@
          * @param featureTags The IMS media feature tags included in the current IMS registration.
          * @return The in-progress Builder instance for RegistrationState.
          */
-        public Builder addRegisteredFeatureTags(@NonNull Set<String> featureTags) {
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder addRegisteredFeatureTags(@NonNull Set<String> featureTags) {
             mState.mRegisteredTags.addAll(featureTags);
             return this;
         }
@@ -167,13 +167,9 @@
          *         The availability of the feature tag depends on the {@link DeregisteringReason}.
          * @return The in-progress Builder instance for RegistrationState.
          */
-        public Builder addDeregisteringFeatureTag(@NonNull String featureTag,
+        public @NonNull Builder addDeregisteringFeatureTag(@NonNull String featureTag,
                 @DeregisteringReason int reason) {
-            boolean ftExists = mState.mDeregisteringTags.stream().anyMatch(
-                    f -> f.getFeatureTag().equals(featureTag));
-            if (!ftExists) {
-                mState.mDeregisteringTags.add(new FeatureTagState(featureTag, reason));
-            }
+            mState.mDeregisteringTags.add(new FeatureTagState(featureTag, reason));
             return this;
         }
 
@@ -185,20 +181,16 @@
          * @param reason The reason why the media feature tag has been deregistered.
          * @return The in-progress Builder instance for RegistrationState.
          */
-        public Builder addDeregisteredFeatureTag(@NonNull String featureTag,
+        public @NonNull Builder addDeregisteredFeatureTag(@NonNull String featureTag,
                 @DeregisteredReason int reason) {
-            boolean ftExists = mState.mDeregisteredTags.stream().anyMatch(
-                    f -> f.getFeatureTag().equals(featureTag));
-            if (!ftExists) {
-                mState.mDeregisteredTags.add(new FeatureTagState(featureTag, reason));
-            }
+            mState.mDeregisteredTags.add(new FeatureTagState(featureTag, reason));
             return this;
         }
 
         /**
          * @return the finalized instance.
          */
-        public DelegateRegistrationState build() {
+        public @NonNull DelegateRegistrationState build() {
             return mState;
         }
     }
@@ -212,7 +204,7 @@
      * Used for unparcelling only.
      */
     private DelegateRegistrationState(Parcel source) {
-        source.readList(mRegisteredTags, null /*classloader*/);
+        mRegisteredTags = (ArraySet<String>) source.readArraySet(null);
         readStateFromParcel(source, mDeregisteringTags);
         readStateFromParcel(source, mDeregisteredTags);
     }
@@ -268,7 +260,8 @@
         return new ArraySet<>(mDeregisteredTags);
     }
 
-    public static final Creator<DelegateRegistrationState> CREATOR =
+
+    public static final @NonNull Creator<DelegateRegistrationState> CREATOR =
             new Creator<DelegateRegistrationState>() {
         @Override
         public DelegateRegistrationState createFromParcel(Parcel source) {
@@ -287,13 +280,13 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeList(mRegisteredTags);
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeArraySet(mRegisteredTags);
         writeStateToParcel(dest, mDeregisteringTags);
         writeStateToParcel(dest, mDeregisteredTags);
     }
 
-    private void writeStateToParcel(Parcel dest, List<FeatureTagState> state) {
+    private void writeStateToParcel(Parcel dest, Set<FeatureTagState> state) {
         dest.writeInt(state.size());
         for (FeatureTagState s : state) {
             dest.writeString(s.getFeatureTag());
@@ -301,11 +294,12 @@
         }
     }
 
-    private void readStateFromParcel(Parcel source, List<FeatureTagState> emptyState) {
+    private void readStateFromParcel(Parcel source, Set<FeatureTagState> emptyState) {
         int len = source.readInt();
         for (int i = 0; i < len; i++) {
             String ft = source.readString();
             int reason = source.readInt();
+
             emptyState.add(new FeatureTagState(ft, reason));
         }
     }
diff --git a/telephony/java/android/telephony/ims/DelegateRequest.java b/telephony/java/android/telephony/ims/DelegateRequest.java
index 73d0840..c322d92 100644
--- a/telephony/java/android/telephony/ims/DelegateRequest.java
+++ b/telephony/java/android/telephony/ims/DelegateRequest.java
@@ -17,6 +17,7 @@
 package android.telephony.ims;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.ims.stub.SipDelegate;
@@ -31,6 +32,7 @@
  * SipDelegateConnection given back to the requesting application.
  * @hide
  */
+@SystemApi
 public final class DelegateRequest implements Parcelable {
 
     private final ArrayList<String> mFeatureTags;
@@ -52,7 +54,7 @@
      * @return the list of IMS feature tag associated with this DelegateRequest in the format
      * defined in RCC.07 section 2.6.1.3.
      */
-    public Set<String> getFeatureTags() {
+    public @NonNull Set<String> getFeatureTags() {
         return new ArraySet<>(mFeatureTags);
     }
 
@@ -70,7 +72,7 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeList(mFeatureTags);
     }
 
diff --git a/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java
index 0f1afc4..fb65949 100644
--- a/telephony/java/android/telephony/ims/DelegateStateCallback.java
+++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java
@@ -18,10 +18,11 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.telephony.ims.stub.SipDelegate;
 import android.telephony.ims.stub.SipTransportImplBase;
 
-import java.util.List;
+import java.util.Set;
 
 /**
  * Callback interface to notify a remote application of the following:
@@ -34,26 +35,24 @@
  * </ul>
  * @hide
  */
+@SystemApi
 public interface DelegateStateCallback {
 
     /**
      * This must be called by the ImsService after {@link SipTransportImplBase#createSipDelegate} is
      * called by the framework to notify the framework and remote application that the
      * {@link SipDelegate} has been successfully created.
-     *
-     * @param delegate The SipDelegate created to service the DelegateRequest.
-     * @param deniedTags A List of {@link FeatureTagState}, which contains the feature tags
+     *  @param delegate The SipDelegate created to service the DelegateRequest.
+     * @param deniedTags A Set of {@link FeatureTagState}s, which contain the feature tags
      *    associated with this {@link SipDelegate} that have no access to send/receive SIP messages
      *    as well as a reason for why the feature tag is denied. For more information on the reason
      *    why the feature tag was denied access, see the
      *    {@link SipDelegateManager.DeniedReason} reasons. This is considered a permanent denial due
      *    to this {@link SipDelegate} not supporting a feature or this ImsService already
      *    implementing this feature elsewhere. If all features of this {@link SipDelegate} are
-     *    denied, {@link #onCreated(SipDelegate, List)} should still be called as the framework will
-     *    later call {@link SipTransportImplBase#destroySipDelegate(SipDelegate, int)} to clean the
-     *    delegate up.
+     *    denied, this method should still be called.
      */
-    void onCreated(@NonNull SipDelegate delegate, @Nullable List<FeatureTagState> deniedTags);
+    void onCreated(@NonNull SipDelegate delegate, @Nullable Set<FeatureTagState> deniedTags);
 
     /**
      * This must be called by the ImsService after the framework calls
diff --git a/telephony/java/android/telephony/ims/FeatureTagState.java b/telephony/java/android/telephony/ims/FeatureTagState.java
index 060be6f..3622065 100644
--- a/telephony/java/android/telephony/ims/FeatureTagState.java
+++ b/telephony/java/android/telephony/ims/FeatureTagState.java
@@ -17,6 +17,7 @@
 package android.telephony.ims;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.ims.stub.DelegateConnectionStateCallback;
@@ -39,6 +40,7 @@
  * currently available.
  * @hide
  */
+@SystemApi
 public final class FeatureTagState implements Parcelable {
 
     private final String mFeatureTag;
@@ -48,8 +50,8 @@
      * Associate an IMS feature tag with its current state. See {@link DelegateRegistrationState}
      * and {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged(
      * DelegateRegistrationState, List)} and
-     * {@link DelegateStateCallback#onCreated(SipDelegate, List)} for examples on how and when this
-     * is used.
+     * {@link DelegateStateCallback#onCreated(SipDelegate, java.util.Set)} for examples on how and
+     * when this is used.
      *
      * @param featureTag The IMS feature tag that is deregistered, in the process of
      *                   deregistering, or denied.
@@ -93,12 +95,12 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(mFeatureTag);
         dest.writeInt(mState);
     }
 
-    public static final Creator<FeatureTagState> CREATOR = new Creator<FeatureTagState>() {
+    public static final @NonNull Creator<FeatureTagState> CREATOR = new Creator<FeatureTagState>() {
         @Override
         public FeatureTagState createFromParcel(Parcel source) {
             return new FeatureTagState(source);
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 1b51936..aaa68d6 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -207,6 +208,42 @@
             "android.telephony.ims.extra.RETRY_CALL_FAIL_NETWORKTYPE";
 
     /**
+     * Extra for the call composer call priority, either {@link ImsCallProfile#PRIORITY_NORMAL} or
+     * {@link ImsCallProfile#PRIORITY_URGENT}. It can be set via
+     * {@link #setCallExtraInt(String, int)}.
+     *
+     * Reference: RCC.20 Section 2.4.4.2
+     */
+    public static final String EXTRA_PRIORITY = "android.telephony.ims.extra.PRIORITY";
+
+    // TODO(hallliu) remove the reference to the maximum length and update it later.
+    /**
+     * Extra for the call composer call subject, a string of maximum length 60 characters.
+     * It can be set via {@link #setCallExtra(String, String)}.
+     *
+     * Reference: RCC.20 Section 2.4.3.2
+     */
+    public static final String EXTRA_CALL_SUBJECT = "android.telephony.ims.extra.CALL_SUBJECT";
+
+    /**
+     * Extra for the call composer call location, an {@Link android.location.Location} parcelable
+     * class to represent the geolocation as a latitude and longitude pair. It can be set via
+     * {@link #setCallExtraParcelable(String, Parcelable)}.
+     *
+     * Reference: RCC.20 Section 2.4.3.2
+     */
+    public static final String EXTRA_LOCATION = "android.telephony.ims.extra.LOCATION";
+
+    /**
+     * Extra for the call composer picture URL, a String that indicates the URL on the carrier’s
+     * server infrastructure to get the picture. It can be set via
+     * {@link #setCallExtra(String, String)}.
+     *
+     * Reference: RCC.20 Section 2.4.3.2
+     */
+    public static final String EXTRA_PICTURE_URL = "android.telephony.ims.extra.PICTURE_URL";
+
+    /**
      * Values for EXTRA_OIR / EXTRA_CNAP
      */
     /**
@@ -244,6 +281,21 @@
      */
     public static final int DIALSTRING_USSD = 2;
 
+    // Values for EXTRA_PRIORITY
+    /**
+     * Indicates the call composer call priority is normal.
+     *
+     * Reference: RCC.20 Section 2.4.4.2
+     */
+    public static final int PRIORITY_NORMAL = 0;
+
+    /**
+     * Indicates the call composer call priority is urgent.
+     *
+     * Reference: RCC.20 Section 2.4.4.2
+     */
+    public static final int PRIORITY_URGENT = 1;
+
     /**
      * Call is not restricted on peer side and High Definition media is supported
      */
@@ -588,6 +640,19 @@
         return mCallExtras.getInt(name, defaultValue);
     }
 
+    /**
+     * Get the call extras (Parcelable), given the extra name.
+     * @param name call extra name
+     * @return the corresponding call extra Parcelable or null if not applicable
+     */
+    @Nullable
+    public <T extends Parcelable> T getCallExtraParcelable(@Nullable String name) {
+        if (mCallExtras != null) {
+            return mCallExtras.getParcelable(name);
+        }
+        return null;
+    }
+
     public void setCallExtra(String name, String value) {
         if (mCallExtras != null) {
             mCallExtras.putString(name, value);
@@ -607,6 +672,17 @@
     }
 
     /**
+     * Set the call extra value (Parcelable), given the call extra name.
+     * @param name call extra name
+     * @param parcelable call extra value
+     */
+    public void setCallExtraParcelable(@NonNull String name, @NonNull Parcelable parcelable) {
+        if (mCallExtras != null) {
+            mCallExtras.putParcelable(name, parcelable);
+        }
+    }
+
+    /**
      * Set the call restrict cause, which provides the reason why a call has been restricted from
      * using High Definition media.
      */
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index d12a6ae..5848be8 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -105,10 +105,17 @@
     public @interface RequestResult {}
 
     /**
+     * The base class of {@link OptionsBuilder} and {@link PresenceBuilder}
+     */
+    public static abstract class RcsUcsCapabilityBuilder {
+        public abstract @NonNull RcsContactUceCapability build();
+    }
+
+    /**
      * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
      * queried through SIP OPTIONS.
      */
-    public static class OptionsBuilder {
+    public static class OptionsBuilder extends RcsUcsCapabilityBuilder {
 
         private final RcsContactUceCapability mCapabilities;
 
@@ -155,6 +162,7 @@
         /**
          * @return the constructed instance.
          */
+        @Override
         public @NonNull RcsContactUceCapability build() {
             return mCapabilities;
         }
@@ -164,7 +172,7 @@
      * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
      * queried through a presence server.
      */
-    public static class PresenceBuilder {
+    public static class PresenceBuilder extends RcsUcsCapabilityBuilder {
 
         private final RcsContactUceCapability mCapabilities;
 
@@ -205,6 +213,7 @@
         /**
          * @return the RcsContactUceCapability instance.
          */
+        @Override
         public @NonNull RcsContactUceCapability build() {
             return mCapabilities;
         }
diff --git a/telephony/java/android/telephony/ims/SipDelegateConnection.java b/telephony/java/android/telephony/ims/SipDelegateConnection.java
index 6bfdc2c..c3cc1ed 100644
--- a/telephony/java/android/telephony/ims/SipDelegateConnection.java
+++ b/telephony/java/android/telephony/ims/SipDelegateConnection.java
@@ -17,6 +17,7 @@
 package android.telephony.ims;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.telephony.ims.stub.SipDelegate;
 
 /**
@@ -36,6 +37,7 @@
  * @see SipDelegateManager#createSipDelegate
  * @hide
  */
+@SystemApi
 public interface SipDelegateConnection {
 
     /**
@@ -47,9 +49,8 @@
      * @param sipMessage The SipMessage to be sent.
      * @param configVersion The SipDelegateImsConfiguration version used to construct the
      *                      SipMessage. See {@link SipDelegateImsConfiguration#getVersion} for more
-     *                      information on this parameter and why it is used.
      */
-    void sendMessage(@NonNull SipMessage sipMessage, int configVersion);
+    void sendMessage(@NonNull SipMessage sipMessage, long configVersion);
 
     /**
      * Notify the {@link SipDelegate} that a SIP message received from
diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
index 8abd0ee..eddbb10 100644
--- a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
+++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
@@ -17,7 +17,10 @@
 package android.telephony.ims;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.StringDef;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
@@ -47,7 +50,8 @@
  * update.
  * @hide
  */
-public class SipDelegateImsConfiguration implements Parcelable {
+@SystemApi
+public final class SipDelegateImsConfiguration implements Parcelable {
 
     /**
      * IPV4 Address type.
@@ -354,7 +358,7 @@
     /**
      * Builder class to be used when constructing a new SipDelegateImsConfiguration.
      */
-    public static class Builder {
+    public static final class Builder {
         private final long mVersion;
         private final PersistableBundle mBundle;
 
@@ -381,7 +385,10 @@
         /**
          * Put a string value into this configuration bundle for the given key.
          */
-        public Builder putString(@StringConfigKey String key, String value) {
+        // getString is available below.
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder addString(@NonNull @StringConfigKey String key,
+                @NonNull String value) {
             mBundle.putString(key, value);
             return this;
         }
@@ -389,7 +396,9 @@
         /**
          * Replace the existing default value with a new value for a given key.
          */
-        public Builder putInt(@IntConfigKey String key, int value) {
+        // getInt is available below.
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder addInt(@NonNull @IntConfigKey String key, int value) {
             mBundle.putInt(key, value);
             return this;
         }
@@ -397,7 +406,9 @@
         /**
          * Replace the existing default value with a new value for a given key.
          */
-        public Builder putBoolean(@BooleanConfigKey String key, boolean value) {
+        // getBoolean is available below.
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder addBoolean(@NonNull @BooleanConfigKey String key, boolean value) {
             mBundle.putBoolean(key, value);
             return this;
         }
@@ -405,7 +416,7 @@
         /**
          * @return a new SipDelegateImsConfiguration from this Builder.
          */
-        public SipDelegateImsConfiguration build() {
+        public @NonNull SipDelegateImsConfiguration build() {
             return new SipDelegateImsConfiguration(mVersion, mBundle);
         }
     }
@@ -424,30 +435,38 @@
     }
 
     /**
+     * @return {@code true} if this configuration object has a an entry for the key specified,
+     * {@code false} if it does not.
+     */
+    public boolean containsKey(@NonNull String key) {
+        return mBundle.containsKey(key);
+    }
+
+    /**
      * @return the string value associated with a given key or {@code null} if it doesn't exist.
      */
-    public @StringConfigKey String getString(String key) {
+    public @Nullable @StringConfigKey String getString(@NonNull String key) {
         return mBundle.getString(key);
     }
 
     /**
-     * @return the Integer value associated with a given key or {@code null} if the value doesn't
-     * exist.
+     * @return the integer value associated with a given key if it exists or the supplied default
+     * value if it does not.
      */
-    public @IntConfigKey Integer getInt(String key) {
+    public @IntConfigKey int getInt(@NonNull String key, int defaultValue) {
         if (!mBundle.containsKey(key)) {
-            return null;
+            return defaultValue;
         }
         return mBundle.getInt(key);
     }
 
     /**
-     * @return the Integer value associated with a given key or {@code null} if the value doesn't
-     * exist.
+     * @return the boolean value associated with a given key or the supplied default value if the
+     * value doesn't exist in the bundle.
      */
-    public @BooleanConfigKey Boolean getBoolen(String key) {
+    public @BooleanConfigKey boolean getBoolean(@NonNull String key, boolean defaultValue) {
         if (!mBundle.containsKey(key)) {
-            return null;
+            return defaultValue;
         }
         return mBundle.getBoolean(key);
     }
@@ -455,7 +474,7 @@
     /**
      * @return a shallow copy of the full configuration.
      */
-    public PersistableBundle copyBundle() {
+    public @NonNull PersistableBundle copyBundle() {
         return new PersistableBundle(mBundle);
     }
 
@@ -479,12 +498,12 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeLong(mVersion);
         dest.writePersistableBundle(mBundle);
     }
 
-    public static final Creator<SipDelegateImsConfiguration> CREATOR =
+    public static final @NonNull Creator<SipDelegateImsConfiguration> CREATOR =
             new Creator<SipDelegateImsConfiguration>() {
         @Override
         public SipDelegateImsConfiguration createFromParcel(Parcel source) {
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 190a792..2ec88ff 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -54,7 +54,6 @@
      * The SIP message has failed being sent or received for an unknown reason.
      * <p>
      * The caller should retry a message that failed with this response.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_UNKNOWN = 0;
 
@@ -64,47 +63,40 @@
      * <p>
      * This is considered a permanent error and the system will automatically begin the teardown and
      * destruction of the SipDelegate. No further messages should be sent on this transport.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_DELEGATE_DEAD = 1;
 
     /**
      * The message has not been sent/received because the delegate is in the process of closing and
      * has become unavailable. No further messages should be sent/received on this delegate.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_DELEGATE_CLOSED = 2;
 
     /**
      * The SIP message has an invalid start line and the message can not be sent.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_INVALID_START_LINE = 3;
 
     /**
      * One or more of the header fields in the header section of the outgoing SIP message is invalid
      * and the SIP message can not be sent.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS = 4;
 
     /**
      * The body content of the SIP message is invalid and the message can not be sent.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT = 5;
 
     /**
      * The feature tag associated with the outgoing message does not match any known feature tags
      * and this message can not be sent.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG = 6;
 
     /**
      * The feature tag associated with the outgoing message is not enabled for the associated
      * SipDelegateConnection and can not be sent.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE = 7;
 
@@ -113,7 +105,6 @@
      * <p>
      * This message should be retried when connectivity to the network is re-established. See
      * {@link android.net.ConnectivityManager.NetworkCallback} for how this can be determined.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE = 8;
 
@@ -124,7 +115,6 @@
      * This is considered a temporary failure, the message should not be retried until an IMS
      * registration change callback is received via
      * {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged}
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_NOT_REGISTERED = 9;
 
@@ -135,7 +125,6 @@
      * <p>
      * The @link SipMessage} should be recreated using the newest
      * {@link SipDelegateImsConfiguration} and sent again.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION = 10;
 
@@ -146,7 +135,6 @@
      * This is considered a temporary error and the {@link SipDelegateConnection} should resend the
      * message once {@link DelegateRegistrationState#DEREGISTERING_REASON_FEATURE_TAGS_CHANGING} is
      * no longer reported.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION = 11;
 
@@ -171,7 +159,6 @@
 
     /**
      * Access to use this feature tag has been denied for an unknown reason.
-     * @hide
      */
     public static final int DENIED_REASON_UNKNOWN = 0;
 
@@ -179,14 +166,12 @@
      * This feature tag is allowed to be used by this SipDelegateConnection, but it is in use by
      * another SipDelegateConnection and can not be associated with this delegate. The feature tag
      * will stay in this state until the feature tag is release by the other application.
-     * @hide
      */
     public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1;
 
     /**
      * Access to use this feature tag has been denied because this application does not have the
      * permissions required to access this feature tag.
-     * @hide
      */
     public static final int DENIED_REASON_NOT_ALLOWED = 2;
 
@@ -194,14 +179,12 @@
      * Access to use this feature tag has been denied because single registration is not allowed by
      * the carrier at this time. The application should fall back to dual registration if
      * applicable.
-     * @hide
      */
     public static final int DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED = 3;
 
     /**
      * This feature tag is not recognized as a valid feature tag by the SipDelegate and has been
      * denied.
-     * @hide
      */
     public static final int DENIED_REASON_INVALID = 4;
 
@@ -218,33 +201,28 @@
 
     /**
      * The SipDelegate has closed due to an unknown reason.
-     * @hide
      */
     public static final int SIP_DELEGATE_DESTROY_REASON_UNKNOWN = 0;
 
     /**
      * The SipDelegate has closed because the IMS service has died unexpectedly.
-     * @hide
      */
     public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD = 1;
 
     /**
      * The SipDelegate has closed because the IMS application has requested that the connection be
      * destroyed.
-     * @hide
      */
     public static final int SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP = 2;
 
     /**
      * The SipDelegate has been closed due to the user disabling RCS.
-     * @hide
      */
     public static final int SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS = 3;
 
     /**
      * The SipDelegate has been closed due to the subscription associated with this delegate being
      * torn down.
-     * @hide
      */
     public static final int SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN = 4;
 
@@ -331,7 +309,6 @@
      *           SipDelegateConnection.
      * @throws ImsException Thrown if there was a problem communicating with the ImsService
      * associated with this SipDelegateManager. See {@link ImsException#getCode()}.
-     * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void createSipDelegate(@NonNull DelegateRequest request, @NonNull Executor executor,
@@ -366,7 +343,6 @@
      * This will also clean up all related callbacks in the associated ImsService.
      * @param delegateConnection The SipDelegateConnection to destroy.
      * @param reason The reason for why this SipDelegateConnection was destroyed.
-     * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void destroySipDelegate(@NonNull SipDelegateConnection delegateConnection,
diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java
index c3b1be2..1539224 100644
--- a/telephony/java/android/telephony/ims/SipMessage.java
+++ b/telephony/java/android/telephony/ims/SipMessage.java
@@ -17,10 +17,14 @@
 package android.telephony.ims;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Arrays;
+import java.util.Objects;
+
 /**
  * Represents a partially encoded SIP message. See RFC 3261 for more information on how SIP
  * messages are structured and used.
@@ -29,6 +33,7 @@
  * verification and should not be used as a generic SIP message container.
  * @hide
  */
+@SystemApi
 public final class SipMessage implements Parcelable {
     // Should not be set to true for production!
     private static final boolean IS_DEBUGGING = Build.IS_ENG;
@@ -95,14 +100,14 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(mStartLine);
         dest.writeString(mHeaderSection);
         dest.writeInt(mContent.length);
         dest.writeByteArray(mContent);
     }
 
-    public static final Creator<SipMessage> CREATOR = new Creator<SipMessage>() {
+    public static final @NonNull Creator<SipMessage> CREATOR = new Creator<SipMessage>() {
         @Override
         public SipMessage createFromParcel(Parcel source) {
             return new SipMessage(source);
@@ -152,4 +157,21 @@
         }
         return startLine;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SipMessage that = (SipMessage) o;
+        return mStartLine.equals(that.mStartLine)
+                && mHeaderSection.equals(that.mHeaderSection)
+                && Arrays.equals(mContent, that.mContent);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(mStartLine, mHeaderSection);
+        result = 31 * result + Arrays.hashCode(mContent);
+        return result;
+    }
 }
diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl
index 477ee95..5d6766a6 100644
--- a/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl
@@ -23,7 +23,7 @@
  * {@hide}
  */
 oneway interface ISipDelegate {
-    void sendMessage(in SipMessage sipMessage, int configVersion);
+    void sendMessage(in SipMessage sipMessage, long configVersion);
     void notifyMessageReceived(in String viaTransactionId);
     void notifyMessageReceiveError(in String viaTransactionId, int reason);
 
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
index a7f62cc..522ad81 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
@@ -29,12 +29,13 @@
 import android.telephony.ims.SipMessage;
 import android.telephony.ims.stub.SipDelegate;
 
-import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
  * Implementation of callbacks by wrapping the internal AIDL from telephony. Also implements
- * ISipDelegate internally when {@link DelegateStateCallback#onCreated(SipDelegate, List)} is called
+ * ISipDelegate internally when {@link DelegateStateCallback#onCreated(SipDelegate, Set)} is called
  * in order to trampoline events back to telephony.
  * @hide
  */
@@ -42,7 +43,7 @@
 
     private final ISipDelegate.Stub mDelegateBinder = new ISipDelegate.Stub() {
         @Override
-        public void sendMessage(SipMessage sipMessage, int configVersion) {
+        public void sendMessage(SipMessage sipMessage, long configVersion) {
             SipDelegate d = mDelegate;
             final long token = Binder.clearCallingIdentity();
             try {
@@ -136,10 +137,10 @@
 
     @Override
     public void onCreated(@NonNull SipDelegate delegate,
-            @Nullable List<FeatureTagState> deniedTags) {
+            @Nullable Set<FeatureTagState> deniedTags) {
         mDelegate = delegate;
         try {
-            mStateBinder.onCreated(mDelegateBinder, deniedTags);
+            mStateBinder.onCreated(mDelegateBinder, new ArrayList<>(deniedTags));
         } catch (RemoteException e) {
             // BinderDied will trigger destroySipDelegate, so just ignore this locally.
         }
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
index 3bd1a46..29ba8e2 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
@@ -158,7 +158,7 @@
     }
 
     @Override
-    public void sendMessage(SipMessage sipMessage, int configVersion) {
+    public void sendMessage(SipMessage sipMessage, long configVersion) {
         try {
             ISipDelegate conn = getSipDelegateBinder();
             if (conn == null) {
diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java
index 59f9601..eefe849 100644
--- a/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java
+++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java
@@ -17,6 +17,7 @@
 package android.telephony.ims.stub;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.telephony.ims.SipDelegateConnection;
 import android.telephony.ims.SipDelegateManager;
 import android.telephony.ims.SipMessage;
@@ -26,6 +27,7 @@
  * messages as well as the result of sending a SIP message.
  * @hide
  */
+@SystemApi
 public interface DelegateConnectionMessageCallback {
 
     /**
@@ -49,6 +51,6 @@
      *                         previously sent {@link SipMessage}.
      * @param reason The reason for the failure.
      */
-    void onMessageSendFailure(String viaTransactionId,
+    void onMessageSendFailure(@NonNull String viaTransactionId,
             @SipDelegateManager.MessageFailureReason int reason);
 }
diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
index 9761805..02218ea 100644
--- a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
+++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
@@ -17,6 +17,7 @@
 package android.telephony.ims.stub;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.telephony.ims.DelegateRegistrationState;
 import android.telephony.ims.DelegateRequest;
 import android.telephony.ims.FeatureTagState;
@@ -58,6 +59,7 @@
  *
  * @hide
  */
+@SystemApi
 public interface DelegateConnectionStateCallback {
 
     /**
diff --git a/telephony/java/android/telephony/ims/stub/SipDelegate.java b/telephony/java/android/telephony/ims/stub/SipDelegate.java
index 3ec9709..d7e7b62 100644
--- a/telephony/java/android/telephony/ims/stub/SipDelegate.java
+++ b/telephony/java/android/telephony/ims/stub/SipDelegate.java
@@ -17,6 +17,7 @@
 package android.telephony.ims.stub;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.telephony.ims.DelegateMessageCallback;
 import android.telephony.ims.ImsService;
 import android.telephony.ims.SipDelegateImsConfiguration;
@@ -40,6 +41,7 @@
  * {@link android.telephony.ims.DelegateStateCallback} for more information.
  * @hide
  */
+@SystemApi
 public interface SipDelegate {
 
     /**
@@ -57,7 +59,7 @@
      *         {@link DelegateMessageCallback#onMessageSendFailure} should be called with code
      *         {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION}.
      */
-    void sendMessage(@NonNull SipMessage message, int configVersion);
+    void sendMessage(@NonNull SipMessage message, long configVersion);
 
     /**
      * The framework is requesting that routing resources associated with the SIP dialog using the
diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
index 93d438c..1f74c09 100644
--- a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
@@ -17,6 +17,7 @@
 package android.telephony.ims.stub;
 
 import android.annotation.NonNull;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.os.Binder;
 import android.os.IBinder;
@@ -32,7 +33,6 @@
 import android.util.Log;
 
 import java.util.ArrayList;
-import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Executor;
 
@@ -99,7 +99,8 @@
     /**
      * Called by the Telephony framework to request the creation of a new {@link SipDelegate}.
      * <p>
-     * The implementation must call {@link DelegateStateCallback#onCreated(SipDelegate, List)} with
+     * The implementation must call
+     * {@link DelegateStateCallback#onCreated(SipDelegate, java.util.Set)} with
      * the {@link SipDelegate} that is associated with the {@link DelegateRequest}.
      * <p>
      * This method will be called on the Executor specified in
@@ -112,8 +113,9 @@
      *           for the SipDelegate.
      * @param mc A callback back to the remote application to be used to send SIP messages to the
      *           remote application and acknowledge the sending of outgoing SIP messages.
-     * @hide
      */
+    // executor used is defined in the constructor.
+    @SuppressLint("ExecutorRegistration")
     public void createSipDelegate(int subscriptionId, @NonNull DelegateRequest request,
             @NonNull DelegateStateCallback dc, @NonNull DelegateMessageCallback mc) {
         throw new UnsupportedOperationException("createSipDelegate not implemented!");
@@ -130,7 +132,6 @@
      * @param delegate The delegate to be destroyed.
      * @param reason The reason the remote connection to this {@link SipDelegate} is being
      *         destroyed.
-     * @hide
      */
     public void destroySipDelegate(@NonNull SipDelegate delegate,
             @SipDelegateManager.SipDelegateDestroyReason int reason) {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index b524549..5d4fdd0 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -857,6 +857,11 @@
             in int[] featureTypes, in String packageName);
 
     /**
+     *  @return true if the ImsService cleared any carrier ImsService overrides, false otherwise.
+     */
+    boolean clearCarrierImsServiceOverride(int slotIndex);
+
+    /**
     * @return the package name of the carrier/device ImsService associated with this slot.
     */
     String getBoundImsServicePackage(int slotIndex, boolean isCarrierImsService, int featureType);
diff --git a/tests/BootImageProfileTest/TEST_MAPPING b/tests/BootImageProfileTest/TEST_MAPPING
new file mode 100644
index 0000000..1b569f9
--- /dev/null
+++ b/tests/BootImageProfileTest/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "BootImageProfileTest"
+    }
+  ]
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 1f03c4d..686ddcb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -105,8 +105,7 @@
                                 configuration.endRotation)
                             navBarLayerIsAlwaysVisible(enabled = false)
                             statusBarLayerIsAlwaysVisible(enabled = false)
-                            visibleLayersShownMoreThanOneConsecutiveEntry(
-                                    enabled = Surface.ROTATION_0 == configuration.endRotation)
+                            visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 174541970)
 
                             appLayerReplacesWallpaperLayer(testApp)
                         }
diff --git a/tests/RollbackTest/RollbackTest/AndroidManifest.xml b/tests/RollbackTest/RollbackTest/AndroidManifest.xml
index 9274da2..590105b 100644
--- a/tests/RollbackTest/RollbackTest/AndroidManifest.xml
+++ b/tests/RollbackTest/RollbackTest/AndroidManifest.xml
@@ -19,8 +19,6 @@
 
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
     <application>
-        <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
-                  android:exported="true" />
         <uses-library android:name="android.test.runner" />
     </application>
 
diff --git a/tests/StagedInstallTest/app/AndroidManifest.xml b/tests/StagedInstallTest/app/AndroidManifest.xml
index a678f1ec..d7ac9d0 100644
--- a/tests/StagedInstallTest/app/AndroidManifest.xml
+++ b/tests/StagedInstallTest/app/AndroidManifest.xml
@@ -20,8 +20,6 @@
 
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
     <application>
-        <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
-                  android:exported="true" />
         <uses-library android:name="android.test.runner" />
     </application>
 
diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
index c9161b6..cade5ba 100644
--- a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
+++ b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
@@ -26,9 +26,9 @@
 import android.util.SparseArray;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import com.android.testutils.DevSdkIgnoreRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -38,7 +38,7 @@
 import java.util.List;
 
 @IgnoreUpTo(Build.VERSION_CODES.R)
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
 @SmallTest
 public class OemNetworkPreferencesTest {
 
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index ba87dc5..5d45737 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -56,8 +56,10 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
@@ -5398,6 +5400,106 @@
     }
 
     @Test
+    public void testApplyUnderlyingCapabilities() throws Exception {
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mCellNetworkAgent.connect(false /* validated */);
+        mWiFiNetworkAgent.connect(false /* validated */);
+
+        final NetworkCapabilities cellNc = new NetworkCapabilities()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+                .setLinkDownstreamBandwidthKbps(10);
+        final NetworkCapabilities wifiNc = new NetworkCapabilities()
+                .addTransportType(TRANSPORT_WIFI)
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .addCapability(NET_CAPABILITY_NOT_METERED)
+                .addCapability(NET_CAPABILITY_NOT_ROAMING)
+                .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+                .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
+                .setLinkUpstreamBandwidthKbps(20);
+        mCellNetworkAgent.setNetworkCapabilities(cellNc, true /* sendToConnectivityService */);
+        mWiFiNetworkAgent.setNetworkCapabilities(wifiNc, true /* sendToConnectivityService */);
+        waitForIdle();
+
+        final Network mobile = mCellNetworkAgent.getNetwork();
+        final Network wifi = mWiFiNetworkAgent.getNetwork();
+
+        final NetworkCapabilities initialCaps = new NetworkCapabilities();
+        initialCaps.addCapability(NET_CAPABILITY_INTERNET);
+        initialCaps.removeCapability(NET_CAPABILITY_NOT_VPN);
+
+        final NetworkCapabilities withNoUnderlying = new NetworkCapabilities();
+        withNoUnderlying.addCapability(NET_CAPABILITY_INTERNET);
+        withNoUnderlying.addCapability(NET_CAPABILITY_NOT_CONGESTED);
+        withNoUnderlying.addCapability(NET_CAPABILITY_NOT_ROAMING);
+        withNoUnderlying.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+        withNoUnderlying.addTransportType(TRANSPORT_VPN);
+        withNoUnderlying.removeCapability(NET_CAPABILITY_NOT_VPN);
+
+        final NetworkCapabilities withMobileUnderlying = new NetworkCapabilities(withNoUnderlying);
+        withMobileUnderlying.addTransportType(TRANSPORT_CELLULAR);
+        withMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_ROAMING);
+        withMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
+        withMobileUnderlying.setLinkDownstreamBandwidthKbps(10);
+
+        final NetworkCapabilities withWifiUnderlying = new NetworkCapabilities(withNoUnderlying);
+        withWifiUnderlying.addTransportType(TRANSPORT_WIFI);
+        withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED);
+        withWifiUnderlying.setLinkUpstreamBandwidthKbps(20);
+
+        final NetworkCapabilities withWifiAndMobileUnderlying =
+                new NetworkCapabilities(withNoUnderlying);
+        withWifiAndMobileUnderlying.addTransportType(TRANSPORT_CELLULAR);
+        withWifiAndMobileUnderlying.addTransportType(TRANSPORT_WIFI);
+        withWifiAndMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED);
+        withWifiAndMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_ROAMING);
+        withWifiAndMobileUnderlying.setLinkDownstreamBandwidthKbps(10);
+        withWifiAndMobileUnderlying.setLinkUpstreamBandwidthKbps(20);
+
+        NetworkCapabilities caps = new NetworkCapabilities(initialCaps);
+        final boolean notDeclaredMetered = false;
+        mService.applyUnderlyingCapabilities(new Network[]{}, caps, notDeclaredMetered);
+        assertEquals(withNoUnderlying, caps);
+
+        caps = new NetworkCapabilities(initialCaps);
+        mService.applyUnderlyingCapabilities(new Network[]{null}, caps, notDeclaredMetered);
+        assertEquals(withNoUnderlying, caps);
+
+        caps = new NetworkCapabilities(initialCaps);
+        mService.applyUnderlyingCapabilities(new Network[]{mobile}, caps, notDeclaredMetered);
+        assertEquals(withMobileUnderlying, caps);
+
+        mService.applyUnderlyingCapabilities(new Network[]{wifi}, caps, notDeclaredMetered);
+        assertEquals(withWifiUnderlying, caps);
+
+        final boolean isDeclaredMetered = true;
+        withWifiUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED);
+        caps = new NetworkCapabilities(initialCaps);
+        mService.applyUnderlyingCapabilities(new Network[]{wifi}, caps, isDeclaredMetered);
+        assertEquals(withWifiUnderlying, caps);
+
+        caps = new NetworkCapabilities(initialCaps);
+        mService.applyUnderlyingCapabilities(new Network[]{mobile, wifi}, caps, isDeclaredMetered);
+        assertEquals(withWifiAndMobileUnderlying, caps);
+
+        withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED);
+        caps = new NetworkCapabilities(initialCaps);
+        mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi},
+                caps, notDeclaredMetered);
+        assertEquals(withWifiAndMobileUnderlying, caps);
+
+        caps = new NetworkCapabilities(initialCaps);
+        mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi},
+                caps, notDeclaredMetered);
+        assertEquals(withWifiAndMobileUnderlying, caps);
+
+        mService.applyUnderlyingCapabilities(null, caps, notDeclaredMetered);
+        assertEquals(withWifiUnderlying, caps);
+    }
+
+    @Test
     public void testVpnConnectDisconnectUnderlyingNetwork() throws Exception {
         final TestNetworkCallback callback = new TestNetworkCallback();
         final NetworkRequest request = new NetworkRequest.Builder()
@@ -5947,17 +6049,28 @@
                 && caps.hasTransport(TRANSPORT_VPN)
                 && caps.hasTransport(TRANSPORT_WIFI));
 
+        // Change the VPN's capabilities somehow (specifically, disconnect wifi).
+        mWiFiNetworkAgent.disconnect();
+        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        callback.expectCapabilitiesThat(mMockVpn, (caps)
+                -> caps.getUids().size() == 2
+                && caps.getUids().contains(new UidRange(uid, uid))
+                && caps.getUids().contains(UidRange.createForUser(restrictedUserId))
+                && caps.hasTransport(TRANSPORT_VPN)
+                && !caps.hasTransport(TRANSPORT_WIFI));
+
         // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
         final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
         removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId);
         handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
 
-        // Expect that the VPN gains the UID range for the restricted user.
+        // Expect that the VPN gains the UID range for the restricted user, and that the capability
+        // change made just before that (i.e., loss of TRANSPORT_WIFI) is preserved.
         callback.expectCapabilitiesThat(mMockVpn, (caps)
                 -> caps.getUids().size() == 1
                 && caps.getUids().contains(new UidRange(uid, uid))
                 && caps.hasTransport(TRANSPORT_VPN)
-                && caps.hasTransport(TRANSPORT_WIFI));
+                && !caps.hasTransport(TRANSPORT_WIFI));
     }
 
     @Test
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index d0db55f..337507a 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -21,15 +21,6 @@
 import static android.content.pm.UserInfo.FLAG_PRIMARY;
 import static android.content.pm.UserInfo.FLAG_RESTRICTED;
 import static android.net.ConnectivityManager.NetworkCallback;
-import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.NetworkCapabilities.TRANSPORT_VPN;
-import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -621,102 +612,6 @@
         order.verify(mNotificationManager).cancel(anyString(), anyInt());
     }
 
-    @Test
-    public void testCapabilities() {
-        setMockedUsers(primaryUser);
-
-        final Network mobile = new Network(1);
-        final Network wifi = new Network(2);
-
-        final Map<Network, NetworkCapabilities> networks = new HashMap<>();
-        networks.put(
-                mobile,
-                new NetworkCapabilities()
-                        .addTransportType(TRANSPORT_CELLULAR)
-                        .addCapability(NET_CAPABILITY_INTERNET)
-                        .addCapability(NET_CAPABILITY_NOT_CONGESTED)
-                        .setLinkDownstreamBandwidthKbps(10));
-        networks.put(
-                wifi,
-                new NetworkCapabilities()
-                        .addTransportType(TRANSPORT_WIFI)
-                        .addCapability(NET_CAPABILITY_INTERNET)
-                        .addCapability(NET_CAPABILITY_NOT_METERED)
-                        .addCapability(NET_CAPABILITY_NOT_ROAMING)
-                        .addCapability(NET_CAPABILITY_NOT_CONGESTED)
-                        .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
-                        .setLinkUpstreamBandwidthKbps(20));
-        setMockedNetworks(networks);
-
-        final NetworkCapabilities caps = new NetworkCapabilities();
-
-        Vpn.applyUnderlyingCapabilities(
-                mConnectivityManager, new Network[] {}, caps, false /* isAlwaysMetered */);
-        assertTrue(caps.hasTransport(TRANSPORT_VPN));
-        assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
-        assertFalse(caps.hasTransport(TRANSPORT_WIFI));
-        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps());
-        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps());
-        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
-        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
-        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
-        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
-
-        Vpn.applyUnderlyingCapabilities(
-                mConnectivityManager,
-                new Network[] {mobile},
-                caps,
-                false /* isAlwaysMetered */);
-        assertTrue(caps.hasTransport(TRANSPORT_VPN));
-        assertTrue(caps.hasTransport(TRANSPORT_CELLULAR));
-        assertFalse(caps.hasTransport(TRANSPORT_WIFI));
-        assertEquals(10, caps.getLinkDownstreamBandwidthKbps());
-        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps());
-        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
-        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
-        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
-        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
-
-        Vpn.applyUnderlyingCapabilities(
-                mConnectivityManager, new Network[] {wifi}, caps, false /* isAlwaysMetered */);
-        assertTrue(caps.hasTransport(TRANSPORT_VPN));
-        assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
-        assertTrue(caps.hasTransport(TRANSPORT_WIFI));
-        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps());
-        assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
-        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
-        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
-        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
-        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
-
-        Vpn.applyUnderlyingCapabilities(
-                mConnectivityManager, new Network[] {wifi}, caps, true /* isAlwaysMetered */);
-        assertTrue(caps.hasTransport(TRANSPORT_VPN));
-        assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
-        assertTrue(caps.hasTransport(TRANSPORT_WIFI));
-        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps());
-        assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
-        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
-        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
-        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
-        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
-
-        Vpn.applyUnderlyingCapabilities(
-                mConnectivityManager,
-                new Network[] {mobile, wifi},
-                caps,
-                false /* isAlwaysMetered */);
-        assertTrue(caps.hasTransport(TRANSPORT_VPN));
-        assertTrue(caps.hasTransport(TRANSPORT_CELLULAR));
-        assertTrue(caps.hasTransport(TRANSPORT_WIFI));
-        assertEquals(10, caps.getLinkDownstreamBandwidthKbps());
-        assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
-        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
-        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
-        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
-        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
-    }
-
     /**
      * The profile name should NOT change between releases for backwards compatibility
      *
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 852b1244..768b4b2 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -190,8 +190,11 @@
     AddOptionalFlag("--version-name",
         "Version name to inject into the AndroidManifest.xml if none is present.",
         &options_.manifest_fixer_options.version_name_default);
+    AddOptionalFlag("--revision-code",
+        "Revision code (integer) to inject into the AndroidManifest.xml if none is\n"
+            "present.", &options_.manifest_fixer_options.revision_code_default);
     AddOptionalSwitch("--replace-version",
-        "If --version-code and/or --version-name are specified, these\n"
+        "If --version-code, --version-name, and/or --revision-code are specified, these\n"
             "values will replace any value already in the manifest. By\n"
             "default, nothing is changed if the manifest already defines\n"
             "these attributes.",
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index c03661c..8abd9de 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -367,6 +367,16 @@
       }
     }
 
+    if (options_.revision_code_default) {
+      if (options_.replace_version) {
+        el->RemoveAttribute(xml::kSchemaAndroid, "revisionCode");
+      }
+      if (el->FindAttribute(xml::kSchemaAndroid, "revisionCode") == nullptr) {
+        el->attributes.push_back(xml::Attribute{xml::kSchemaAndroid, "revisionCode",
+                                                options_.revision_code_default.value()});
+      }
+    }
+
     return true;
   });
 
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index ec4367b..34ad8d5 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -60,6 +60,10 @@
   // replace_version is set.
   Maybe<std::string> version_code_major_default;
 
+  // The revision code to set if 'android:revisionCode' is not defined in <manifest> or if
+  // replace_version is set.
+  Maybe<std::string> revision_code_default;
+
   // The version of the framework being compiled against to set for 'android:compileSdkVersion' in
   // the <manifest> tag.
   Maybe<std::string> compile_sdk_version;
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 781ff7b..432f10b 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -445,6 +445,66 @@
   EXPECT_THAT(attr->value, StrEq("0x20000000"));
 }
 
+TEST_F(ManifestFixerTest, UseDefaultRevisionCode) {
+  ManifestFixerOptions options;
+  options.revision_code_default = std::string("0x10000000");
+
+  std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+                package="android"
+                android:versionCode="0x00000001" />)EOF",
+                                                            options);
+  ASSERT_THAT(doc, NotNull());
+
+  xml::Element* manifest_el = doc->root.get();
+  ASSERT_THAT(manifest_el, NotNull());
+
+  xml::Attribute* attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("0x10000000"));
+}
+
+TEST_F(ManifestFixerTest, DontUseDefaultRevisionCode) {
+  ManifestFixerOptions options;
+  options.revision_code_default = std::string("0x10000000");
+
+  std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
+        <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+                  package="android"
+                  android:versionCode="0x00000001"
+                  android:revisionCode="0x00000002" />)EOF",
+                                                            options);
+  ASSERT_THAT(doc, NotNull());
+
+  xml::Element* manifest_el = doc->root.get();
+  ASSERT_THAT(manifest_el, NotNull());
+
+  xml::Attribute* attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("0x00000002"));
+}
+
+TEST_F(ManifestFixerTest, ReplaceRevisionCode) {
+  ManifestFixerOptions options;
+  options.replace_version = true;
+  options.revision_code_default = std::string("0x10000000");
+
+  std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
+        <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+                  package="android"
+                  android:versionCode="0x00000001"
+                  android:revisionCode="0x00000002" />)EOF",
+                                                            options);
+  ASSERT_THAT(doc, NotNull());
+
+  xml::Element* manifest_el = doc->root.get();
+  ASSERT_THAT(manifest_el, NotNull());
+
+  xml::Attribute* attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("0x10000000"));
+}
+
 TEST_F(ManifestFixerTest, ReplaceVersionName) {
   ManifestFixerOptions options;
   options.replace_version = true;
diff --git a/tools/codegen/src/com/android/codegen/ClassPrinter.kt b/tools/codegen/src/com/android/codegen/ClassPrinter.kt
index b90e1bb..8cae14a 100644
--- a/tools/codegen/src/com/android/codegen/ClassPrinter.kt
+++ b/tools/codegen/src/com/android/codegen/ClassPrinter.kt
@@ -145,7 +145,6 @@
             }
             return when {
                 cliArgs.contains("--hidden-$kebabCase") -> true
-                this == FeatureFlag.BUILD_UPON -> FeatureFlag.BUILDER.hidden
                 else -> false
             }
         }
diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt
index 6a635d0..d9ad649 100644
--- a/tools/codegen/src/com/android/codegen/SharedConstants.kt
+++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt
@@ -1,7 +1,7 @@
 package com.android.codegen
 
 const val CODEGEN_NAME = "codegen"
-const val CODEGEN_VERSION = "1.0.21"
+const val CODEGEN_VERSION = "1.0.22"
 
 const val CANONICAL_BUILDER_CLASS = "Builder"
 const val BASE_BUILDER_CLASS = "BaseBuilder"
diff --git a/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl b/wifi/aidl-export/android/net/wifi/CoexUnsafeChannel.aidl
similarity index 73%
copy from services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl
copy to wifi/aidl-export/android/net/wifi/CoexUnsafeChannel.aidl
index 45e4c69..cb359e9 100644
--- a/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl
+++ b/wifi/aidl-export/android/net/wifi/CoexUnsafeChannel.aidl
@@ -1,11 +1,11 @@
-/*
- * 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.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     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,
@@ -16,8 +16,4 @@
 
 package android.net.wifi;
 
-/** @hide */
-parcelable WifiApiServiceInfo {
-    String name;
-    IBinder binder;
-}
+parcelable CoexUnsafeChannel;
diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt
index 5e8fc07..48d9fd4 100644
--- a/wifi/api/system-current.txt
+++ b/wifi/api/system-current.txt
@@ -1,6 +1,17 @@
 // Signature format: 2.0
 package android.net.wifi {
 
+  public final class CoexUnsafeChannel implements android.os.Parcelable {
+    ctor public CoexUnsafeChannel(int, int);
+    ctor public CoexUnsafeChannel(int, int, int);
+    method public int getBand();
+    method public int getChannel();
+    method public int getPowerCapDbm();
+    method public boolean isPowerCapAvailable();
+    method public void setPowerCapDbm(int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.CoexUnsafeChannel> CREATOR;
+  }
+
   public abstract class EasyConnectStatusCallback {
     ctor public EasyConnectStatusCallback();
     method public void onBootstrapUriGenerated(@NonNull String);
@@ -456,6 +467,8 @@
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset();
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void forget(int, @Nullable android.net.wifi.WifiManager.ActionListener);
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration,java.util.Map<java.lang.Integer,java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(@NonNull java.util.List<android.net.wifi.ScanResult>);
+    method @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS) public int getCoexRestrictions();
+    method @NonNull @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS) public java.util.Set<android.net.wifi.CoexUnsafeChannel> getCoexUnsafeChannels();
     method @Nullable @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCountryCode();
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public android.net.Network getCurrentNetwork();
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String[] getFactoryMacAddresses();
@@ -476,6 +489,7 @@
     method public boolean isVerboseLoggingEnabled();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled();
     method public boolean isWifiScannerSupported();
+    method @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS) public void registerCoexCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.CoexCallback);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerNetworkRequestMatchCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerSoftApCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerTrafficStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.TrafficStateCallback);
@@ -487,6 +501,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveSoftApBackupData();
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setAutoWakeupEnabled(boolean);
+    method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS) public void setCoexUnsafeChannels(@NonNull java.util.Set<android.net.wifi.CoexUnsafeChannel>, int);
     method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setPasspointMeteredOverride(@NonNull String, int);
@@ -507,6 +522,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void stopEasyConnectSession();
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean stopSoftAp();
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void stopTemporarilyDisablingAllNonCarrierMergedWifi();
+    method @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS) public void unregisterCoexCallback(@NonNull android.net.wifi.WifiManager.CoexCallback);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterNetworkRequestMatchCallback(@NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterSoftApCallback(@NonNull android.net.wifi.WifiManager.SoftApCallback);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterTrafficStateCallback(@NonNull android.net.wifi.WifiManager.TrafficStateCallback);
@@ -520,6 +536,9 @@
     field public static final int CHANGE_REASON_ADDED = 0; // 0x0
     field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2
     field public static final int CHANGE_REASON_REMOVED = 1; // 0x1
+    field public static final int COEX_RESTRICTION_SOFTAP = 2; // 0x2
+    field public static final int COEX_RESTRICTION_WIFI_AWARE = 4; // 0x4
+    field public static final int COEX_RESTRICTION_WIFI_DIRECT = 1; // 0x1
     field public static final String CONFIGURED_NETWORKS_CHANGED_ACTION = "android.net.wifi.CONFIGURED_NETWORKS_CHANGE";
     field public static final int DEVICE_MOBILITY_STATE_HIGH_MVMT = 1; // 0x1
     field public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2; // 0x2
@@ -575,6 +594,11 @@
     method public void onSuccess();
   }
 
+  public abstract static class WifiManager.CoexCallback {
+    ctor public WifiManager.CoexCallback();
+    method public abstract void onCoexUnsafeChannelsChanged();
+  }
+
   public static interface WifiManager.NetworkRequestMatchCallback {
     method public default void onAbort();
     method public default void onMatch(@NonNull java.util.List<android.net.wifi.ScanResult>);
diff --git a/wifi/java/android/net/wifi/CoexUnsafeChannel.java b/wifi/java/android/net/wifi/CoexUnsafeChannel.java
new file mode 100644
index 0000000..3f9efa0
--- /dev/null
+++ b/wifi/java/android/net/wifi/CoexUnsafeChannel.java
@@ -0,0 +1,176 @@
+/*
+ * 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;
+
+import static android.net.wifi.WifiScanner.WIFI_BAND_24_GHZ;
+import static android.net.wifi.WifiScanner.WIFI_BAND_5_GHZ;
+import static android.net.wifi.WifiScanner.WIFI_BAND_6_GHZ;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Data structure class representing a Wi-Fi channel that would cause interference to/receive
+ * interference from the active cellular channels and should be avoided.
+ *
+ * If {@link #isPowerCapAvailable()} is {@code true}, then a valid power cap value is available
+ * through {@link #getPowerCapDbm()} to be used if this channel cannot be avoided. If {@code false},
+ * then {@link #getPowerCapDbm()} throws an IllegalStateException and the channel will not need to
+ * cap its power.
+ *
+ * @hide
+ */
+@SystemApi
+public final class CoexUnsafeChannel implements Parcelable {
+    private @WifiAnnotations.WifiBandBasic int mBand;
+    private int mChannel;
+    private boolean mIsPowerCapAvailable = false;
+    private int mPowerCapDbm;
+
+    /**
+     * Constructor for a CoexUnsafeChannel with no power cap specified.
+     * @param band One of {@link WifiAnnotations.WifiBandBasic}
+     * @param channel Channel number
+     */
+    public CoexUnsafeChannel(@WifiAnnotations.WifiBandBasic int band, int channel) {
+        mBand = band;
+        mChannel = channel;
+    }
+
+    /**
+     * Constructor for a CoexUnsafeChannel with power cap specified.
+     * @param band One of {@link WifiAnnotations.WifiBandBasic}
+     * @param channel Channel number
+     * @param powerCapDbm Power cap in dBm
+     */
+    public CoexUnsafeChannel(@WifiAnnotations.WifiBandBasic int band, int channel,
+            int powerCapDbm) {
+        mBand = band;
+        mChannel = channel;
+        setPowerCapDbm(powerCapDbm);
+    }
+
+    /** Returns the Wi-Fi band of this channel as one of {@link WifiAnnotations.WifiBandBasic} */
+    public @WifiAnnotations.WifiBandBasic int getBand() {
+        return mBand;
+    }
+
+    /** Returns the channel number of this channel. */
+    public int getChannel() {
+        return mChannel;
+    }
+
+    /** Returns {@code true} if {@link #getPowerCapDbm()} is a valid value, else {@code false} */
+    public boolean isPowerCapAvailable() {
+        return mIsPowerCapAvailable;
+    }
+
+    /**
+     * Returns the power cap of this channel in dBm. Throws IllegalStateException if
+     * {@link #isPowerCapAvailable()} is {@code false}.
+     */
+    public int getPowerCapDbm() {
+        if (!mIsPowerCapAvailable) {
+            throw new IllegalStateException("getPowerCapDbm called but power cap is unavailable");
+        }
+        return mPowerCapDbm;
+    }
+
+    /** Set the power cap of this channel. */
+    public void setPowerCapDbm(int powerCapDbm) {
+        mIsPowerCapAvailable = true;
+        mPowerCapDbm = powerCapDbm;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        CoexUnsafeChannel that = (CoexUnsafeChannel) o;
+        return mBand == that.mBand
+                && mChannel == that.mChannel
+                && mIsPowerCapAvailable == that.mIsPowerCapAvailable
+                && mPowerCapDbm == that.mPowerCapDbm;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mBand, mChannel, mIsPowerCapAvailable, mPowerCapDbm);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sj = new StringBuilder("CoexUnsafeChannel{");
+        sj.append(mChannel);
+        sj.append(", ");
+        if (mBand == WIFI_BAND_24_GHZ) {
+            sj.append("2.4GHz");
+        } else if (mBand == WIFI_BAND_5_GHZ) {
+            sj.append("5GHz");
+        } else if (mBand == WIFI_BAND_6_GHZ) {
+            sj.append("6GHz");
+        } else {
+            sj.append("UNKNOWN BAND");
+        }
+        if (mIsPowerCapAvailable) {
+            sj.append(", ").append(mPowerCapDbm).append("dBm");
+        }
+        sj.append('}');
+        return sj.toString();
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mBand);
+        dest.writeInt(mChannel);
+        dest.writeBoolean(mIsPowerCapAvailable);
+        if (mIsPowerCapAvailable) {
+            dest.writeInt(mPowerCapDbm);
+        }
+    }
+
+    /** Implement the Parcelable interface */
+    public static final @NonNull Creator<CoexUnsafeChannel> CREATOR =
+            new Creator<CoexUnsafeChannel>() {
+                public CoexUnsafeChannel createFromParcel(Parcel in) {
+                    final int band = in.readInt();
+                    final int channel = in.readInt();
+                    final boolean isPowerCapAvailable = in.readBoolean();
+                    if (isPowerCapAvailable) {
+                        final int powerCapDbm = in.readInt();
+                        return new CoexUnsafeChannel(band, channel, powerCapDbm);
+                    }
+                    return new CoexUnsafeChannel(band, channel);
+                }
+
+                public CoexUnsafeChannel[] newArray(int size) {
+                    return new CoexUnsafeChannel[size];
+                }
+            };
+}
diff --git a/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl b/wifi/java/android/net/wifi/ICoexCallback.aidl
similarity index 75%
rename from services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl
rename to wifi/java/android/net/wifi/ICoexCallback.aidl
index 45e4c69..89e4c4b 100644
--- a/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl
+++ b/wifi/java/android/net/wifi/ICoexCallback.aidl
@@ -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.
@@ -16,8 +16,11 @@
 
 package android.net.wifi;
 
-/** @hide */
-parcelable WifiApiServiceInfo {
-    String name;
-    IBinder binder;
+/**
+ * Interface for Wi-Fi/cellular coex callback.
+ * @hide
+ */
+oneway interface ICoexCallback
+{
+    void onCoexUnsafeChannelsChanged();
 }
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 0b3c057..6dee751 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -24,7 +24,9 @@
 
 import android.net.DhcpInfo;
 import android.net.Network;
+import android.net.wifi.CoexUnsafeChannel;
 import android.net.wifi.IActionListener;
+import android.net.wifi.ICoexCallback;
 import android.net.wifi.IDppCallback;
 import android.net.wifi.ILocalOnlyHotspotCallback;
 import android.net.wifi.INetworkRequestMatchCallback;
@@ -144,6 +146,16 @@
 
     void updateInterfaceIpState(String ifaceName, int mode);
 
+    void setCoexUnsafeChannels(in List<CoexUnsafeChannel> unsafeChannels, int mandatoryRestrictions);
+
+    List<CoexUnsafeChannel> getCoexUnsafeChannels();
+
+    int getCoexRestrictions();
+
+    void registerCoexCallback(in ICoexCallback callback);
+
+    void unregisterCoexCallback(in ICoexCallback callback);
+
     boolean startSoftAp(in WifiConfiguration wifiConfig, String packageName);
 
     boolean startTetheredHotspot(in SoftApConfiguration softApConfig, String packageName);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index d089e1a..b8fa1e1 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -72,6 +72,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -3110,6 +3111,238 @@
         }
     }
 
+    /* Wi-Fi/Cellular Coex */
+
+    /**
+     * Mandatory coex restriction flag for Wi-Fi Direct.
+     *
+     * @see #setCoexUnsafeChannels(Set, int)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int COEX_RESTRICTION_WIFI_DIRECT = 0x1 << 0;
+
+    /**
+     * Mandatory coex restriction flag for SoftAP
+     *
+     * @see #setCoexUnsafeChannels(Set, int)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int COEX_RESTRICTION_SOFTAP = 0x1 << 1;
+
+    /**
+     * Mandatory coex restriction flag for Wi-Fi Aware.
+     *
+     * @see #setCoexUnsafeChannels(Set, int)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int COEX_RESTRICTION_WIFI_AWARE = 0x1 << 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = {"COEX_RESTRICTION_"}, value = {
+            COEX_RESTRICTION_WIFI_DIRECT,
+            COEX_RESTRICTION_SOFTAP,
+            COEX_RESTRICTION_WIFI_AWARE
+    })
+    public @interface CoexRestriction {}
+
+    /**
+     * Specify the set of {@link CoexUnsafeChannel} to propagate through the framework for
+     * Wi-Fi/Cellular coex channel avoidance if the default algorithm is disabled via overlay
+     * (i.e. config_wifiCoexDefaultAlgorithmEnabled = false). Otherwise do nothing.
+     *
+     * @param unsafeChannels Set of {@link CoexUnsafeChannel} to avoid.
+     * @param restrictions Bitmap of {@link CoexRestriction} specifying the mandatory restricted
+     *                     uses of the specified channels. If any restrictions are set, then the
+     *                     supplied CoexUnsafeChannels will be completely avoided for the
+     *                     specified modes, rather than be avoided with best effort.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS)
+    public void setCoexUnsafeChannels(@NonNull Set<CoexUnsafeChannel> unsafeChannels,
+            int restrictions) {
+        if (unsafeChannels == null) {
+            throw new IllegalArgumentException("unsafeChannels must not be null");
+        }
+        try {
+            mService.setCoexUnsafeChannels(new ArrayList<>(unsafeChannels), restrictions);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the set of current {@link CoexUnsafeChannel} being used for Wi-Fi/Cellular coex
+     * channel avoidance.
+     *
+     * This returns the set calculated by the default algorithm if
+     * config_wifiCoexDefaultAlgorithmEnabled is {@code true}. Otherwise, returns the set supplied
+     * in {@link #setCoexUnsafeChannels(Set, int)}.
+     *
+     * If any {@link CoexRestriction} flags are set in {@link #getCoexRestrictions()}, then the
+     * CoexUnsafeChannels should be totally avoided (i.e. not best effort) for the Wi-Fi modes
+     * specified by the flags.
+     *
+     * @return Set of current CoexUnsafeChannels.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS)
+    public Set<CoexUnsafeChannel> getCoexUnsafeChannels() {
+        try {
+            return new HashSet<>(mService.getCoexUnsafeChannels());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the current coex restrictions being used for Wi-Fi/Cellular coex
+     * channel avoidance.
+     *
+     * This returns the restrictions calculated by the default algorithm if
+     * config_wifiCoexDefaultAlgorithmEnabled is {@code true}. Otherwise, returns the value supplied
+     * in {@link #setCoexUnsafeChannels(Set, int)}.
+     *
+     * @return int containing a bitwise-OR combination of {@link CoexRestriction}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS)
+    public int getCoexRestrictions() {
+        try {
+            return mService.getCoexRestrictions();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Registers a CoexCallback to listen on the current CoexUnsafeChannels and restrictions being
+     * used for Wi-Fi/cellular coex channel avoidance.
+     * @param executor Executor to execute listener callback on
+     * @param callback CoexCallback to register
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS)
+    public void registerCoexCallback(
+            @NonNull @CallbackExecutor Executor executor, @NonNull CoexCallback callback) {
+        if (executor == null) throw new IllegalArgumentException("executor must not be null");
+        if (callback == null) throw new IllegalArgumentException("callback must not be null");
+        CoexCallback.CoexCallbackProxy proxy = callback.getProxy();
+        proxy.initProxy(executor, callback);
+        try {
+            mService.registerCoexCallback(proxy);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregisters a CoexCallback from listening on the current CoexUnsafeChannels and restrictions
+     * being used for Wi-Fi/cellular coex channel avoidance.
+     * @param callback CoexCallback to unregister
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS)
+    public void unregisterCoexCallback(@NonNull CoexCallback callback) {
+        if (callback == null) throw new IllegalArgumentException("callback must not be null");
+        CoexCallback.CoexCallbackProxy proxy = callback.getProxy();
+        try {
+            mService.unregisterCoexCallback(proxy);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } finally {
+            proxy.cleanUpProxy();
+        }
+    }
+
+    /**
+     * Abstract callback class for applications to receive updates about current CoexUnsafeChannels
+     * for Wi-Fi/Cellular coex channel avoidance.
+     *
+     * @hide
+     */
+    @SystemApi
+    public abstract static class CoexCallback {
+        private final CoexCallbackProxy mCoexCallbackProxy;
+
+        public CoexCallback() {
+            mCoexCallbackProxy = new CoexCallbackProxy();
+        }
+
+        /*package*/ @NonNull
+        CoexCallbackProxy getProxy() {
+            return mCoexCallbackProxy;
+        }
+
+        /**
+         * Indicates that the current CoexUnsafeChannels or restrictions have changed.
+         * Clients should call {@link #getCoexUnsafeChannels()} and {@link #getCoexRestrictions()}
+         * to get the updated values.
+         */
+        public abstract void onCoexUnsafeChannelsChanged();
+
+        /**
+         * Callback proxy for CoexCallback objects.
+         */
+        private static class CoexCallbackProxy extends ICoexCallback.Stub {
+            private final Object mLock = new Object();
+            @Nullable @GuardedBy("mLock") private Executor mExecutor;
+            @Nullable @GuardedBy("mLock") private CoexCallback mCallback;
+
+            CoexCallbackProxy() {
+                mExecutor = null;
+                mCallback = null;
+            }
+
+            /*package*/ void initProxy(@NonNull Executor executor,
+                    @NonNull CoexCallback callback) {
+                synchronized (mLock) {
+                    mExecutor = executor;
+                    mCallback = callback;
+                }
+            }
+
+            /*package*/ void cleanUpProxy() {
+                synchronized (mLock) {
+                    mExecutor = null;
+                    mCallback = null;
+                }
+            }
+
+            @Override
+            public void onCoexUnsafeChannelsChanged() {
+                Executor executor;
+                CoexCallback callback;
+                synchronized (mLock) {
+                    executor = mExecutor;
+                    callback = mCallback;
+                }
+                if (executor == null || callback == null) {
+                    return;
+                }
+                Binder.clearCallingIdentity();
+                executor.execute(callback::onCoexUnsafeChannelsChanged);
+            }
+        }
+    }
+
     /**
      * Start Soft AP (hotspot) mode for tethering purposes with the specified configuration.
      * Note that starting Soft AP mode may disable station mode operation if the device does not
@@ -5991,7 +6224,6 @@
                 executor.execute(callback::onScanResultsAvailable);
             }
         }
-
     }
 
     /**
diff --git a/wifi/tests/src/android/net/wifi/CoexUnsafeChannelTest.java b/wifi/tests/src/android/net/wifi/CoexUnsafeChannelTest.java
new file mode 100644
index 0000000..320f25e
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/CoexUnsafeChannelTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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;
+
+import static android.net.wifi.WifiScanner.WIFI_BAND_24_GHZ;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.CoexUnsafeChannel}.
+ */
+@SmallTest
+public class CoexUnsafeChannelTest {
+    /**
+     * Verifies {@link CoexUnsafeChannel#isPowerCapAvailable()} returns false if no cap is set.
+     */
+    @Test
+    public void testIsPowerCapAvailable_noPowerCap_returnsFalse() {
+        CoexUnsafeChannel unsafeChannel = new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6);
+
+        assertThat(unsafeChannel.isPowerCapAvailable()).isFalse();
+    }
+
+    /**
+     * Verifies {@link CoexUnsafeChannel#isPowerCapAvailable()} returns true if a cap is set, and
+     * {@link CoexUnsafeChannel#getPowerCapDbm()} returns the set value.
+     */
+    @Test
+    public void testIsPowerCapAvailable_powerCapSet_returnsTrue() {
+        final int powerCapDbm = -50;
+        CoexUnsafeChannel unsafeChannel = new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6);
+
+        unsafeChannel.setPowerCapDbm(powerCapDbm);
+
+        assertThat(unsafeChannel.isPowerCapAvailable()).isTrue();
+        assertThat(unsafeChannel.getPowerCapDbm()).isEqualTo(powerCapDbm);
+    }
+
+    /**
+     * Verifies {@link CoexUnsafeChannel#getPowerCapDbm()} throws an IllegalStateException if
+     * {@link CoexUnsafeChannel#isPowerCapAvailable()} is {@code false}.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testGetPowerCap_powerCapUnavailable_throwsException() {
+        CoexUnsafeChannel unsafeChannel = new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6);
+
+        unsafeChannel.getPowerCapDbm();
+    }
+
+    /**
+     * Verify parcel read/write for CoexUnsafeChannel with or without power cap.
+     */
+    @Test
+    public void testParcelReadWrite_withOrWithoutCap_readEqualsWritten() throws Exception {
+        CoexUnsafeChannel writeUnsafeChannelNoCap =
+                new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6);
+        CoexUnsafeChannel writeUnsafeChannelCapped =
+                new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6, -50);
+
+        CoexUnsafeChannel readUnsafeChannelNoCap = parcelReadWrite(writeUnsafeChannelNoCap);
+        CoexUnsafeChannel readUnsafeChannelCapped = parcelReadWrite(writeUnsafeChannelCapped);
+
+        assertThat(writeUnsafeChannelNoCap).isEqualTo(readUnsafeChannelNoCap);
+        assertThat(writeUnsafeChannelCapped).isEqualTo(readUnsafeChannelCapped);
+    }
+
+    /**
+     * Write the provided {@link CoexUnsafeChannel} to a parcel and deserialize it.
+     */
+    private static CoexUnsafeChannel parcelReadWrite(CoexUnsafeChannel writeResult)
+            throws Exception {
+        Parcel parcel = Parcel.obtain();
+        writeResult.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);    // Rewind data position back to the beginning for read.
+        return CoexUnsafeChannel.CREATOR.createFromParcel(parcel);
+    }
+}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index aefebbc..39f6f57 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -19,6 +19,9 @@
 import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_METERED;
 import static android.net.wifi.WifiManager.ActionListener;
 import static android.net.wifi.WifiManager.BUSY;
+import static android.net.wifi.WifiManager.COEX_RESTRICTION_SOFTAP;
+import static android.net.wifi.WifiManager.COEX_RESTRICTION_WIFI_AWARE;
+import static android.net.wifi.WifiManager.COEX_RESTRICTION_WIFI_DIRECT;
 import static android.net.wifi.WifiManager.ERROR;
 import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC;
 import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE;
@@ -43,6 +46,7 @@
 import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SAE;
 import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SUITE_B;
 import static android.net.wifi.WifiManager.WpsCallback;
+import static android.net.wifi.WifiScanner.WIFI_BAND_24_GHZ;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -74,6 +78,7 @@
 import android.content.pm.ApplicationInfo;
 import android.net.DhcpInfo;
 import android.net.MacAddress;
+import android.net.wifi.WifiManager.CoexCallback;
 import android.net.wifi.WifiManager.LocalOnlyHotspotCallback;
 import android.net.wifi.WifiManager.LocalOnlyHotspotObserver;
 import android.net.wifi.WifiManager.LocalOnlyHotspotReservation;
@@ -108,9 +113,11 @@
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
@@ -151,6 +158,7 @@
     private WifiManager mWifiManager;
     private WifiNetworkSuggestion mWifiNetworkSuggestion;
     private ScanResultsCallback mScanResultsCallback;
+    private CoexCallback mCoexCallback;
     private WifiActivityEnergyInfo mWifiActivityEnergyInfo;
 
     /**
@@ -214,10 +222,149 @@
                 mRunnable.run();
             }
         };
+        mCoexCallback = new CoexCallback() {
+            @Override
+            public void onCoexUnsafeChannelsChanged() {
+                mRunnable.run();
+            }
+        };
         mWifiActivityEnergyInfo = new WifiActivityEnergyInfo(0, 0, 0, 0, 0, 0);
     }
 
     /**
+     * Check the call to setCoexUnsafeChannels calls WifiServiceImpl to setCoexUnsafeChannels with
+     * the provided CoexUnsafeChannels and restrictions bitmask.
+     */
+    @Test
+    public void testSetCoexUnsafeChannelsGoesToWifiServiceImpl() throws Exception {
+        Set<CoexUnsafeChannel> unsafeChannels = new HashSet<>();
+        int restrictions = COEX_RESTRICTION_WIFI_DIRECT | COEX_RESTRICTION_SOFTAP
+                | COEX_RESTRICTION_WIFI_AWARE;
+
+        mWifiManager.setCoexUnsafeChannels(unsafeChannels, restrictions);
+
+        verify(mWifiService).setCoexUnsafeChannels(new ArrayList<>(unsafeChannels), restrictions);
+    }
+
+    /**
+     * Verify an IllegalArgumentException if passed a null value for unsafeChannels.
+     */
+    @Test
+    public void testSetCoexUnsafeChannelsThrowsIllegalArgumentExceptionOnNullUnsafeChannels() {
+        try {
+            mWifiManager.setCoexUnsafeChannels(null, 0);
+            fail("expected IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    /**
+     * Check the call to getCoexUnsafeChannels calls WifiServiceImpl to return the values from
+     * getCoexUnsafeChannels.
+     */
+    @Test
+    public void testGetCoexUnsafeChannelsGoesToWifiServiceImpl() throws Exception {
+        Set<CoexUnsafeChannel> unsafeChannels = new HashSet<>();
+        unsafeChannels.add(new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6));
+        when(mWifiService.getCoexUnsafeChannels()).thenReturn(new ArrayList<>(unsafeChannels));
+
+        assertEquals(mWifiManager.getCoexUnsafeChannels(), unsafeChannels);
+    }
+
+    /**
+     * Verify call to getCoexRestrictions calls WifiServiceImpl to return the value from
+     * getCoexRestrictions.
+     */
+    @Test
+    public void testGetCoexRestrictionsGoesToWifiServiceImpl() throws Exception {
+        int restrictions = COEX_RESTRICTION_WIFI_DIRECT | COEX_RESTRICTION_SOFTAP
+                | COEX_RESTRICTION_WIFI_AWARE;
+        when(mWifiService.getCoexRestrictions()).thenReturn(restrictions);
+
+        assertEquals(mWifiService.getCoexRestrictions(), restrictions);
+    }
+
+
+    /**
+     * Verify an IllegalArgumentException is thrown if callback is not provided.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testRegisterCoexCallbackWithNullCallback() throws Exception {
+        mWifiManager.registerCoexCallback(mExecutor, null);
+    }
+
+    /**
+     * Verify an IllegalArgumentException is thrown if executor is not provided.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testRegisterCoexCallbackWithNullExecutor() throws Exception {
+        mWifiManager.registerCoexCallback(null, mCoexCallback);
+    }
+
+    /**
+     * Verify client provided callback is being called to the right callback.
+     */
+    @Test
+    public void testAddCoexCallbackAndReceiveEvent() throws Exception {
+        ArgumentCaptor<ICoexCallback.Stub> callbackCaptor =
+                ArgumentCaptor.forClass(ICoexCallback.Stub.class);
+        mWifiManager.registerCoexCallback(new SynchronousExecutor(), mCoexCallback);
+        verify(mWifiService).registerCoexCallback(callbackCaptor.capture());
+        callbackCaptor.getValue().onCoexUnsafeChannelsChanged();
+        verify(mRunnable).run();
+    }
+
+    /**
+     * Verify client provided callback is being called to the right executor.
+     */
+    @Test
+    public void testRegisterCoexCallbackWithTheTargetExecutor() throws Exception {
+        ArgumentCaptor<ICoexCallback.Stub> callbackCaptor =
+                ArgumentCaptor.forClass(ICoexCallback.Stub.class);
+        mWifiManager.registerCoexCallback(mExecutor, mCoexCallback);
+        verify(mWifiService).registerCoexCallback(callbackCaptor.capture());
+        mWifiManager.registerCoexCallback(mAnotherExecutor, mCoexCallback);
+        callbackCaptor.getValue().onCoexUnsafeChannelsChanged();
+        verify(mExecutor, never()).execute(any(Runnable.class));
+        verify(mAnotherExecutor).execute(any(Runnable.class));
+    }
+
+    /**
+     * Verify client register unregister then register again, to ensure callback still works.
+     */
+    @Test
+    public void testRegisterUnregisterThenRegisterAgainWithCoexCallback() throws Exception {
+        ArgumentCaptor<ICoexCallback.Stub> callbackCaptor =
+                ArgumentCaptor.forClass(ICoexCallback.Stub.class);
+        mWifiManager.registerCoexCallback(new SynchronousExecutor(), mCoexCallback);
+        verify(mWifiService).registerCoexCallback(callbackCaptor.capture());
+        mWifiManager.unregisterCoexCallback(mCoexCallback);
+        callbackCaptor.getValue().onCoexUnsafeChannelsChanged();
+        verify(mRunnable, never()).run();
+        mWifiManager.registerCoexCallback(new SynchronousExecutor(), mCoexCallback);
+        callbackCaptor.getValue().onCoexUnsafeChannelsChanged();
+        verify(mRunnable).run();
+    }
+
+    /**
+     * Verify client unregisterCoexCallback.
+     */
+    @Test
+    public void testUnregisterCoexCallback() throws Exception {
+        mWifiManager.unregisterCoexCallback(mCoexCallback);
+        verify(mWifiService).unregisterCoexCallback(any());
+    }
+
+    /**
+     * Verify client unregisterCoexCallback with null callback will cause an exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testUnregisterCoexCallbackWithNullCallback() throws Exception {
+        mWifiManager.unregisterCoexCallback(null);
+    }
+
+
+    /**
      * Check the call to startSoftAp calls WifiService to startSoftAp with the provided
      * WifiConfiguration.  Verify that the return value is propagated to the caller.
      */