Merge "[Settings] keys with @TestApi need to have @Readable" into sc-dev
diff --git a/Android.bp b/Android.bp
index 6a47db1..8b15f02 100644
--- a/Android.bp
+++ b/Android.bp
@@ -465,6 +465,7 @@
     static_libs: [
         "android.net.ipsec.ike.stubs.module_lib",
         "framework-appsearch.stubs.module_lib",
+        "framework-connectivity.stubs.module_lib",
         "framework-graphics.stubs.module_lib",
         "framework-media.stubs.module_lib",
         "framework-mediaprovider.stubs.module_lib",
@@ -487,6 +488,7 @@
         "android.net.ipsec.ike.impl",
         "framework-minus-apex",
         "framework-appsearch.impl",
+        "framework-connectivity.impl",
         "framework-graphics.impl",
         "framework-mediaprovider.impl",
         "framework-permission.impl",
@@ -643,14 +645,17 @@
     defaults: ["framework-aidl-export-defaults"],
     srcs: [
         ":framework-non-updatable-sources",
-        ":framework-connectivity-sources",
         "core/java/**/*.logtags",
     ],
     // See comment on framework-atb-backward-compatibility module below
     exclude_srcs: ["core/java/android/content/pm/AndroidTestBaseUpdater.java"],
     aidl: {
         generate_get_transaction_name: true,
-        local_include_dirs: ["media/aidl"],
+        local_include_dirs: [
+            "media/aidl",
+            // TODO: move to include_dirs when migrated to packages/modules
+            "packages/Connectivity/framework/aidl-export",
+        ],
         include_dirs: ["frameworks/av/aidl"],
     },
     dxflags: [
@@ -703,8 +708,6 @@
     apex_available: ["//apex_available:platform"],
     visibility: [
         "//frameworks/base",
-        // TODO: remove when framework-connectivity can build against API
-        "//frameworks/base/packages/Connectivity/framework",
         // TODO(b/147128803) remove the below lines
         "//frameworks/base/apex/appsearch/framework",
         "//frameworks/base/apex/blobstore/framework",
@@ -743,6 +746,7 @@
     static_libs: [
         "app-compat-annotations",
         "framework-minus-apex",
+        "framework-connectivity.impl", // TODO(b/182859030): should be removed
         "framework-appsearch.impl", // TODO(b/146218515): should be removed
         "framework-updatable-stubs-module_libs_api",
     ],
@@ -1461,7 +1465,7 @@
     ],
     libs: [
         "framework-annotations-lib",
-        "framework-connectivity",
+        "framework-connectivity.stubs.module_lib",
         "unsupportedappusage",
     ],
     visibility: [
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index 47cf17c..5acfe6a 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -26,10 +26,8 @@
   }
 
   public static final class AppSearchManager.SearchContext.Builder {
-    ctor @Deprecated public AppSearchManager.SearchContext.Builder();
     ctor public AppSearchManager.SearchContext.Builder(@NonNull String);
     method @NonNull public android.app.appsearch.AppSearchManager.SearchContext build();
-    method @Deprecated @NonNull public android.app.appsearch.AppSearchManager.SearchContext.Builder setDatabaseName(@NonNull String);
   }
 
   public final class AppSearchResult<ValueType> {
@@ -53,7 +51,6 @@
   public final class AppSearchSchema {
     method @NonNull public java.util.List<android.app.appsearch.AppSearchSchema.PropertyConfig> getProperties();
     method @NonNull public String getSchemaType();
-    method @Deprecated @IntRange(from=0) public int getVersion();
   }
 
   public static final class AppSearchSchema.BooleanPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
@@ -69,7 +66,6 @@
     ctor public AppSearchSchema.Builder(@NonNull String);
     method @NonNull public android.app.appsearch.AppSearchSchema.Builder addProperty(@NonNull android.app.appsearch.AppSearchSchema.PropertyConfig);
     method @NonNull public android.app.appsearch.AppSearchSchema build();
-    method @Deprecated @NonNull public android.app.appsearch.AppSearchSchema.Builder setVersion(@IntRange(from=0) int);
   }
 
   public static final class AppSearchSchema.BytesPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
@@ -149,7 +145,6 @@
     method public void remove(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
     method public void reportUsage(@NonNull android.app.appsearch.ReportUsageRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
     method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec);
-    method @Deprecated public void setSchema(@NonNull android.app.appsearch.SetSchemaRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.SetSchemaResponse>>);
     method public void setSchema(@NonNull android.app.appsearch.SetSchemaRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.SetSchemaResponse>>);
   }
 
@@ -181,15 +176,12 @@
     method public int getScore();
     method public long getTtlMillis();
     method @NonNull public String getUri();
-    field @Deprecated public static final String DEFAULT_NAMESPACE = "";
   }
 
   public static class GenericDocument.Builder<BuilderType extends android.app.appsearch.GenericDocument.Builder> {
-    ctor @Deprecated public GenericDocument.Builder(@NonNull String, @NonNull String);
     ctor public GenericDocument.Builder(@NonNull String, @NonNull String, @NonNull String);
     method @NonNull public android.app.appsearch.GenericDocument build();
     method @NonNull public BuilderType setCreationTimestampMillis(long);
-    method @Deprecated @NonNull public BuilderType setNamespace(@NonNull String);
     method @NonNull public BuilderType setPropertyBoolean(@NonNull String, @NonNull boolean...);
     method @NonNull public BuilderType setPropertyBytes(@NonNull String, @NonNull byte[]...);
     method @NonNull public BuilderType setPropertyDocument(@NonNull String, @NonNull android.app.appsearch.GenericDocument...);
@@ -208,16 +200,14 @@
   }
 
   public static final class GetByUriRequest.Builder {
-    ctor @Deprecated public GetByUriRequest.Builder();
     ctor public GetByUriRequest.Builder(@NonNull String);
     method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
     method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.lang.String...);
     method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>);
     method @NonNull public android.app.appsearch.GetByUriRequest build();
-    method @Deprecated @NonNull public android.app.appsearch.GetByUriRequest.Builder setNamespace(@NonNull String);
   }
 
-  public class GetSchemaResponse extends java.util.HashSet<android.app.appsearch.AppSearchSchema> {
+  public class GetSchemaResponse {
     method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> getSchemas();
     method @IntRange(from=0) public int getVersion();
   }
@@ -265,12 +255,10 @@
   }
 
   public static final class RemoveByUriRequest.Builder {
-    ctor @Deprecated public RemoveByUriRequest.Builder();
     ctor public RemoveByUriRequest.Builder(@NonNull String);
     method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@NonNull java.lang.String...);
     method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>);
     method @NonNull public android.app.appsearch.RemoveByUriRequest build();
-    method @Deprecated @NonNull public android.app.appsearch.RemoveByUriRequest.Builder setNamespace(@NonNull String);
   }
 
   public final class ReportSystemUsageRequest {
@@ -295,17 +283,14 @@
   }
 
   public static final class ReportUsageRequest.Builder {
-    ctor @Deprecated public ReportUsageRequest.Builder();
     ctor public ReportUsageRequest.Builder(@NonNull String);
     method @NonNull public android.app.appsearch.ReportUsageRequest build();
-    method @Deprecated @NonNull public android.app.appsearch.ReportUsageRequest.Builder setNamespace(@NonNull String);
     method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setUri(@NonNull String);
     method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setUsageTimeMillis(long);
   }
 
   public final class SearchResult {
     method @NonNull public String getDatabaseName();
-    method @Deprecated @NonNull public android.app.appsearch.GenericDocument getDocument();
     method @NonNull public android.app.appsearch.GenericDocument getGenericDocument();
     method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches();
     method @NonNull public String getPackageName();
@@ -322,12 +307,10 @@
 
   public static final class SearchResult.MatchInfo {
     method @NonNull public CharSequence getExactMatch();
-    method @Deprecated @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchPosition();
     method @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchRange();
     method @NonNull public String getFullText();
     method @NonNull public String getPropertyPath();
     method @NonNull public CharSequence getSnippet();
-    method @Deprecated @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetPosition();
     method @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetRange();
   }
 
@@ -405,7 +388,6 @@
     method @NonNull public java.util.Map<java.lang.String,android.app.appsearch.Migrator> getMigrators();
     method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> getSchemas();
     method @NonNull public java.util.Set<java.lang.String> getSchemasNotDisplayedBySystem();
-    method @Deprecated @NonNull public java.util.Set<java.lang.String> getSchemasNotVisibleToSystemUi();
     method @NonNull public java.util.Map<java.lang.String,java.util.Set<android.app.appsearch.PackageIdentifier>> getSchemasVisibleToPackages();
     method @IntRange(from=1) public int getVersion();
     method public boolean isForceOverride();
@@ -421,7 +403,6 @@
     method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setMigrators(@NonNull java.util.Map<java.lang.String,android.app.appsearch.Migrator>);
     method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeDisplayedBySystem(@NonNull String, boolean);
     method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForPackage(@NonNull String, boolean, @NonNull android.app.appsearch.PackageIdentifier);
-    method @Deprecated @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForSystemUi(@NonNull String, boolean);
     method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setVersion(@IntRange(from=1) int);
   }
 
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index 0c6b86b..9776827 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -147,22 +147,10 @@
 
         /** Builder for {@link SearchContext} objects. */
         public static final class Builder {
-            private String mDatabaseName;
+            private final String mDatabaseName;
             private boolean mBuilt = false;
 
             /**
-             * TODO(b/181887768): This method exists only for dogfooder transition and must be
-             * removed.
-             *
-             * @deprecated Please supply the databaseName in {@link #Builder(String)} instead. This
-             *     method exists only for dogfooder transition and must be removed.
-             */
-            @Deprecated
-            public Builder() {
-                mDatabaseName = "";
-            }
-
-            /**
              * Creates a new {@link SearchContext.Builder}.
              *
              * <p>{@link AppSearchSession} will create or open a database under the given name.
@@ -182,37 +170,6 @@
                 mDatabaseName = databaseName;
             }
 
-            /**
-             * Sets the name of the database associated with {@link AppSearchSession}.
-             *
-             * <p>{@link AppSearchSession} will create or open a database under the given name.
-             *
-             * <p>Databases with different names are fully separate with distinct types, namespaces,
-             * and data.
-             *
-             * <p>Database name cannot contain {@code '/'}.
-             *
-             * <p>If not specified, defaults to the empty string.
-             *
-             * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
-             * removed.
-             *
-             * @param databaseName The name of the database.
-             * @throws IllegalArgumentException if the databaseName contains {@code '/'}.
-             * @deprecated Please supply the databaseName in {@link #Builder(String)} instead. This
-             *     method exists only for dogfooder transition and must be removed.
-             */
-            @Deprecated
-            @NonNull
-            public Builder setDatabaseName(@NonNull String databaseName) {
-                Preconditions.checkState(!mBuilt, "Builder has already been used");
-                Objects.requireNonNull(databaseName);
-                Preconditions.checkArgument(
-                        !databaseName.contains("/"), "Database name cannot contain '/'");
-                mDatabaseName = databaseName;
-                return this;
-            }
-
             /** Builds a {@link SearchContext} instance. */
             @NonNull
             public SearchContext build() {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java
new file mode 100644
index 0000000..e585d91
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch;
+
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
+import android.app.appsearch.exceptions.AppSearchException;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.io.Closeable;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * The helper class for {@link AppSearchSchema} migration.
+ *
+ * <p>It will query and migrate {@link GenericDocument} in given type to a new version.
+ * @hide
+ */
+public class AppSearchMigrationHelper implements Closeable {
+    private final IAppSearchManager mService;
+    private final String mPackageName;
+    private final String mDatabaseName;
+    private final int mUserId;
+    private final File mMigratedFile;
+    private final Map<String, Integer> mCurrentVersionMap;
+    private final Map<String, Integer> mFinalVersionMap;
+    private boolean mAreDocumentsMigrated = false;
+
+    AppSearchMigrationHelper(@NonNull IAppSearchManager service,
+            @UserIdInt int userId,
+            @NonNull Map<String, Integer> currentVersionMap,
+            @NonNull Map<String, Integer> finalVersionMap,
+            @NonNull String packageName,
+            @NonNull String databaseName) throws IOException {
+        mService = Objects.requireNonNull(service);
+        mCurrentVersionMap = Objects.requireNonNull(currentVersionMap);
+        mFinalVersionMap = Objects.requireNonNull(finalVersionMap);
+        mPackageName = Objects.requireNonNull(packageName);
+        mDatabaseName = Objects.requireNonNull(databaseName);
+        mUserId = userId;
+        mMigratedFile = File.createTempFile(/*prefix=*/"appsearch", /*suffix=*/null);
+    }
+
+    /**
+     * Queries all documents that need to be migrated to a different version and transform
+     * documents to that version by passing them to the provided {@link Migrator}.
+     *
+     * <p>The method will be executed on the executor provided to
+     * {@link AppSearchSession#setSchema}.
+     *
+     * @param schemaType The schema type that needs to be updated and whose {@link GenericDocument}
+     *                   need to be migrated.
+     * @param migrator The {@link Migrator} that will upgrade or downgrade a {@link
+     *     GenericDocument} to new version.
+     */
+    @WorkerThread
+    public void queryAndTransform(@NonNull String schemaType, @NonNull Migrator migrator)
+            throws IOException, AppSearchException, InterruptedException, ExecutionException {
+        File queryFile = File.createTempFile(/*prefix=*/"appsearch", /*suffix=*/null);
+        try (ParcelFileDescriptor fileDescriptor =
+                     ParcelFileDescriptor.open(queryFile, MODE_WRITE_ONLY)) {
+            AndroidFuture<AppSearchResult<Void>> androidFuture = new AndroidFuture<>();
+            mService.writeQueryResultsToFile(mPackageName, mDatabaseName,
+                    fileDescriptor,
+                    /*queryExpression=*/ "",
+                    new SearchSpec.Builder()
+                            .addFilterSchemas(schemaType)
+                            .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                            .build().getBundle(),
+                    mUserId,
+                    new IAppSearchResultCallback.Stub() {
+                        @Override
+                        public void onResult(AppSearchResult result) throws RemoteException {
+                            androidFuture.complete(result);
+                        }
+                    });
+            AppSearchResult<Void> result = androidFuture.get();
+            if (!result.isSuccess()) {
+                throw new AppSearchException(result.getResultCode(), result.getErrorMessage());
+            }
+            readAndTransform(queryFile, migrator);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } finally {
+            queryFile.delete();
+        }
+    }
+
+    /**
+     * Puts all {@link GenericDocument} migrated from the previous call to
+     * {@link #queryAndTransform} into AppSearch.
+     *
+     * <p> This method should be only called once.
+     *
+     * @param responseBuilder a SetSchemaResponse builder whose result will be returned by this
+     *                        function with any
+     *                        {@link android.app.appsearch.SetSchemaResponse.MigrationFailure}
+     *                        added in.
+     * @return the {@link SetSchemaResponse} for {@link AppSearchSession#setSchema} call.
+     */
+    @NonNull
+    AppSearchResult<SetSchemaResponse> putMigratedDocuments(
+            @NonNull SetSchemaResponse.Builder responseBuilder) {
+        if (!mAreDocumentsMigrated) {
+            return AppSearchResult.newSuccessfulResult(responseBuilder.build());
+        }
+        try (ParcelFileDescriptor fileDescriptor =
+                     ParcelFileDescriptor.open(mMigratedFile, MODE_READ_ONLY)) {
+            AndroidFuture<AppSearchResult<List<Bundle>>> androidFuture = new AndroidFuture<>();
+            mService.putDocumentsFromFile(mPackageName, mDatabaseName, fileDescriptor, mUserId,
+                    new IAppSearchResultCallback.Stub() {
+                        @Override
+                        public void onResult(AppSearchResult result) throws RemoteException {
+                            androidFuture.complete(result);
+                        }
+                    });
+            AppSearchResult<List<Bundle>> result = androidFuture.get();
+            if (!result.isSuccess()) {
+                return AppSearchResult.newFailedResult(result);
+            }
+            List<Bundle> migratedFailureBundles = result.getResultValue();
+            for (int i = 0; i < migratedFailureBundles.size(); i++) {
+                responseBuilder.addMigrationFailure(
+                        new SetSchemaResponse.MigrationFailure(migratedFailureBundles.get(i)));
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (Throwable t) {
+            return AppSearchResult.throwableToFailedResult(t);
+        } finally {
+            mMigratedFile.delete();
+        }
+        return AppSearchResult.newSuccessfulResult(responseBuilder.build());
+    }
+
+    /**
+     * Reads all saved {@link GenericDocument}s from the given {@link File}.
+     *
+     * <p>Transforms those {@link GenericDocument}s to the final version.
+     *
+     * <p>Save migrated {@link GenericDocument}s to the {@link #mMigratedFile}.
+     */
+    private void readAndTransform(@NonNull File file, @NonNull Migrator migrator)
+            throws IOException {
+        try (DataInputStream inputStream = new DataInputStream(new FileInputStream(file));
+             DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(
+                     mMigratedFile, /*append=*/ true))) {
+            GenericDocument document;
+            while (true) {
+                try {
+                    document = readDocumentFromInputStream(inputStream);
+                } catch (EOFException e) {
+                    break;
+                    // Nothing wrong. We just finished reading.
+                }
+
+                int currentVersion = mCurrentVersionMap.get(document.getSchemaType());
+                int finalVersion = mFinalVersionMap.get(document.getSchemaType());
+
+                GenericDocument newDocument;
+                if (currentVersion < finalVersion) {
+                    newDocument = migrator.onUpgrade(currentVersion, finalVersion, document);
+                } else {
+                    // currentVersion == finalVersion case won't trigger migration and get here.
+                    newDocument = migrator.onDowngrade(currentVersion, finalVersion, document);
+                }
+                writeBundleToOutputStream(outputStream, newDocument.getBundle());
+            }
+            mAreDocumentsMigrated = true;
+        }
+    }
+
+    /**
+     * Reads the {@link Bundle} of a {@link GenericDocument} from given {@link DataInputStream}.
+     *
+     * @param inputStream The inputStream to read from
+     *
+     * @throws IOException        on read failure.
+     * @throws EOFException       if {@link java.io.InputStream} reaches the end.
+     */
+    @NonNull
+    public static GenericDocument readDocumentFromInputStream(
+            @NonNull DataInputStream inputStream) throws IOException {
+        int length = inputStream.readInt();
+        if (length == 0) {
+            throw new EOFException();
+        }
+        byte[] serializedMessage = new byte[length];
+        inputStream.read(serializedMessage);
+
+        Parcel parcel = Parcel.obtain();
+        try {
+            parcel.unmarshall(serializedMessage, 0, serializedMessage.length);
+            parcel.setDataPosition(0);
+            Bundle bundle = parcel.readBundle();
+            return new GenericDocument(bundle);
+        } finally {
+            parcel.recycle();
+        }
+    }
+
+    /**
+     * Serializes a {@link Bundle} and writes into the given {@link DataOutputStream}.
+     */
+    public static void writeBundleToOutputStream(
+            @NonNull DataOutputStream outputStream, @NonNull Bundle bundle)
+            throws IOException {
+        Parcel parcel = Parcel.obtain();
+        try {
+            parcel.writeBundle(bundle);
+            byte[] serializedMessage = parcel.marshall();
+            outputStream.writeInt(serializedMessage.length);
+            outputStream.write(serializedMessage);
+        } finally {
+            parcel.recycle();
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        mMigratedFile.delete();
+    }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index bf733ed..0f6468a 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -19,6 +19,8 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
+import android.app.appsearch.exceptions.AppSearchException;
+import android.app.appsearch.util.SchemaMigrationUtil;
 import android.os.Bundle;
 import android.os.ParcelableException;
 import android.os.RemoteException;
@@ -26,14 +28,17 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import com.android.internal.infra.AndroidFuture;
 import com.android.internal.util.Preconditions;
 
 import java.io.Closeable;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
@@ -55,7 +60,6 @@
     private boolean mIsMutated = false;
     private boolean mIsClosed = false;
 
-
     /**
      * Creates a search session for the client, defined by the {@code userId} and
      * {@code packageName}.
@@ -104,18 +108,6 @@
     }
 
     /**
-     * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
-     * @deprecated This method exists only for dogfooder transition and must be removed.
-     */
-    @Deprecated
-    public void setSchema(
-            @NonNull SetSchemaRequest request,
-            @NonNull @CallbackExecutor Executor callbackExecutor,
-            @NonNull Consumer<AppSearchResult<SetSchemaResponse>> callback) {
-        setSchema(request, callbackExecutor, callbackExecutor, callback);
-    }
-
-    /**
      * Sets the schema that represents the organizational structure of data within the AppSearch
      * database.
      *
@@ -157,26 +149,65 @@
             }
             schemasPackageAccessibleBundles.put(entry.getKey(), packageIdentifierBundles);
         }
+
+        // No need to trigger migration if user never set migrator
+        if (request.getMigrators().isEmpty()) {
+            setSchemaNoMigrations(
+                    request,
+                    schemaBundles,
+                    schemasPackageAccessibleBundles,
+                    callbackExecutor,
+                    callback);
+            return;
+        }
+
         try {
+            // Migration process
+            // 1. Generate the current and the final version map.
+            // TODO(b/182855402) Release binder thread and move the heavy work into worker thread.
+            AndroidFuture<AppSearchResult<GetSchemaResponse>> future = new AndroidFuture<>();
+            getSchema(callbackExecutor, future::complete);
+            AppSearchResult<GetSchemaResponse> getSchemaResult = future.get();
+            if (!getSchemaResult.isSuccess()) {
+                callback.accept(AppSearchResult.newFailedResult(getSchemaResult));
+                return;
+            }
+            GetSchemaResponse getSchemaResponse = getSchemaResult.getResultValue();
+            Set<AppSearchSchema> currentSchemas = getSchemaResponse.getSchemas();
+            Map<String, Integer> currentVersionMap =
+                    SchemaMigrationUtil.buildVersionMap(currentSchemas,
+                            getSchemaResponse.getVersion());
+            Map<String, Integer> finalVersionMap =
+                    SchemaMigrationUtil.buildVersionMap(request.getSchemas(), request.getVersion());
+
+            // 2. SetSchema with forceOverride=false, to retrieve the list of incompatible/deleted
+            // types.
             mService.setSchema(
                     mPackageName,
                     mDatabaseName,
                     schemaBundles,
                     new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
                     schemasPackageAccessibleBundles,
-                    request.isForceOverride(),
+                    /*forceOverride=*/ false,
                     mUserId,
                     request.getVersion(),
                     new IAppSearchResultCallback.Stub() {
                         public void onResult(AppSearchResult result) {
                             callbackExecutor.execute(() -> {
                                 if (result.isSuccess()) {
-                                    callback.accept(
-                                            // TODO(b/177266929) implement Migration in platform.
-                                            // TODO(b/183177268): once migration is implemented, run
-                                            //  it on workExecutor.
-                                            AppSearchResult.newSuccessfulResult(
-                                                    new SetSchemaResponse.Builder().build()));
+                                    // TODO(b/183177268): once migration is implemented, run
+                                    //  it on workExecutor.
+                                    try {
+                                        Bundle bundle = (Bundle) result.getResultValue();
+                                        SetSchemaResponse setSchemaResponse =
+                                                new SetSchemaResponse(bundle);
+                                        setSchemaMigration(
+                                                request, setSchemaResponse, schemaBundles,
+                                                schemasPackageAccessibleBundles, currentVersionMap,
+                                                finalVersionMap, callback);
+                                    } catch (Throwable t) {
+                                        callback.accept(AppSearchResult.throwableToFailedResult(t));
+                                    }
                                 } else {
                                     callback.accept(result);
                                 }
@@ -186,6 +217,8 @@
             mIsMutated = true;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
+        }  catch (Throwable t) {
+            callback.accept(AppSearchResult.throwableToFailedResult(t));
         }
     }
 
@@ -627,4 +660,159 @@
             }
         }
     }
+
+    /**
+     * Set schema to Icing for no-migration scenario.
+     *
+     * <p>We only need one time {@link #setSchema} call for no-migration scenario by using the
+     * forceoverride in the request.
+     */
+    private void setSchemaNoMigrations(@NonNull SetSchemaRequest request,
+            @NonNull List<Bundle> schemaBundles,
+            @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<AppSearchResult<SetSchemaResponse>> callback) {
+        try {
+            mService.setSchema(
+                    mPackageName,
+                    mDatabaseName,
+                    schemaBundles,
+                    new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
+                    schemasPackageAccessibleBundles,
+                    request.isForceOverride(),
+                    mUserId,
+                    request.getVersion(),
+                    new IAppSearchResultCallback.Stub() {
+                        public void onResult(AppSearchResult result) {
+                            executor.execute(() -> {
+                                if (result.isSuccess()) {
+                                    try {
+                                        SetSchemaResponse setSchemaResponse =
+                                                new SetSchemaResponse(
+                                                        (Bundle) result.getResultValue());
+                                        if (!request.isForceOverride()) {
+                                            // Throw exception if there is any deleted types or
+                                            // incompatible types. That's the only case we swallowed
+                                            // in the AppSearchImpl#setSchema().
+                                            checkDeletedAndIncompatible(
+                                                    setSchemaResponse.getDeletedTypes(),
+                                                    setSchemaResponse.getIncompatibleTypes());
+                                        }
+                                        callback.accept(AppSearchResult
+                                                .newSuccessfulResult(setSchemaResponse));
+                                    } catch (Throwable t) {
+                                        callback.accept(AppSearchResult.throwableToFailedResult(t));
+                                    }
+                                } else {
+                                    callback.accept(result);
+                                }
+                            });
+                        }
+                    });
+            mIsMutated = true;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set schema to Icing for migration scenario.
+     *
+     * <p>First time {@link #setSchema} call with forceOverride is false gives us all incompatible
+     * changes. After trigger migrations, the second time call {@link #setSchema} will actually
+     * apply the changes.
+     *
+     * @param setSchemaResponse the result of the first setSchema call with forceOverride=false.
+     */
+    private void setSchemaMigration(@NonNull SetSchemaRequest request,
+            @NonNull SetSchemaResponse setSchemaResponse,
+            @NonNull List<Bundle> schemaBundles,
+            @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles,
+            @NonNull Map<String, Integer> currentVersionMap, Map<String, Integer> finalVersionMap,
+            @NonNull Consumer<AppSearchResult<SetSchemaResponse>> callback)
+            throws AppSearchException, IOException, RemoteException, ExecutionException,
+            InterruptedException {
+        // 1. If forceOverride is false, check that all incompatible types will be migrated.
+        // If some aren't we must throw an error, rather than proceeding and deleting those
+        // types.
+        if (!request.isForceOverride()) {
+            Set<String> unmigratedTypes = SchemaMigrationUtil.getUnmigratedIncompatibleTypes(
+                    setSchemaResponse.getIncompatibleTypes(),
+                    request.getMigrators(),
+                    currentVersionMap,
+                    finalVersionMap);
+            // check if there are any unmigrated types or deleted types. If there are, we will throw
+            // an exception.
+            // Since the force override is false, the schema will not have been set if there are any
+            // incompatible or deleted types.
+            checkDeletedAndIncompatible(setSchemaResponse.getDeletedTypes(),
+                    unmigratedTypes);
+        }
+
+        try (AppSearchMigrationHelper migrationHelper =
+                     new AppSearchMigrationHelper(mService, mUserId, currentVersionMap,
+                             finalVersionMap, mPackageName, mDatabaseName)) {
+            Map<String, Migrator> migratorMap = request.getMigrators();
+
+            // 2. Trigger migration for all migrators.
+            // TODO(b/177266929) trigger migration for all types together rather than separately.
+            Set<String> migratedTypes = new ArraySet<>();
+            for (Map.Entry<String, Migrator> entry : migratorMap.entrySet()) {
+                String schemaType = entry.getKey();
+                Migrator migrator = entry.getValue();
+                if (SchemaMigrationUtil.shouldTriggerMigration(
+                        schemaType, migrator, currentVersionMap, finalVersionMap)) {
+                    migrationHelper.queryAndTransform(schemaType, migrator);
+                    migratedTypes.add(schemaType);
+                }
+            }
+
+            // 3. SetSchema a second time with forceOverride=true if the first attempted failed.
+            if (!setSchemaResponse.getIncompatibleTypes().isEmpty()
+                    || !setSchemaResponse.getDeletedTypes().isEmpty()) {
+                AndroidFuture<AppSearchResult<SetSchemaResponse>> future = new AndroidFuture<>();
+                // only trigger second setSchema() call if the first one is fail.
+                mService.setSchema(
+                        mPackageName,
+                        mDatabaseName,
+                        schemaBundles,
+                        new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
+                        schemasPackageAccessibleBundles,
+                        /*forceOverride=*/ true,
+                        mUserId,
+                        request.getVersion(),
+                        new IAppSearchResultCallback.Stub() {
+                            @Override
+                            public void onResult(AppSearchResult result) throws RemoteException {
+                                future.complete(result);
+                            }
+                        });
+                AppSearchResult<SetSchemaResponse> secondSetSchemaResult = future.get();
+                if (!secondSetSchemaResult.isSuccess()) {
+                    // we failed to set the schema in second time with force override = true, which
+                    // is an impossible case. Since we only swallow the incompatible error in the
+                    // first setSchema call, all other errors will be thrown at the first time.
+                    callback.accept(secondSetSchemaResult);
+                    return;
+                }
+            }
+
+            SetSchemaResponse.Builder responseBuilder = setSchemaResponse.toBuilder()
+                    .addMigratedTypes(migratedTypes);
+            callback.accept(migrationHelper.putMigratedDocuments(responseBuilder));
+        }
+    }
+
+    /**  Checks the setSchema() call won't delete any types or has incompatible types. */
+    //TODO(b/177266929) move this method to util
+    private void checkDeletedAndIncompatible(Set<String> deletedTypes,
+            Set<String> incompatibleTypes)
+            throws AppSearchException {
+        if (!deletedTypes.isEmpty() || !incompatibleTypes.isEmpty()) {
+            String newMessage = "Schema is incompatible."
+                    + "\n  Deleted types: " + deletedTypes
+                    + "\n  Incompatible types: " + incompatibleTypes;
+            throw new AppSearchException(AppSearchResult.RESULT_INVALID_SCHEMA, newMessage);
+        }
+    }
 }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index d436488..48c397f 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -21,6 +21,7 @@
 import android.app.appsearch.AppSearchResult;
 import android.app.appsearch.IAppSearchBatchResultCallback;
 import android.app.appsearch.IAppSearchResultCallback;
+import android.os.ParcelFileDescriptor;
 import com.android.internal.infra.AndroidFuture;
 
 parcelable SearchResults;
@@ -41,7 +42,8 @@
      *     incompatible documents will be deleted.
      * @param userId Id of the calling user
      * @param callback {@link IAppSearchResultCallback#onResult} will be called with an
-     *     {@link AppSearchResult}&lt;{@link Void}&gt;.
+     *     {@link AppSearchResult}&lt;{@link Bundle}&gt;, where the value are
+     *     {@link SetSchemaResponse} bundle.
      */
     void setSchema(
         in String packageName,
@@ -189,6 +191,47 @@
     void invalidateNextPageToken(in long nextPageToken, in int userId);
 
     /**
+    * Searches a document based on a given specifications.
+    *
+    * <p>Documents will be save to the given ParcelFileDescriptor
+    *
+    * @param packageName The name of the package to query over.
+    * @param databaseName The databaseName this query for.
+    * @param fileDescriptor The ParcelFileDescriptor where documents should be written to.
+    * @param queryExpression String to search for.
+    * @param searchSpecBundle SearchSpec bundle.
+    * @param userId Id of the calling user.
+    * @param callback {@link IAppSearchResultCallback#onResult} will be called with an
+    *        {@link AppSearchResult}&lt;{@code null}&gt;.
+    */
+    void writeQueryResultsToFile(
+        in String packageName,
+        in String databaseName,
+        in ParcelFileDescriptor fileDescriptor,
+        in String queryExpression,
+        in Bundle searchSpecBundle,
+        in int userId,
+        in IAppSearchResultCallback callback);
+
+    /**
+    * Inserts documents from the given file into the index.
+    *
+    * @param packageName The name of the package that owns this document.
+    * @param databaseName  The name of the database where this document lives.
+    * @param fileDescriptor The ParcelFileDescriptor where documents should be read from.
+    * @param userId Id of the calling user.
+    * @param callback {@link IAppSearchResultCallback#onResult} will be called with an
+    *     {@link AppSearchResult}&lt;{@link List}&lt;{@link Bundle}&gt;&gt;, where the value are
+    *     MigrationFailure bundles.
+    */
+    void putDocumentsFromFile(
+        in String packageName,
+        in String databaseName,
+        in ParcelFileDescriptor fileDescriptor,
+        in int userId,
+        in IAppSearchResultCallback callback);
+
+    /**
      * Reports usage of a particular document by URI and namespace.
      *
      * <p>A usage report represents an event in which a user interacted with or viewed a document.
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
index 79b7b75..a8048dc 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
@@ -17,7 +17,6 @@
 package android.app.appsearch;
 
 import android.annotation.IntDef;
-import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.appsearch.exceptions.IllegalSchemaException;
@@ -77,12 +76,6 @@
         return mBundle.getString(SCHEMA_TYPE_FIELD, "");
     }
 
-    /** @deprecated Use {@link GetSchemaResponse#getVersion()} instead. */
-    @Deprecated
-    public @IntRange(from = 0) int getVersion() {
-        return 0;
-    }
-
     /**
      * Returns the list of {@link PropertyConfig}s that are part of this schema.
      *
@@ -150,17 +143,6 @@
         }
 
         /**
-         * @deprecated TODO(b/181887768): This method is a no-op and only exists for dogfooder
-         *     transition.
-         */
-        @Deprecated
-        @NonNull
-        public AppSearchSchema.Builder setVersion(@IntRange(from = 0) int version) {
-            Preconditions.checkState(!mBuilt, "Builder has already been used");
-            return this;
-        }
-
-        /**
          * Constructs a new {@link AppSearchSchema} from the contents of this builder.
          *
          * <p>After calling this method, the builder must no longer be used.
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
index 4ce95ea..8c9d950 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -46,15 +46,6 @@
 public class GenericDocument {
     private static final String TAG = "AppSearchGenericDocumen";
 
-    /**
-     * The default empty namespace.
-     *
-     * <p>TODO(b/181887768): This exists only for dogfooder transition and must be removed.
-     *
-     * @deprecated This exists only for dogfooder transition and must be removed.
-     */
-    @Deprecated public static final String DEFAULT_NAMESPACE = "";
-
     /** The maximum number of elements in a repeatable field. */
     private static final int MAX_REPEATED_PROPERTY_LENGTH = 100;
 
@@ -585,41 +576,6 @@
          *
          * <p>Once {@link #build} is called, the instance can no longer be used.
          *
-         * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
-         * removed.
-         *
-         * @param uri the URI to set for the {@link GenericDocument}.
-         * @param schemaType the {@link AppSearchSchema} type of the {@link GenericDocument}. The
-         *     provided {@code schemaType} must be defined using {@link AppSearchSession#setSchema}
-         *     prior to inserting a document of this {@code schemaType} into the AppSearch index
-         *     using {@link AppSearchSession#put}. Otherwise, the document will be rejected by
-         *     {@link AppSearchSession#put} with result code {@link
-         *     AppSearchResult#RESULT_NOT_FOUND}.
-         * @deprecated Please supply the namespace in {@link #Builder(String, String, String)}
-         *     instead. This method exists only for dogfooder transition and must be removed.
-         */
-        @Deprecated
-        @SuppressWarnings("unchecked")
-        public Builder(@NonNull String uri, @NonNull String schemaType) {
-            Preconditions.checkNotNull(uri);
-            Preconditions.checkNotNull(schemaType);
-            mBuilderTypeInstance = (BuilderType) this;
-            mBundle.putString(GenericDocument.URI_FIELD, uri);
-            mBundle.putString(GenericDocument.SCHEMA_TYPE_FIELD, schemaType);
-            mBundle.putString(GenericDocument.NAMESPACE_FIELD, DEFAULT_NAMESPACE);
-            // Set current timestamp for creation timestamp by default.
-            mBundle.putLong(
-                    GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, System.currentTimeMillis());
-            mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS);
-            mBundle.putInt(GenericDocument.SCORE_FIELD, DEFAULT_SCORE);
-            mBundle.putBundle(PROPERTIES_FIELD, mProperties);
-        }
-
-        /**
-         * Creates a new {@link GenericDocument.Builder}.
-         *
-         * <p>Once {@link #build} is called, the instance can no longer be used.
-         *
          * <p>URIs are unique within a namespace.
          *
          * <p>The number of namespaces per app should be kept small for efficiency reasons.
@@ -651,29 +607,6 @@
         }
 
         /**
-         * Sets the app-defined namespace this document resides in. No special values are reserved
-         * or understood by the infrastructure.
-         *
-         * <p>URIs are unique within a namespace.
-         *
-         * <p>The number of namespaces per app should be kept small for efficiency reasons.
-         *
-         * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
-         * removed.
-         *
-         * @throws IllegalStateException if the builder has already been used.
-         * @deprecated Please supply the namespace in {@link #Builder(String, String, String)}
-         *     instead. This method exists only for dogfooder transition and must be removed.
-         */
-        @Deprecated
-        @NonNull
-        public BuilderType setNamespace(@NonNull String namespace) {
-            Preconditions.checkState(!mBuilt, "Builder has already been used");
-            mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace);
-            return mBuilderTypeInstance;
-        }
-
-        /**
          * Sets the score of the {@link GenericDocument}.
          *
          * <p>The score is a query-independent measure of the document's quality, relative to other
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
index 6881a27..1719e14 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
@@ -107,49 +107,17 @@
      * <p>Once {@link #build} is called, the instance can no longer be used.
      */
     public static final class Builder {
-        private String mNamespace;
+        private final String mNamespace;
         private final Set<String> mUris = new ArraySet<>();
         private final Map<String, List<String>> mProjectionTypePropertyPaths = new ArrayMap<>();
         private boolean mBuilt = false;
 
-        /**
-         * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
-         *
-         * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
-         *     exists only for dogfooder transition and must be removed.
-         */
-        @Deprecated
-        public Builder() {
-            mNamespace = GenericDocument.DEFAULT_NAMESPACE;
-        }
-
         /** Creates a {@link GetByUriRequest.Builder} instance. */
         public Builder(@NonNull String namespace) {
             mNamespace = Preconditions.checkNotNull(namespace);
         }
 
         /**
-         * Sets the namespace to retrieve documents for.
-         *
-         * <p>If this is not called, the namespace defaults to an empty string.
-         *
-         * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
-         * removed.
-         *
-         * @throws IllegalStateException if the builder has already been used.
-         * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
-         *     exists only for dogfooder transition and must
-         */
-        @Deprecated
-        @NonNull
-        public Builder setNamespace(@NonNull String namespace) {
-            Preconditions.checkState(!mBuilt, "Builder has already been used");
-            Preconditions.checkNotNull(namespace);
-            mNamespace = namespace;
-            return this;
-        }
-
-        /**
          * Adds one or more URIs to the request.
          *
          * @throws IllegalStateException if the builder has already been used.
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java
index 3e69367..1f56ef3 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java
@@ -24,30 +24,17 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.Set;
 
 /** The response class of {@link AppSearchSession#getSchema} */
-// TODO(b/181887768) extends only for dogfooder transition. */
-public class GetSchemaResponse extends HashSet<AppSearchSchema> {
+public class GetSchemaResponse {
     private static final String VERSION_FIELD = "version";
     private static final String SCHEMAS_FIELD = "schemas";
 
     private final Bundle mBundle;
 
-    // TODO(b/181887768) Remove this method once this class no longer extends HashSet. */
-    private static Set<AppSearchSchema> getSchemasFromBundle(Bundle bundle) {
-        ArrayList<Bundle> schemaBundles = bundle.getParcelableArrayList(SCHEMAS_FIELD);
-        Set<AppSearchSchema> schemas = new ArraySet<>(schemaBundles.size());
-        for (int i = 0; i < schemaBundles.size(); i++) {
-            schemas.add(new AppSearchSchema(schemaBundles.get(i)));
-        }
-        return schemas;
-    }
-
     GetSchemaResponse(@NonNull Bundle bundle) {
-        super(getSchemasFromBundle(Preconditions.checkNotNull(bundle)));
-        mBundle = bundle;
+        mBundle = Preconditions.checkNotNull(bundle);
     }
 
     /**
@@ -72,10 +59,17 @@
 
     /**
      * Return the schemas most recently successfully provided to {@link AppSearchSession#setSchema}.
+     *
+     * <p>It is inefficient to call this method repeatedly.
      */
     @NonNull
     public Set<AppSearchSchema> getSchemas() {
-        return this;
+        ArrayList<Bundle> schemaBundles = mBundle.getParcelableArrayList(SCHEMAS_FIELD);
+        Set<AppSearchSchema> schemas = new ArraySet<>(schemaBundles.size());
+        for (int i = 0; i < schemaBundles.size(); i++) {
+            schemas.add(new AppSearchSchema(schemaBundles.get(i)));
+        }
+        return schemas;
     }
 
     /** Builder for {@link GetSchemaResponse} objects. */
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
index 455cf3a..8da68c0 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
@@ -59,48 +59,16 @@
      * <p>Once {@link #build} is called, the instance can no longer be used.
      */
     public static final class Builder {
-        private String mNamespace;
+        private final String mNamespace;
         private final Set<String> mUris = new ArraySet<>();
         private boolean mBuilt = false;
 
-        /**
-         * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
-         *
-         * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
-         *     exists only for dogfooder transition and must be removed.
-         */
-        @Deprecated
-        public Builder() {
-            mNamespace = GenericDocument.DEFAULT_NAMESPACE;
-        }
-
         /** Creates a {@link RemoveByUriRequest.Builder} instance. */
         public Builder(@NonNull String namespace) {
             mNamespace = Preconditions.checkNotNull(namespace);
         }
 
         /**
-         * Sets the namespace to remove documents for.
-         *
-         * <p>If this is not set, it defaults to an empty string.
-         *
-         * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
-         * removed.
-         *
-         * @throws IllegalStateException if the builder has already been used.
-         * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
-         *     exists only for dogfooder transition and must
-         */
-        @Deprecated
-        @NonNull
-        public Builder setNamespace(@NonNull String namespace) {
-            Preconditions.checkState(!mBuilt, "Builder has already been used");
-            Preconditions.checkNotNull(namespace);
-            mNamespace = namespace;
-            return this;
-        }
-
-        /**
          * Adds one or more URIs to the request.
          *
          * @throws IllegalStateException if the builder has already been used.
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
index 2cd08c6..646e73c 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
@@ -62,49 +62,17 @@
 
     /** Builder for {@link ReportUsageRequest} objects. */
     public static final class Builder {
-        private String mNamespace;
+        private final String mNamespace;
         private String mUri;
         private Long mUsageTimeMillis;
         private boolean mBuilt = false;
 
-        /**
-         * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
-         *
-         * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
-         *     exists only for dogfooder transition and must be removed.
-         */
-        @Deprecated
-        public Builder() {
-            mNamespace = GenericDocument.DEFAULT_NAMESPACE;
-        }
-
         /** Creates a {@link ReportUsageRequest.Builder} instance. */
         public Builder(@NonNull String namespace) {
             mNamespace = Preconditions.checkNotNull(namespace);
         }
 
         /**
-         * Sets which namespace the document being used belongs to.
-         *
-         * <p>If this is not set, it defaults to an empty string.
-         *
-         * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
-         * removed.
-         *
-         * @throws IllegalStateException if the builder has already been used
-         * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
-         *     exists only for dogfooder transition and must
-         */
-        @Deprecated
-        @NonNull
-        public ReportUsageRequest.Builder setNamespace(@NonNull String namespace) {
-            Preconditions.checkState(!mBuilt, "Builder has already been used");
-            Preconditions.checkNotNull(namespace);
-            mNamespace = namespace;
-            return this;
-        }
-
-        /**
          * Sets the URI of the document being used.
          *
          * <p>This field is required.
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
index e66056f..55a228d 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
@@ -68,13 +68,6 @@
         return mBundle;
     }
 
-    /** @deprecated TODO(b/181887768): This method exists only for dogfooder transition. */
-    @NonNull
-    @Deprecated
-    public GenericDocument getDocument() {
-        return getGenericDocument();
-    }
-
     /**
      * Contains the matching {@link GenericDocument}.
      *
@@ -142,17 +135,18 @@
      * <ul>
      *   <li>{@link SearchSpec#RANKING_STRATEGY_NONE} - this value will be 0
      *   <li>{@link SearchSpec#RANKING_STRATEGY_DOCUMENT_SCORE} - the value returned by calling
-     *       {@link GenericDocument#getScore()} on the document returned by {@link #getDocument()}
+     *       {@link GenericDocument#getScore()} on the document returned by {@link
+     *       #getGenericDocument()}
      *   <li>{@link SearchSpec#RANKING_STRATEGY_CREATION_TIMESTAMP} - the value returned by calling
      *       {@link GenericDocument#getCreationTimestampMillis()} on the document returned by {@link
-     *       #getDocument()}
+     *       #getGenericDocument()}
      *   <li>{@link SearchSpec#RANKING_STRATEGY_RELEVANCE_SCORE} - an arbitrary double value where a
      *       higher value means more relevant
      *   <li>{@link SearchSpec#RANKING_STRATEGY_USAGE_COUNT} - the number of times usage has been
-     *       reported for the document returned by {@link #getDocument()}
+     *       reported for the document returned by {@link #getGenericDocument()}
      *   <li>{@link SearchSpec#RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP} - the timestamp of the
      *       most recent usage that has been reported for the document returned by {@link
-     *       #getDocument()}
+     *       #getGenericDocument()}
      * </ul>
      *
      * @return Ranking signal of the document
@@ -354,13 +348,6 @@
             return mFullText;
         }
 
-        /** @deprecated TODO(b/181887768): This method exists only for dogfooder transition. */
-        @NonNull
-        @Deprecated
-        public MatchRange getExactMatchPosition() {
-            return getExactMatchRange();
-        }
-
         /**
          * Gets the exact {@link MatchRange} corresponding to the given entry.
          *
@@ -387,13 +374,6 @@
             return getSubstring(getExactMatchRange());
         }
 
-        /** @deprecated TODO(b/181887768): This method exists only for dogfooder transition. */
-        @NonNull
-        @Deprecated
-        public MatchRange getSnippetPosition() {
-            return getSnippetRange();
-        }
-
         /**
          * Gets the snippet {@link MatchRange} corresponding to the given entry.
          *
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
index e840ffc..1324451 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
@@ -109,16 +109,6 @@
     }
 
     /**
-     * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
-     * @deprecated This method exists only for dogfooder transition and must be removed.
-     */
-    @Deprecated
-    @NonNull
-    public Set<String> getSchemasNotVisibleToSystemUi() {
-        return getSchemasNotDisplayedBySystem();
-    }
-
-    /**
      * Returns all the schema types that are opted out of being displayed and visible on any system
      * UI surface.
      */
@@ -222,17 +212,6 @@
         }
 
         /**
-         * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
-         * @deprecated This method exists only for dogfooder transition and must be removed.
-         */
-        @Deprecated
-        @NonNull
-        public Builder setSchemaTypeVisibilityForSystemUi(
-                @NonNull String schemaType, boolean displayed) {
-            return setSchemaTypeDisplayedBySystem(schemaType, displayed);
-        }
-
-        /**
          * Sets whether or not documents from the provided {@code schemaType} will be displayed and
          * visible on any system UI surface.
          *
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
index 32d7e043..c9473bd 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
@@ -20,11 +20,12 @@
 import android.app.appsearch.AppSearchResult;
 import android.app.appsearch.AppSearchSchema;
 import android.app.appsearch.Migrator;
-import android.app.appsearch.SetSchemaResponse;
 import android.app.appsearch.exceptions.AppSearchException;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Log;
 
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
@@ -35,70 +36,84 @@
  * @hide
  */
 public final class SchemaMigrationUtil {
+    private static final String TAG = "AppSearchMigrateUtil";
+
     private SchemaMigrationUtil() {}
 
-    /** Returns all active {@link Migrator}s that need to be triggered in this migration. */
+    /**
+     * Finds out which incompatible schema type won't be migrated by comparing its current and final
+     * version number.
+     */
     @NonNull
-    public static Map<String, Migrator> getActiveMigrators(
-            @NonNull Set<AppSearchSchema> existingSchemas,
+    public static Set<String> getUnmigratedIncompatibleTypes(
+            @NonNull Set<String> incompatibleSchemaTypes,
             @NonNull Map<String, Migrator> migrators,
-            int currentVersion,
-            int finalVersion) {
-        if (currentVersion == finalVersion) {
-            return Collections.emptyMap();
-        }
-        Set<String> existingTypes = new ArraySet<>(existingSchemas.size());
-        for (AppSearchSchema schema : existingSchemas) {
-            existingTypes.add(schema.getSchemaType());
-        }
-
-        Map<String, Migrator> activeMigrators = new ArrayMap<>();
-        for (Map.Entry<String, Migrator> entry : migrators.entrySet()) {
-            // The device contains the source type, and we should trigger migration for the type.
-            String schemaType = entry.getKey();
-            Migrator migrator = entry.getValue();
-            if (existingTypes.contains(schemaType)
-                    && migrator.shouldMigrate(currentVersion, finalVersion)) {
-                activeMigrators.put(schemaType, migrator);
+            @NonNull Map<String, Integer> currentVersionMap,
+            @NonNull Map<String, Integer> finalVersionMap)
+            throws AppSearchException {
+        Set<String> unmigratedSchemaTypes = new ArraySet<>();
+        for (String unmigratedSchemaType : incompatibleSchemaTypes) {
+            Integer currentVersion = currentVersionMap.get(unmigratedSchemaType);
+            Integer finalVersion = finalVersionMap.get(unmigratedSchemaType);
+            if (currentVersion == null) {
+                // impossible, we have done something wrong.
+                throw new AppSearchException(
+                        AppSearchResult.RESULT_UNKNOWN_ERROR,
+                        "Cannot find the current version number for schema type: "
+                                + unmigratedSchemaType);
+            }
+            if (finalVersion == null) {
+                // The schema doesn't exist in the SetSchemaRequest.
+                unmigratedSchemaTypes.add(unmigratedSchemaType);
+                continue;
+            }
+            // we don't have migrator or won't trigger migration for this schema type.
+            Migrator migrator = migrators.get(unmigratedSchemaType);
+            if (migrator == null
+                    || !migrator.shouldMigrate(currentVersion, finalVersion)) {
+                unmigratedSchemaTypes.add(unmigratedSchemaType);
             }
         }
-        return activeMigrators;
+        return Collections.unmodifiableSet(unmigratedSchemaTypes);
     }
 
     /**
-     * Checks the setSchema() call won't delete any types or has incompatible types after all {@link
-     * Migrator} has been triggered..
+     * Triggers upgrade or downgrade migration for the given schema type if its version stored in
+     * AppSearch is different with the version in the request.
+     *
+     * @return {@code True} if we trigger the migration for the given type.
      */
-    public static void checkDeletedAndIncompatibleAfterMigration(
-            @NonNull SetSchemaResponse setSchemaResponse, @NonNull Set<String> activeMigrators)
+    public static boolean shouldTriggerMigration(
+            @NonNull String schemaType,
+            @NonNull Migrator migrator,
+            @NonNull Map<String, Integer> currentVersionMap,
+            @NonNull Map<String, Integer> finalVersionMap)
             throws AppSearchException {
-        Set<String> unmigratedIncompatibleTypes =
-                new ArraySet<>(setSchemaResponse.getIncompatibleTypes());
-        unmigratedIncompatibleTypes.removeAll(activeMigrators);
-
-        Set<String> unmigratedDeletedTypes = new ArraySet<>(setSchemaResponse.getDeletedTypes());
-        unmigratedDeletedTypes.removeAll(activeMigrators);
-
-        // check if there are any unmigrated incompatible types or deleted types. If there
-        // are, we will getActiveMigratorsthrow an exception. That's the only case we
-        // swallowed in the AppSearchImpl#setSchema().
-        // Since the force override is false, the schema will not have been set if there are
-        // any incompatible or deleted types.
-        checkDeletedAndIncompatible(unmigratedDeletedTypes, unmigratedIncompatibleTypes);
+        Integer currentVersion = currentVersionMap.get(schemaType);
+        Integer finalVersion = finalVersionMap.get(schemaType);
+        if (currentVersion == null) {
+            Log.d(TAG, "The SchemaType: " + schemaType + " not present in AppSearch.");
+            return false;
+        }
+        if (finalVersion == null) {
+            throw new AppSearchException(
+                    AppSearchResult.RESULT_INVALID_ARGUMENT,
+                    "Receive a migrator for schema type : "
+                            + schemaType
+                            + ", but the schema doesn't exist in the request.");
+        }
+        return migrator.shouldMigrate(currentVersion, finalVersion);
     }
 
-    /** Checks the setSchema() call won't delete any types or has incompatible types. */
-    public static void checkDeletedAndIncompatible(
-            @NonNull Set<String> deletedTypes, @NonNull Set<String> incompatibleTypes)
-            throws AppSearchException {
-        if (deletedTypes.size() > 0 || incompatibleTypes.size() > 0) {
-            String newMessage =
-                    "Schema is incompatible."
-                            + "\n  Deleted types: "
-                            + deletedTypes
-                            + "\n  Incompatible types: "
-                            + incompatibleTypes;
-            throw new AppSearchException(AppSearchResult.RESULT_INVALID_SCHEMA, newMessage);
+    /** Builds a Map of SchemaType and its version of given set of {@link AppSearchSchema}. */
+    //TODO(b/182620003) remove this method once support migrate to another type
+    @NonNull
+    public static Map<String, Integer> buildVersionMap(
+            @NonNull Collection<AppSearchSchema> schemas, int version) {
+        Map<String, Integer> currentVersionMap = new ArrayMap<>(schemas.size());
+        for (AppSearchSchema currentSchema : schemas) {
+            currentVersionMap.put(currentSchema.getSchemaType(), version);
         }
+        return currentVersionMap;
     }
 }
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 6e3fb82..91ed6cd 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -21,6 +21,7 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.appsearch.AppSearchBatchResult;
+import android.app.appsearch.AppSearchMigrationHelper;
 import android.app.appsearch.AppSearchResult;
 import android.app.appsearch.AppSearchSchema;
 import android.app.appsearch.GenericDocument;
@@ -31,10 +32,12 @@
 import android.app.appsearch.PackageIdentifier;
 import android.app.appsearch.SearchResultPage;
 import android.app.appsearch.SearchSpec;
+import android.app.appsearch.SetSchemaResponse;
 import android.content.Context;
 import android.content.pm.PackageManagerInternal;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
 import android.os.ParcelableException;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -49,6 +52,11 @@
 import com.android.server.SystemService;
 import com.android.server.appsearch.external.localstorage.AppSearchImpl;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -125,7 +133,7 @@
                     schemasPackageAccessible.put(entry.getKey(), packageIdentifiers);
                 }
                 AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUserId);
-                impl.setSchema(
+                SetSchemaResponse setSchemaResponse = impl.setSchema(
                         packageName,
                         databaseName,
                         schemas,
@@ -133,8 +141,8 @@
                         schemasPackageAccessible,
                         forceOverride,
                         schemaVersion);
-                invokeCallbackOnResult(
-                        callback, AppSearchResult.newSuccessfulResult(/*result=*/ null));
+                invokeCallbackOnResult(callback,
+                        AppSearchResult.newSuccessfulResult(setSchemaResponse.getBundle()));
             } catch (Throwable t) {
                 invokeCallbackOnError(callback, t);
             } finally {
@@ -399,6 +407,98 @@
         }
 
         @Override
+        public void writeQueryResultsToFile(
+                @NonNull String packageName,
+                @NonNull String databaseName,
+                @NonNull ParcelFileDescriptor fileDescriptor,
+                @NonNull String queryExpression,
+                @NonNull Bundle searchSpecBundle,
+                @UserIdInt int userId,
+                @NonNull IAppSearchResultCallback callback) {
+            int callingUid = Binder.getCallingUid();
+            int callingUserId = handleIncomingUser(userId, callingUid);
+            final long callingIdentity = Binder.clearCallingIdentity();
+            try {
+                verifyCallingPackage(callingUid, packageName);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(callingUserId);
+                // we don't need to append the file. The file is always brand new.
+                try (DataOutputStream outputStream = new DataOutputStream(
+                        new FileOutputStream(fileDescriptor.getFileDescriptor()))) {
+                    SearchResultPage searchResultPage = impl.query(
+                            packageName,
+                            databaseName,
+                            queryExpression,
+                            new SearchSpec(searchSpecBundle));
+                    while (!searchResultPage.getResults().isEmpty()) {
+                        for (int i = 0; i < searchResultPage.getResults().size(); i++) {
+                            AppSearchMigrationHelper.writeBundleToOutputStream(
+                                    outputStream, searchResultPage.getResults().get(i)
+                                            .getGenericDocument().getBundle());
+                        }
+                        searchResultPage = impl.getNextPage(searchResultPage.getNextPageToken());
+                    }
+                }
+                invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
+            } catch (Throwable t) {
+                invokeCallbackOnError(callback, t);
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
+            }
+        }
+
+        @Override
+        public void putDocumentsFromFile(
+                @NonNull String packageName,
+                @NonNull String databaseName,
+                @NonNull ParcelFileDescriptor fileDescriptor,
+                @UserIdInt int userId,
+                @NonNull IAppSearchResultCallback callback) {
+            int callingUid = Binder.getCallingUid();
+            int callingUserId = handleIncomingUser(userId, callingUid);
+            final long callingIdentity = Binder.clearCallingIdentity();
+            try {
+                verifyCallingPackage(callingUid, packageName);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(callingUserId);
+
+                GenericDocument document;
+                ArrayList<Bundle> migrationFailureBundles = new ArrayList<>();
+                try (DataInputStream inputStream = new DataInputStream(
+                        new FileInputStream(fileDescriptor.getFileDescriptor()))) {
+                    while (true) {
+                        try {
+                            document = AppSearchMigrationHelper
+                                    .readDocumentFromInputStream(inputStream);
+                        } catch (EOFException e) {
+                            // nothing wrong, we just finish the reading.
+                            break;
+                        }
+                        try {
+                            impl.putDocument(packageName, databaseName, document, /*logger=*/ null);
+                        } catch (Throwable t) {
+                            migrationFailureBundles.add(
+                                    new SetSchemaResponse.MigrationFailure.Builder()
+                                            .setNamespace(document.getNamespace())
+                                            .setSchemaType(document.getSchemaType())
+                                            .setUri(document.getUri())
+                                            .setAppSearchResult(
+                                                    AppSearchResult.throwableToFailedResult(t))
+                                            .build().getBundle());
+                        }
+                    }
+                }
+                impl.persistToDisk();
+                invokeCallbackOnResult(callback,
+                        AppSearchResult.newSuccessfulResult(migrationFailureBundles));
+            } catch (Throwable t) {
+                invokeCallbackOnError(callback, t);
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
+            }
+        }
+
+        @Override
         public void reportUsage(
                 @NonNull String packageName,
                 @NonNull String databaseName,
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
index 8c953d1..cacf880 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -25,7 +25,6 @@
 import android.content.pm.PackageManager;
 import android.os.Environment;
 import android.os.UserHandle;
-import android.os.storage.StorageManager;
 import android.util.SparseArray;
 
 import com.android.internal.R;
@@ -130,11 +129,7 @@
 
     private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) {
         // See com.android.internal.app.ChooserActivity::getPinnedSharedPrefs
-        //TODO(b/177685938):Switch from getDataUserCePackageDirectory to getDataSystemCeDirectory
-        File userCeDir =
-                Environment.getDataUserCePackageDirectory(
-                        StorageManager.UUID_PRIVATE_INTERNAL, userId, context.getPackageName());
-        return new File(userCeDir, APP_SEARCH_DIR);
+        return new File(Environment.getDataSystemCeDirectory(userId), APP_SEARCH_DIR);
     }
 
     /**
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
index 7847429..1ed26d6 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
@@ -156,7 +156,7 @@
             AppSearchImpl.createPrefix(PACKAGE_NAME, DATABASE_NAME);
 
     /** Namespace of documents that contain visibility settings */
-    private static final String NAMESPACE = GenericDocument.DEFAULT_NAMESPACE;
+    private static final String NAMESPACE = "";
 
     /**
      * Prefix to add to all visibility document uri's. IcingSearchEngine doesn't allow empty uri's.
@@ -337,9 +337,9 @@
         Preconditions.checkNotNull(schemasPackageAccessible);
 
         // Persist the document
-        GenericDocument.Builder visibilityDocument =
-                new GenericDocument.Builder(/*uri=*/ addUriPrefix(prefix), VISIBILITY_TYPE)
-                        .setNamespace(NAMESPACE);
+        GenericDocument.Builder<?> visibilityDocument =
+                new GenericDocument.Builder<>(
+                        NAMESPACE, /*uri=*/ addUriPrefix(prefix), VISIBILITY_TYPE);
         if (!schemasNotPlatformSurfaceable.isEmpty()) {
             visibilityDocument.setPropertyString(
                     NOT_PLATFORM_SURFACEABLE_PROPERTY,
@@ -351,17 +351,16 @@
         for (Map.Entry<String, List<PackageIdentifier>> entry :
                 schemasPackageAccessible.entrySet()) {
             for (int i = 0; i < entry.getValue().size(); i++) {
-                GenericDocument packageAccessibleDocument =
-                        new GenericDocument.Builder(/*uri=*/ "", PACKAGE_ACCESSIBLE_TYPE)
-                                .setNamespace(NAMESPACE)
-                                .setPropertyString(
-                                        PACKAGE_NAME_PROPERTY,
-                                        entry.getValue().get(i).getPackageName())
-                                .setPropertyBytes(
-                                        SHA_256_CERT_PROPERTY,
-                                        entry.getValue().get(i).getSha256Certificate())
-                                .setPropertyString(ACCESSIBLE_SCHEMA_PROPERTY, entry.getKey())
-                                .build();
+                GenericDocument packageAccessibleDocument = new GenericDocument.Builder<>(
+                        NAMESPACE, /*uri=*/ "", PACKAGE_ACCESSIBLE_TYPE)
+                        .setPropertyString(
+                                PACKAGE_NAME_PROPERTY,
+                                entry.getValue().get(i).getPackageName())
+                        .setPropertyBytes(
+                                SHA_256_CERT_PROPERTY,
+                                entry.getValue().get(i).getSha256Certificate())
+                        .setPropertyString(ACCESSIBLE_SCHEMA_PROPERTY, entry.getKey())
+                        .build();
                 packageAccessibleDocuments.add(packageAccessibleDocument);
             }
             schemaToPackageIdentifierMap.put(entry.getKey(), new ArraySet<>(entry.getValue()));
diff --git a/apex/jobscheduler/framework/Android.bp b/apex/jobscheduler/framework/Android.bp
index b51c2aa..f766fcf 100644
--- a/apex/jobscheduler/framework/Android.bp
+++ b/apex/jobscheduler/framework/Android.bp
@@ -32,6 +32,7 @@
     },
     libs: [
         "app-compat-annotations",
+        "framework-connectivity.stubs.module_lib",
         "framework-minus-apex",
         "unsupportedappusage",
     ],
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
index 78c5b15..3ea1922 100644
--- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java
+++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
@@ -29,6 +30,7 @@
 import android.content.Intent;
 import android.os.Build;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
@@ -42,7 +44,9 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
+import java.util.Objects;
 import java.util.WeakHashMap;
+import java.util.concurrent.Executor;
 
 /**
  * This class provides access to the system alarm services.  These allow you
@@ -194,6 +198,15 @@
     public static final int FLAG_ALLOW_WHILE_IDLE_COMPAT = 1 << 5;
 
     /**
+     * Flag for alarms: Used to mark prioritized alarms. These alarms will get to execute while idle
+     * and can be sent separately from other alarms that may be already due at the time.
+     * These alarms can be set via
+     * {@link #setPrioritized(int, long, long, String, Executor, OnAlarmListener)}
+     * @hide
+     */
+    public static final int FLAG_PRIORITIZE = 1 << 6;
+
+    /**
      * For apps targeting {@link Build.VERSION_CODES#S} or above, APIs
      * {@link #setExactAndAllowWhileIdle(int, long, PendingIntent)} and
      * {@link #setAlarmClock(AlarmClockInfo, PendingIntent)} will require holding a new
@@ -227,15 +240,15 @@
 
     final class ListenerWrapper extends IAlarmListener.Stub implements Runnable {
         final OnAlarmListener mListener;
-        Handler mHandler;
+        Executor mExecutor;
         IAlarmCompleteListener mCompletion;
 
         public ListenerWrapper(OnAlarmListener listener) {
             mListener = listener;
         }
 
-        public void setHandler(Handler h) {
-           mHandler = h;
+        void setExecutor(Executor e) {
+            mExecutor = e;
         }
 
         public void cancel() {
@@ -250,7 +263,7 @@
         public void doAlarm(IAlarmCompleteListener alarmManager) {
             mCompletion = alarmManager;
 
-            mHandler.post(this);
+            mExecutor.execute(this);
         }
 
         @Override
@@ -368,7 +381,7 @@
      */
     public void set(@AlarmType int type, long triggerAtMillis, PendingIntent operation) {
         setImpl(type, triggerAtMillis, legacyExactLength(), 0, 0, operation, null, null,
-                null, null, null);
+                (Handler) null, null, null);
     }
 
     /**
@@ -457,7 +470,7 @@
     public void setRepeating(@AlarmType int type, long triggerAtMillis,
             long intervalMillis, PendingIntent operation) {
         setImpl(type, triggerAtMillis, legacyExactLength(), intervalMillis, 0, operation,
-                null, null, null, null, null);
+                null, null, (Handler) null, null, null);
     }
 
     /**
@@ -507,7 +520,7 @@
     public void setWindow(@AlarmType int type, long windowStartMillis, long windowLengthMillis,
             PendingIntent operation) {
         setImpl(type, windowStartMillis, windowLengthMillis, 0, 0, operation,
-                null, null, null, null, null);
+                null, null, (Handler) null, null, null);
     }
 
     /**
@@ -526,6 +539,53 @@
     }
 
     /**
+     * Schedule an alarm that is prioritized by the system while the device is in power saving modes
+     * such as battery saver and device idle (doze).
+     *
+     * <p>
+     * Apps that use this are not guaranteed to get all alarms as requested during power saving
+     * modes, i.e. the system may still impose restrictions on how frequently these alarms will go
+     * off for a particular application, like requiring a certain minimum duration be elapsed
+     * between consecutive alarms. This duration will be normally be in the order of a few minutes.
+     *
+     * <p>
+     * When the system wakes up to deliver these alarms, it may not deliver any of the other pending
+     * alarms set earlier by the calling app, even the special ones set via
+     * {@link #setAndAllowWhileIdle(int, long, PendingIntent)} or
+     * {@link #setExactAndAllowWhileIdle(int, long, PendingIntent)}. So the caller should not
+     * expect these to arrive in any relative order to its other alarms.
+     *
+     * @param type type of alarm
+     * @param windowStartMillis The earliest time, in milliseconds, that the alarm should
+     *        be delivered, expressed in the appropriate clock's units (depending on the alarm
+     *        type).
+     * @param windowLengthMillis The length of the requested delivery window,
+     *        in milliseconds.  The alarm will be delivered no later than this many
+     *        milliseconds after {@code windowStartMillis}.  Note that this parameter
+     *        is a <i>duration,</i> not the timestamp of the end of the window.
+     * @param tag string describing the alarm, used for logging and battery-use
+     *         attribution
+     * @param listener {@link OnAlarmListener} instance whose
+     *         {@link OnAlarmListener#onAlarm() onAlarm()} method will be
+     *         called when the alarm time is reached.  A given OnAlarmListener instance can
+     *         only be the target of a single pending alarm, just as a given PendingIntent
+     *         can only be used with one alarm at a time.
+     * @param executor {@link Executor} on which to execute the listener's onAlarm()
+     *         callback.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.SCHEDULE_PRIORITIZED_ALARM)
+    public void setPrioritized(@AlarmType int type, long windowStartMillis, long windowLengthMillis,
+            @NonNull String tag, @NonNull Executor executor, @NonNull OnAlarmListener listener) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(tag);
+        Objects.requireNonNull(listener);
+        setImpl(type, windowStartMillis, windowLengthMillis, 0, FLAG_PRIORITIZE, null, listener,
+                tag, executor, null, null);
+    }
+
+    /**
      * Schedule an alarm to be delivered precisely at the stated time.
      *
      * <p>
@@ -565,7 +625,7 @@
      */
     @RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true)
     public void setExact(@AlarmType int type, long triggerAtMillis, PendingIntent operation) {
-        setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, operation, null, null, null,
+        setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, operation, null, null, (Handler) null,
                 null, null);
     }
 
@@ -645,7 +705,7 @@
     @RequiresPermission(Manifest.permission.SCHEDULE_EXACT_ALARM)
     public void setAlarmClock(AlarmClockInfo info, PendingIntent operation) {
         setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, 0, operation,
-                null, null, null, null, info);
+                null, null, (Handler) null, null, info);
     }
 
     /** @hide */
@@ -654,7 +714,7 @@
     public void set(@AlarmType int type, long triggerAtMillis, long windowMillis,
             long intervalMillis, PendingIntent operation, WorkSource workSource) {
         setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0, operation, null, null,
-                null, workSource, null);
+                (Handler) null, workSource, null);
     }
 
     /**
@@ -698,6 +758,15 @@
             long intervalMillis, int flags, PendingIntent operation, final OnAlarmListener listener,
             String listenerTag, Handler targetHandler, WorkSource workSource,
             AlarmClockInfo alarmClock) {
+        final Handler handlerToUse = (targetHandler != null) ? targetHandler : mMainThreadHandler;
+        setImpl(type, triggerAtMillis, windowMillis, intervalMillis, flags, operation, listener,
+                listenerTag, new HandlerExecutor(handlerToUse), workSource, alarmClock);
+    }
+
+    private void setImpl(@AlarmType int type, long triggerAtMillis, long windowMillis,
+            long intervalMillis, int flags, PendingIntent operation, final OnAlarmListener listener,
+            String listenerTag, Executor targetExecutor, WorkSource workSource,
+            AlarmClockInfo alarmClock) {
         if (triggerAtMillis < 0) {
             /* NOTYET
             if (mAlwaysExact) {
@@ -726,9 +795,7 @@
                     sWrappers.put(listener, new WeakReference<>(recipientWrapper));
                 }
             }
-
-            final Handler handler = (targetHandler != null) ? targetHandler : mMainThreadHandler;
-            recipientWrapper.setHandler(handler);
+            recipientWrapper.setExecutor(targetExecutor);
         }
 
         try {
@@ -834,7 +901,7 @@
     public void setInexactRepeating(@AlarmType int type, long triggerAtMillis,
             long intervalMillis, PendingIntent operation) {
         setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, 0, operation, null,
-                null, null, null, null);
+                null, (Handler) null, null, null);
     }
 
     /**
@@ -884,7 +951,7 @@
     public void setAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis,
             PendingIntent operation) {
         setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, 0, FLAG_ALLOW_WHILE_IDLE,
-                operation, null, null, null, null, null);
+                operation, null, null, (Handler) null, null, null);
     }
 
     /**
@@ -945,7 +1012,7 @@
     public void setExactAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis,
             PendingIntent operation) {
         setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, operation,
-                null, null, null, null, null);
+                null, null, (Handler) null, null, null);
     }
 
     /**
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 58fc874..3c9496f 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -23,6 +23,7 @@
 import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_COMPAT;
 import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
 import static android.app.AlarmManager.FLAG_IDLE_UNTIL;
+import static android.app.AlarmManager.FLAG_PRIORITIZE;
 import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE;
 import static android.app.AlarmManager.INTERVAL_DAY;
 import static android.app.AlarmManager.INTERVAL_HOUR;
@@ -100,6 +101,7 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
+import android.util.SparseLongArray;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
@@ -221,6 +223,7 @@
     AlarmHandler mHandler;
     AppWakeupHistory mAppWakeupHistory;
     AppWakeupHistory mAllowWhileIdleHistory;
+    private final SparseLongArray mLastPriorityAlarmDispatch = new SparseLongArray();
     ClockReceiver mClockReceiver;
     final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
     IBinder.DeathRecipient mListenerDeathRecipient;
@@ -432,6 +435,8 @@
 
         @VisibleForTesting
         static final String KEY_CRASH_NON_CLOCK_APPS = "crash_non_clock_apps";
+        @VisibleForTesting
+        static final String KEY_PRIORITY_ALARM_DELAY = "priority_alarm_delay";
 
         private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
         private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
@@ -470,6 +475,8 @@
         // TODO (b/171306433): Change to true by default.
         private static final boolean DEFAULT_CRASH_NON_CLOCK_APPS = false;
 
+        private static final long DEFAULT_PRIORITY_ALARM_DELAY = 9 * 60_000;
+
         // Minimum futurity of a new alarm
         public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
 
@@ -525,6 +532,12 @@
          */
         public boolean CRASH_NON_CLOCK_APPS = DEFAULT_CRASH_NON_CLOCK_APPS;
 
+        /**
+         * Minimum delay between two slots that an app can get for their prioritized alarms, while
+         * the device is in doze.
+         */
+        public long PRIORITY_ALARM_DELAY = DEFAULT_PRIORITY_ALARM_DELAY;
+
         private long mLastAllowWhileIdleWhitelistDuration = -1;
 
         Constants() {
@@ -662,6 +675,10 @@
                             CRASH_NON_CLOCK_APPS = properties.getBoolean(KEY_CRASH_NON_CLOCK_APPS,
                                     DEFAULT_CRASH_NON_CLOCK_APPS);
                             break;
+                        case KEY_PRIORITY_ALARM_DELAY:
+                            PRIORITY_ALARM_DELAY = properties.getLong(KEY_PRIORITY_ALARM_DELAY,
+                                    DEFAULT_PRIORITY_ALARM_DELAY);
+                            break;
                         default:
                             if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) {
                                 // The quotas need to be updated in order, so we can't just rely
@@ -809,6 +826,11 @@
             pw.print(KEY_CRASH_NON_CLOCK_APPS, CRASH_NON_CLOCK_APPS);
             pw.println();
 
+            pw.print(KEY_PRIORITY_ALARM_DELAY);
+            pw.print("=");
+            TimeUtils.formatDuration(PRIORITY_ALARM_DELAY, pw);
+            pw.println();
+
             pw.decreaseIndent();
         }
 
@@ -1794,6 +1816,11 @@
                 batterySaverPolicyElapsed = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
                         alarm.sourcePackage, userId, quota) + window;
             }
+        } else if ((alarm.flags & FLAG_PRIORITIZE) != 0) {
+            final long lastDispatch = mLastPriorityAlarmDispatch.get(alarm.creatorUid, 0);
+            batterySaverPolicyElapsed = (lastDispatch == 0)
+                    ? nowElapsed
+                    : lastDispatch + mConstants.PRIORITY_ALARM_DELAY;
         } else {
             // Not allowed.
             batterySaverPolicyElapsed = nowElapsed + INDEFINITE_DELAY;
@@ -1849,6 +1876,12 @@
                         alarm.sourcePackage, userId, quota) + window;
                 deviceIdlePolicyTime = Math.min(whenInQuota, mPendingIdleUntil.getWhenElapsed());
             }
+        } else if ((alarm.flags & FLAG_PRIORITIZE) != 0) {
+            final long lastDispatch = mLastPriorityAlarmDispatch.get(alarm.creatorUid, 0);
+            final long whenAllowed = (lastDispatch == 0)
+                    ? nowElapsed
+                    : lastDispatch + mConstants.PRIORITY_ALARM_DELAY;
+            deviceIdlePolicyTime = Math.min(whenAllowed, mPendingIdleUntil.getWhenElapsed());
         } else {
             // Not allowed.
             deviceIdlePolicyTime = mPendingIdleUntil.getWhenElapsed();
@@ -2025,7 +2058,12 @@
             // make sure the caller is allowed to use the requested kind of alarm, and also
             // decide what quota and broadcast options to use.
             Bundle idleOptions = null;
-            if (exact || allowWhileIdle) {
+            if ((flags & FLAG_PRIORITIZE) != 0) {
+                getContext().enforcePermission(
+                        Manifest.permission.SCHEDULE_PRIORITIZED_ALARM,
+                        Binder.getCallingPid(), callingUid, "AlarmManager.setPrioritized");
+                flags &= ~(FLAG_ALLOW_WHILE_IDLE | FLAG_ALLOW_WHILE_IDLE_COMPAT);
+            } else if (exact || allowWhileIdle) {
                 final boolean needsPermission;
                 boolean lowerQuota;
                 if (CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION,
@@ -2107,6 +2145,7 @@
                 flags |= FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
                 flags &= ~FLAG_ALLOW_WHILE_IDLE;
                 flags &= ~FLAG_ALLOW_WHILE_IDLE_COMPAT;
+                flags &= ~FLAG_PRIORITIZE;
                 idleOptions = null;
             }
 
@@ -2489,6 +2528,19 @@
             pw.println("Allow while idle history:");
             mAllowWhileIdleHistory.dump(pw, nowELAPSED);
 
+            if (mLastPriorityAlarmDispatch.size() > 0) {
+                pw.println("Last priority alarm dispatches:");
+                pw.increaseIndent();
+                for (int i = 0; i < mLastPriorityAlarmDispatch.size(); i++) {
+                    pw.print("UID: ");
+                    UserHandle.formatUid(pw, mLastPriorityAlarmDispatch.keyAt(i));
+                    pw.print(": ");
+                    TimeUtils.formatDuration(mLastPriorityAlarmDispatch.valueAt(i), nowELAPSED, pw);
+                    pw.println();
+                }
+                pw.decreaseIndent();
+            }
+
             if (mLog.dump(pw, "Recent problems:")) {
                 pw.println();
             }
@@ -3303,6 +3355,11 @@
                 mPendingBackgroundAlarms.removeAt(i);
             }
         }
+        for (int i = mLastPriorityAlarmDispatch.size() - 1; i >= 0; i--) {
+            if (UserHandle.getUserId(mLastPriorityAlarmDispatch.keyAt(i)) == userHandle) {
+                mLastPriorityAlarmDispatch.removeAt(i);
+            }
+        }
         if (mNextWakeFromIdle != null && whichAlarms.test(mNextWakeFromIdle)) {
             mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
             if (mPendingIdleUntil != null) {
@@ -4103,6 +4160,7 @@
             IntentFilter sdFilter = new IntentFilter();
             sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
             sdFilter.addAction(Intent.ACTION_USER_STOPPED);
+            sdFilter.addAction(Intent.ACTION_UID_REMOVED);
             getContext().registerReceiver(this, sdFilter);
         }
 
@@ -4132,6 +4190,9 @@
                             mAllowWhileIdleHistory.removeForUser(userHandle);
                         }
                         return;
+                    case Intent.ACTION_UID_REMOVED:
+                        mLastPriorityAlarmDispatch.delete(uid);
+                        return;
                     case Intent.ACTION_PACKAGE_REMOVED:
                         if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                             // This package is being updated; don't kill its alarms.
@@ -4522,11 +4583,11 @@
             if (inflight.isBroadcast()) {
                 notifyBroadcastAlarmPendingLocked(alarm.uid);
             }
-            if (isAllowedWhileIdleRestricted(alarm)) {
-                final boolean doze = (mPendingIdleUntil != null);
-                final boolean batterySaver = (mAppStateTracker != null
-                        && mAppStateTracker.isForceAllAppsStandbyEnabled());
-                if (doze || batterySaver) {
+            final boolean doze = (mPendingIdleUntil != null);
+            final boolean batterySaver = (mAppStateTracker != null
+                    && mAppStateTracker.isForceAllAppsStandbyEnabled());
+            if (doze || batterySaver) {
+                if (isAllowedWhileIdleRestricted(alarm)) {
                     // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm while the
                     // device was in doze or battery saver.
                     mAllowWhileIdleHistory.recordAlarmForPackage(alarm.sourcePackage,
@@ -4538,6 +4599,16 @@
                         return (doze && adjustDeliveryTimeBasedOnDeviceIdle(a))
                                 || (batterySaver && adjustDeliveryTimeBasedOnBatterySaver(a));
                     });
+                } else if ((alarm.flags & FLAG_PRIORITIZE) != 0) {
+                    mLastPriorityAlarmDispatch.put(alarm.creatorUid, nowELAPSED);
+                    mAlarmStore.updateAlarmDeliveries(a -> {
+                        if (a.creatorUid != alarm.creatorUid
+                                || (alarm.flags & FLAG_PRIORITIZE) == 0) {
+                            return false;
+                        }
+                        return (doze && adjustDeliveryTimeBasedOnDeviceIdle(a))
+                                || (batterySaver && adjustDeliveryTimeBasedOnBatterySaver(a));
+                    });
                 }
                 if (RECORD_DEVICE_IDLE_ALARMS) {
                     IdleDispatchEntry ent = new IdleDispatchEntry();
diff --git a/core/api/current.txt b/core/api/current.txt
index feec087..935cf70 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1009,7 +1009,7 @@
     field public static final int multiArch = 16843918; // 0x101048e
     field public static final int multiprocess = 16842771; // 0x1010013
     field public static final int name = 16842755; // 0x1010003
-    field public static final int nativeHeapZeroInit = 16844325; // 0x1010625
+    field public static final int nativeHeapZeroInitialized = 16844325; // 0x1010625
     field public static final int navigationBarColor = 16843858; // 0x1010452
     field public static final int navigationBarDividerColor = 16844141; // 0x101056d
     field public static final int navigationContentDescription = 16843969; // 0x10104c1
@@ -7013,6 +7013,7 @@
     method public void onBugreportShared(@NonNull android.content.Context, @NonNull android.content.Intent, @NonNull String);
     method public void onBugreportSharingDeclined(@NonNull android.content.Context, @NonNull android.content.Intent);
     method @Nullable public String onChoosePrivateKeyAlias(@NonNull android.content.Context, @NonNull android.content.Intent, int, @Nullable android.net.Uri, @Nullable String);
+    method public void onComplianceAcknowledgementRequired(@NonNull android.content.Context, @NonNull android.content.Intent);
     method @Nullable public CharSequence onDisableRequested(@NonNull android.content.Context, @NonNull android.content.Intent);
     method public void onDisabled(@NonNull android.content.Context, @NonNull android.content.Intent);
     method public void onEnabled(@NonNull android.content.Context, @NonNull android.content.Intent);
@@ -7067,6 +7068,7 @@
   }
 
   public class DevicePolicyManager {
+    method public void acknowledgeDeviceCompliant();
     method public void addCrossProfileIntentFilter(@NonNull android.content.ComponentName, android.content.IntentFilter, int);
     method public boolean addCrossProfileWidgetProvider(@NonNull android.content.ComponentName, String);
     method public int addOverrideApn(@NonNull android.content.ComponentName, @NonNull android.telephony.data.ApnSetting);
@@ -7185,6 +7187,7 @@
     method public boolean isBackupServiceEnabled(@NonNull android.content.ComponentName);
     method @Deprecated public boolean isCallerApplicationRestrictionsManagingPackage();
     method public boolean isCommonCriteriaModeEnabled(@Nullable android.content.ComponentName);
+    method public boolean isComplianceAcknowledgementRequired();
     method public boolean isDeviceIdAttestationSupported();
     method public boolean isDeviceOwnerApp(String);
     method public boolean isEnterpriseNetworkPreferenceEnabled();
@@ -11864,7 +11867,7 @@
     method public static CharSequence getCategoryTitle(android.content.Context, int);
     method public int getGwpAsanMode();
     method public int getMemtagMode();
-    method @Nullable public Boolean isNativeHeapZeroInit();
+    method public int getNativeHeapZeroInitialized();
     method public boolean isProfileableByShell();
     method public boolean isResourceOverlay();
     method public boolean isVirtualPreload();
@@ -11919,6 +11922,9 @@
     field public static final int MEMTAG_DEFAULT = -1; // 0xffffffff
     field public static final int MEMTAG_OFF = 0; // 0x0
     field public static final int MEMTAG_SYNC = 2; // 0x2
+    field public static final int ZEROINIT_DEFAULT = -1; // 0xffffffff
+    field public static final int ZEROINIT_DISABLED = 0; // 0x0
+    field public static final int ZEROINIT_ENABLED = 1; // 0x1
     field public String appComponentFactory;
     field public String backupAgentName;
     field public int category;
@@ -13373,7 +13379,8 @@
     method public void setTo(android.content.res.Resources.Theme);
   }
 
-  public class TypedArray {
+  public class TypedArray implements java.lang.AutoCloseable {
+    method public void close();
     method public boolean getBoolean(@StyleableRes int, boolean);
     method public int getChangingConfigurations();
     method @ColorInt public int getColor(@StyleableRes int, @ColorInt int);
@@ -17859,6 +17866,7 @@
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.String> INFO_VERSION;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size[]> JPEG_AVAILABLE_THUMBNAIL_SIZES;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_DISTORTION;
+    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_DISTORTION_MAXIMUM_RESOLUTION;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LENS_FACING;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_INFO_AVAILABLE_APERTURES;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_INFO_AVAILABLE_FILTER_DENSITIES;
@@ -17868,6 +17876,7 @@
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> LENS_INFO_HYPERFOCAL_DISTANCE;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> LENS_INFO_MINIMUM_FOCUS_DISTANCE;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_INTRINSIC_CALIBRATION;
+    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_INTRINSIC_CALIBRATION_MAXIMUM_RESOLUTION;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LENS_POSE_REFERENCE;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_ROTATION;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_TRANSLATION;
@@ -17887,9 +17896,11 @@
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SCALER_CROPPING_TYPE;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SCALER_DEFAULT_SECURE_IMAGE_SIZE;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS;
+    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap> SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP;
+    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SENSOR_AVAILABLE_TEST_PATTERN_MODES;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.BlackLevelPattern> SENSOR_BLACK_LEVEL_PATTERN;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_CALIBRATION_TRANSFORM1;
@@ -17899,13 +17910,17 @@
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_FORWARD_MATRIX1;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_FORWARD_MATRIX2;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.graphics.Rect> SENSOR_INFO_ACTIVE_ARRAY_SIZE;
+    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.graphics.Rect> SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION;
+    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SENSOR_INFO_BINNING_FACTOR;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_INFO_COLOR_FILTER_ARRANGEMENT;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Long>> SENSOR_INFO_EXPOSURE_TIME_RANGE;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> SENSOR_INFO_LENS_SHADING_APPLIED;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Long> SENSOR_INFO_MAX_FRAME_DURATION;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.SizeF> SENSOR_INFO_PHYSICAL_SIZE;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SENSOR_INFO_PIXEL_ARRAY_SIZE;
+    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SENSOR_INFO_PIXEL_ARRAY_SIZE_MAXIMUM_RESOLUTION;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.graphics.Rect> SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE;
+    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.graphics.Rect> SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>> SENSOR_INFO_SENSITIVITY_RANGE;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_INFO_TIMESTAMP_SOURCE;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_INFO_WHITE_LEVEL;
@@ -18202,8 +18217,10 @@
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING = 4; // 0x4
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3; // 0x3
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS = 5; // 0x5
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING = 17; // 0x11
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA = 13; // 0xd
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA = 14; // 0xe
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR = 16; // 0x10
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7; // 0x7
     field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
     field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
@@ -18221,6 +18238,8 @@
     field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB = 0; // 0x0
     field public static final int SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME = 1; // 0x1
     field public static final int SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN = 0; // 0x0
+    field public static final int SENSOR_PIXEL_MODE_DEFAULT = 0; // 0x0
+    field public static final int SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION = 1; // 0x1
     field public static final int SENSOR_REFERENCE_ILLUMINANT1_CLOUDY_WEATHER = 10; // 0xa
     field public static final int SENSOR_REFERENCE_ILLUMINANT1_COOL_WHITE_FLUORESCENT = 14; // 0xe
     field public static final int SENSOR_REFERENCE_ILLUMINANT1_D50 = 23; // 0x17
@@ -18350,6 +18369,7 @@
     field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> SCALER_ROTATE_AND_CROP;
     field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Long> SENSOR_EXPOSURE_TIME;
     field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Long> SENSOR_FRAME_DURATION;
+    field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> SENSOR_PIXEL_MODE;
     field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> SENSOR_SENSITIVITY;
     field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<int[]> SENSOR_TEST_PATTERN_DATA;
     field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> SENSOR_TEST_PATTERN_MODE;
@@ -18453,6 +18473,8 @@
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> SENSOR_GREEN_SPLIT;
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.util.Rational[]> SENSOR_NEUTRAL_COLOR_POINT;
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.util.Pair<java.lang.Double,java.lang.Double>[]> SENSOR_NOISE_PROFILE;
+    field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> SENSOR_PIXEL_MODE;
+    field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> SENSOR_RAW_BINNING_FACTOR_USED;
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Long> SENSOR_ROLLING_SHUTTER_SKEW;
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> SENSOR_SENSITIVITY;
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<int[]> SENSOR_TEST_PATTERN_DATA;
@@ -18627,6 +18649,7 @@
     ctor public OutputConfiguration(@NonNull android.view.Surface);
     ctor public OutputConfiguration(int, @NonNull android.view.Surface);
     ctor public OutputConfiguration(@NonNull android.util.Size, @NonNull Class<T>);
+    method public void addSensorPixelModeUsed(int);
     method public void addSurface(@NonNull android.view.Surface);
     method @NonNull public static java.util.Collection<android.hardware.camera2.params.OutputConfiguration> createInstancesForMultiResolutionOutput(@NonNull android.hardware.camera2.MultiResolutionImageReader);
     method public int describeContents();
@@ -18635,6 +18658,7 @@
     method @Nullable public android.view.Surface getSurface();
     method public int getSurfaceGroupId();
     method @NonNull public java.util.List<android.view.Surface> getSurfaces();
+    method public void removeSensorPixelModeUsed(int);
     method public void removeSurface(@NonNull android.view.Surface);
     method public void setPhysicalCameraId(@Nullable String);
     method public void writeToParcel(android.os.Parcel, int);
@@ -19805,7 +19829,8 @@
     method public boolean hasSpeed();
     method public boolean hasSpeedAccuracy();
     method public boolean hasVerticalAccuracy();
-    method public boolean isFromMockProvider();
+    method @Deprecated public boolean isFromMockProvider();
+    method public boolean isMock();
     method @Deprecated public void removeAccuracy();
     method @Deprecated public void removeAltitude();
     method @Deprecated public void removeBearing();
@@ -19821,6 +19846,7 @@
     method public void setExtras(@Nullable android.os.Bundle);
     method public void setLatitude(double);
     method public void setLongitude(double);
+    method public void setMock(boolean);
     method public void setProvider(String);
     method public void setSpeed(float);
     method public void setSpeedAccuracyMetersPerSecond(float);
@@ -21345,7 +21371,9 @@
     method @NonNull public android.media.MediaFormat getOutputFormat(int);
     method @NonNull public android.media.MediaCodec.OutputFrame getOutputFrame(int);
     method @Nullable public android.media.Image getOutputImage(int);
+    method @Nullable public android.media.MediaCodec.ParameterDescriptor getParameterDescriptor(@NonNull String);
     method @NonNull public android.media.MediaCodec.QueueRequest getQueueRequest(int);
+    method @NonNull public java.util.List<java.lang.String> getSupportedVendorParameters();
     method @Nullable public static android.media.Image mapHardwareBuffer(@NonNull android.hardware.HardwareBuffer);
     method public void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException;
     method public void queueSecureInputBuffer(int, int, @NonNull android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException;
@@ -21365,6 +21393,8 @@
     method public void signalEndOfInputStream();
     method public void start();
     method public void stop();
+    method public void subscribeToVendorParameters(@NonNull java.util.List<java.lang.String>);
+    method public void unsubscribeFromVendorParameters(@NonNull java.util.List<java.lang.String>);
     field public static final int BUFFER_FLAG_CODEC_CONFIG = 2; // 0x2
     field public static final int BUFFER_FLAG_END_OF_STREAM = 4; // 0x4
     field public static final int BUFFER_FLAG_KEY_FRAME = 1; // 0x1
@@ -21492,6 +21522,11 @@
     method public long getPresentationTimeUs();
   }
 
+  public static class MediaCodec.ParameterDescriptor {
+    method @NonNull public String getName();
+    method public int getType();
+  }
+
   public final class MediaCodec.QueueRequest {
     method public void queue();
     method @NonNull public android.media.MediaCodec.QueueRequest setByteBufferParameter(@NonNull String, @NonNull java.nio.ByteBuffer);
@@ -21817,6 +21852,7 @@
     method public android.util.Range<java.lang.Integer> getQualityRange();
     method public boolean isBitrateModeSupported(int);
     field public static final int BITRATE_MODE_CBR = 2; // 0x2
+    field public static final int BITRATE_MODE_CBR_FD = 3; // 0x3
     field public static final int BITRATE_MODE_CQ = 0; // 0x0
     field public static final int BITRATE_MODE_VBR = 1; // 0x1
   }
@@ -34907,6 +34943,7 @@
     field public static final String ACTION_APPLICATION_SETTINGS = "android.settings.APPLICATION_SETTINGS";
     field public static final String ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS = "android.settings.APP_NOTIFICATION_BUBBLE_SETTINGS";
     field public static final String ACTION_APP_NOTIFICATION_SETTINGS = "android.settings.APP_NOTIFICATION_SETTINGS";
+    field public static final String ACTION_APP_OPEN_BY_DEFAULT_SETTINGS = "com.android.settings.APP_OPEN_BY_DEFAULT_SETTINGS";
     field public static final String ACTION_APP_SEARCH_SETTINGS = "android.settings.APP_SEARCH_SETTINGS";
     field public static final String ACTION_APP_USAGE_SETTINGS = "android.settings.action.APP_USAGE_SETTINGS";
     field public static final String ACTION_AUTO_ROTATE_SETTINGS = "android.settings.AUTO_ROTATE_SETTINGS";
@@ -37476,6 +37513,7 @@
     method public void onDisconnected();
     method public abstract void onFillRequest(@NonNull android.service.autofill.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.FillCallback);
     method public abstract void onSaveRequest(@NonNull android.service.autofill.SaveRequest, @NonNull android.service.autofill.SaveCallback);
+    method public void onSavedDatasetsInfoRequest(@NonNull android.service.autofill.SavedDatasetsInfoCallback);
     field public static final String SERVICE_INTERFACE = "android.service.autofill.AutofillService";
     field public static final String SERVICE_META_DATA = "android.autofill";
   }
@@ -37751,6 +37789,22 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
   }
 
+  public final class SavedDatasetsInfo {
+    ctor public SavedDatasetsInfo(@NonNull String, @IntRange(from=0) int);
+    method @IntRange(from=0) public int getCount();
+    method @NonNull public String getType();
+    field public static final String TYPE_OTHER = "other";
+    field public static final String TYPE_PASSWORDS = "passwords";
+  }
+
+  public interface SavedDatasetsInfoCallback {
+    method public void onError(int);
+    method public void onSuccess(@NonNull java.util.Set<android.service.autofill.SavedDatasetsInfo>);
+    field public static final int ERROR_NEEDS_USER_ACTION = 2; // 0x2
+    field public static final int ERROR_OTHER = 0; // 0x0
+    field public static final int ERROR_UNSUPPORTED = 1; // 0x1
+  }
+
   public final class TextValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
     ctor public TextValueSanitizer(@NonNull java.util.regex.Pattern, @NonNull String);
     method public int describeContents();
@@ -42316,7 +42370,7 @@
   }
 
   public static interface TelephonyCallback.DisplayInfoListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
+    method public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
   }
 
   public static interface TelephonyCallback.EmergencyNumberListListener {
@@ -48310,6 +48364,7 @@
     method protected int computeVerticalScrollRange();
     method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
     method public void createContextMenu(android.view.ContextMenu);
+    method @Nullable public android.view.translation.ViewTranslationRequest createTranslationRequest(@NonNull int[]);
     method @Deprecated public void destroyDrawingCache();
     method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
     method public boolean dispatchCapturedPointerEvent(android.view.MotionEvent);
@@ -48530,6 +48585,7 @@
     method @Nullable public android.graphics.drawable.Drawable getVerticalScrollbarThumbDrawable();
     method @Nullable public android.graphics.drawable.Drawable getVerticalScrollbarTrackDrawable();
     method public int getVerticalScrollbarWidth();
+    method @Nullable public android.view.translation.ViewTranslationCallback getViewTranslationCallback();
     method public android.view.ViewTreeObserver getViewTreeObserver();
     method public int getVisibility();
     method public final int getWidth();
@@ -48673,6 +48729,7 @@
     method public void onStartTemporaryDetach();
     method public boolean onTouchEvent(android.view.MotionEvent);
     method public boolean onTrackballEvent(android.view.MotionEvent);
+    method public void onTranslationResponse(@NonNull android.view.translation.ViewTranslationResponse);
     method @CallSuper public void onVisibilityAggregated(boolean);
     method protected void onVisibilityChanged(@NonNull android.view.View, int);
     method public void onWindowFocusChanged(boolean);
@@ -48881,6 +48938,7 @@
     method public void setVerticalScrollbarPosition(int);
     method public void setVerticalScrollbarThumbDrawable(@Nullable android.graphics.drawable.Drawable);
     method public void setVerticalScrollbarTrackDrawable(@Nullable android.graphics.drawable.Drawable);
+    method public void setViewTranslationCallback(@NonNull android.view.translation.ViewTranslationCallback);
     method public void setVisibility(int);
     method @Deprecated public void setWillNotCacheDrawing(boolean);
     method public void setWillNotDraw(boolean);
@@ -52703,6 +52761,12 @@
     method public void onStarted(@NonNull String, @NonNull String);
   }
 
+  @UiThread public interface ViewTranslationCallback {
+    method public boolean onClearTranslation(@NonNull android.view.View);
+    method public boolean onHideTranslation(@NonNull android.view.View);
+    method public boolean onShowTranslation(@NonNull android.view.View);
+  }
+
   public final class ViewTranslationRequest implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.view.autofill.AutofillId getAutofillId();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 6ba9478..e8bdd41 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3,6 +3,7 @@
 
   public static final class Manifest.permission {
     field public static final String ACCESS_AMBIENT_LIGHT_STATS = "android.permission.ACCESS_AMBIENT_LIGHT_STATS";
+    field public static final String ACCESS_BLOBS_ACROSS_USERS = "android.permission.ACCESS_BLOBS_ACROSS_USERS";
     field public static final String ACCESS_BROADCAST_RADIO = "android.permission.ACCESS_BROADCAST_RADIO";
     field public static final String ACCESS_CACHE_FILESYSTEM = "android.permission.ACCESS_CACHE_FILESYSTEM";
     field public static final String ACCESS_CONTEXT_HUB = "android.permission.ACCESS_CONTEXT_HUB";
@@ -186,6 +187,7 @@
     field public static final String OBSERVE_ROLE_HOLDERS = "android.permission.OBSERVE_ROLE_HOLDERS";
     field public static final String OBSERVE_SENSOR_PRIVACY = "android.permission.OBSERVE_SENSOR_PRIVACY";
     field public static final String OPEN_ACCESSIBILITY_DETAILS_SETTINGS = "android.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS";
+    field public static final String OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD = "android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD";
     field public static final String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG";
     field public static final String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT";
     field public static final String PACKET_KEEPALIVE_OFFLOAD = "android.permission.PACKET_KEEPALIVE_OFFLOAD";
@@ -243,6 +245,7 @@
     field public static final String REVIEW_ACCESSIBILITY_SERVICES = "android.permission.REVIEW_ACCESSIBILITY_SERVICES";
     field public static final String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
     field public static final String ROTATE_SURFACE_FLINGER = "android.permission.ROTATE_SURFACE_FLINGER";
+    field public static final String SCHEDULE_PRIORITIZED_ALARM = "android.permission.SCHEDULE_PRIORITIZED_ALARM";
     field public static final String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
     field public static final String SECURE_ELEMENT_PRIVILEGED_OPERATION = "android.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION";
     field public static final String SEND_CATEGORY_CAR_NOTIFICATIONS = "android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS";
@@ -422,6 +425,7 @@
   public class AlarmManager {
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void set(int, long, long, long, android.app.PendingIntent, android.os.WorkSource);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void set(int, long, long, long, android.app.AlarmManager.OnAlarmListener, android.os.Handler, android.os.WorkSource);
+    method @RequiresPermission(android.Manifest.permission.SCHEDULE_PRIORITIZED_ALARM) public void setPrioritized(int, long, long, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.app.AlarmManager.OnAlarmListener);
   }
 
   public class AppOpsManager {
@@ -951,6 +955,9 @@
     field @Deprecated public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3; // 0x3
     field public static final int PROVISIONING_TRIGGER_QR_CODE = 2; // 0x2
     field public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0; // 0x0
+    field public static final String REQUIRED_APP_MANAGED_DEVICE = "android.app.REQUIRED_APP_MANAGED_DEVICE";
+    field public static final String REQUIRED_APP_MANAGED_PROFILE = "android.app.REQUIRED_APP_MANAGED_PROFILE";
+    field public static final String REQUIRED_APP_MANAGED_USER = "android.app.REQUIRED_APP_MANAGED_USER";
     field public static final int STATE_USER_PROFILE_COMPLETE = 4; // 0x4
     field public static final int STATE_USER_PROFILE_FINALIZED = 5; // 0x5
     field public static final int STATE_USER_SETUP_COMPLETE = 2; // 0x2
@@ -1223,6 +1230,21 @@
     method public static boolean isChangeEnabled(long);
     method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, @NonNull String, @NonNull android.os.UserHandle);
     method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, int);
+    method @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) public static void setPackageOverride(@NonNull String, @NonNull java.util.Map<java.lang.Long,android.app.compat.PackageOverride>);
+  }
+
+  public final class PackageOverride {
+    method public long getMaxVersionCode();
+    method public long getMinVersionCode();
+    method public boolean isEnabled();
+  }
+
+  public static final class PackageOverride.Builder {
+    ctor public PackageOverride.Builder();
+    method @NonNull public android.app.compat.PackageOverride build();
+    method @NonNull public android.app.compat.PackageOverride.Builder setEnabled(boolean);
+    method @NonNull public android.app.compat.PackageOverride.Builder setMaxVersionCode(long);
+    method @NonNull public android.app.compat.PackageOverride.Builder setMinVersionCode(long);
   }
 
 }
@@ -1953,7 +1975,6 @@
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(android.bluetooth.BluetoothDevice);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPriority(android.bluetooth.BluetoothDevice, int);
   }
 
   public final class BluetoothHearingAid implements android.bluetooth.BluetoothProfile {
@@ -2180,6 +2201,7 @@
 package android.companion {
 
   public final class CompanionDeviceManager {
+    method @RequiresPermission("android.permission.ASSOCIATE_COMPANION_DEVICES") public boolean associate(@NonNull String, @NonNull android.net.MacAddress);
     method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean canPairWithoutPrompt(@NonNull String, @NonNull String, int);
     method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean isDeviceAssociatedForWifiConnection(@NonNull String, @NonNull android.net.MacAddress, @NonNull android.os.UserHandle);
   }
@@ -2807,7 +2829,9 @@
     method @NonNull public android.content.pm.SuspendDialogInfo.Builder setMessage(@StringRes int);
     method @NonNull public android.content.pm.SuspendDialogInfo.Builder setNeutralButtonAction(int);
     method @NonNull public android.content.pm.SuspendDialogInfo.Builder setNeutralButtonText(@StringRes int);
+    method @NonNull public android.content.pm.SuspendDialogInfo.Builder setNeutralButtonText(@NonNull String);
     method @NonNull public android.content.pm.SuspendDialogInfo.Builder setTitle(@StringRes int);
+    method @NonNull public android.content.pm.SuspendDialogInfo.Builder setTitle(@NonNull String);
   }
 
 }
@@ -2873,7 +2897,7 @@
   }
 
   public final class DomainVerificationManager {
-    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) public android.content.pm.verify.domain.DomainVerificationInfo getDomainVerificationInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @Nullable @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public android.content.pm.verify.domain.DomainVerificationInfo getDomainVerificationInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public java.util.List<android.content.pm.verify.domain.DomainOwner> getOwnersForDomain(@NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public java.util.List<java.lang.String> queryValidVerificationPackageNames();
     method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationLinkHandlingAllowed(@NonNull String, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -4827,7 +4851,7 @@
   public class Location implements android.os.Parcelable {
     method public boolean isComplete();
     method public void makeComplete();
-    method public void setIsFromMockProvider(boolean);
+    method @Deprecated public void setIsFromMockProvider(boolean);
     field @Deprecated public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
   }
 
@@ -5235,7 +5259,6 @@
     method @Nullable public String getClientPackageName();
     method @Nullable public android.media.MediaRouter2.RoutingController getController(@NonNull String);
     method @Nullable public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context, @NonNull String);
-    method public void registerRouteCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.RouteCallback);
     method public void setRouteVolume(@NonNull android.media.MediaRoute2Info, int);
     method public void startScan();
     method public void stopScan();
@@ -5259,6 +5282,10 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void ensureDefaultRingtones(@NonNull android.content.Context);
   }
 
+  public final class RouteDiscoveryPreference implements android.os.Parcelable {
+    field public static final android.media.RouteDiscoveryPreference EMPTY;
+  }
+
 }
 
 package android.media.audiofx {
@@ -7577,9 +7604,11 @@
     method public void notifyAlertReached();
     method public void notifyLimitReached();
     method public void notifyStatsUpdated(int, @NonNull android.net.NetworkStats, @NonNull android.net.NetworkStats);
+    method public void notifyWarningReached();
     method public abstract void onRequestStatsUpdate(int);
     method public abstract void onSetAlert(long);
     method public abstract void onSetLimit(@NonNull String, long);
+    method public void onSetWarningAndLimit(@NonNull String, long, long);
     field public static final int QUOTA_UNLIMITED = -1; // 0xffffffff
   }
 
@@ -8961,6 +8990,7 @@
     field public static final String NAMESPACE_GAME_DRIVER = "game_driver";
     field public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
     field public static final String NAMESPACE_INTELLIGENCE_ATTENTION = "intelligence_attention";
+    field public static final String NAMESPACE_MEDIA = "media";
     field public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
     field public static final String NAMESPACE_NETD_NATIVE = "netd_native";
     field public static final String NAMESPACE_OTA = "ota";
@@ -10350,6 +10380,7 @@
     method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int setParameter(int, int);
     method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int);
     method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean stopRecognition();
+    method public final void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory);
     field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1
     field public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2
     field public static final int MODEL_PARAM_THRESHOLD_FACTOR = 0; // 0x0
@@ -10378,7 +10409,9 @@
   }
 
   public static class AlwaysOnHotwordDetector.EventPayload {
+    method @Nullable public android.os.ParcelFileDescriptor getAudioStream();
     method @Nullable public android.media.AudioFormat getCaptureAudioFormat();
+    method @Nullable public android.service.voice.HotwordDetectedResult getHotwordDetectedResult();
     method @Nullable public byte[] getTriggerAudio();
   }
 
@@ -12303,6 +12336,20 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.EpsBearerQosSessionAttributes> CREATOR;
   }
 
+  public final class NrQosSessionAttributes implements android.os.Parcelable android.net.QosSessionAttributes {
+    method public int describeContents();
+    method public int get5Qi();
+    method public long getAveragingWindow();
+    method public long getGuaranteedDownlinkBitRate();
+    method public long getGuaranteedUplinkBitRate();
+    method public long getMaxDownlinkBitRate();
+    method public long getMaxUplinkBitRate();
+    method public int getQfi();
+    method @NonNull public java.util.List<java.net.InetSocketAddress> getRemoteAddresses();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.NrQosSessionAttributes> CREATOR;
+  }
+
   public abstract class QualifiedNetworksService extends android.app.Service {
     ctor public QualifiedNetworksService();
     method @NonNull public abstract android.telephony.data.QualifiedNetworksService.NetworkAvailabilityProvider onCreateNetworkAvailabilityProvider(int);
@@ -14050,7 +14097,7 @@
 package android.uwb {
 
   public final class AngleMeasurement implements android.os.Parcelable {
-    ctor public AngleMeasurement(double, double, double);
+    ctor public AngleMeasurement(@FloatRange(from=-3.141592653589793, to=3.141592653589793) double, @FloatRange(from=0.0, to=3.141592653589793) double, @FloatRange(from=0.0, to=1.0) double);
     method public int describeContents();
     method @FloatRange(from=0.0, to=1.0) public double getConfidenceLevel();
     method @FloatRange(from=0.0, to=3.141592653589793) public double getErrorRadians();
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index 3926e39..b50b8dd 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -48,6 +48,14 @@
 
 }
 
+package android.bluetooth {
+
+  public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
+    method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPriority(android.bluetooth.BluetoothDevice, int);
+  }
+
+}
+
 package android.content {
 
   public class Intent implements java.lang.Cloneable android.os.Parcelable {
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
index 47de040..5964f71 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -135,4 +135,20 @@
             throw e.rethrowFromSystemServer();
         }
     }
+    /**
+     * Returns a list of supported game modes for a given package.
+     * <p>
+     * The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+    public @GameMode int[] getAvailableGameModes(@NonNull String packageName) {
+        try {
+            return mService.getAvailableGameModes(packageName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
 }
diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl
index c8e1478..4bf8a3f 100644
--- a/core/java/android/app/IGameManagerService.aidl
+++ b/core/java/android/app/IGameManagerService.aidl
@@ -22,4 +22,5 @@
 interface IGameManagerService {
     int getGameMode(String packageName, int userId);
     void setGameMode(String packageName, int gameMode, int userId);
+    int[] getAvailableGameModes(String packageName);
 }
\ No newline at end of file
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 747a2de..da64dcd 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -524,6 +524,16 @@
             "android.app.action.OPERATION_SAFETY_STATE_CHANGED";
 
     /**
+     * Broadcast action: notify the profile owner on an organization-owned device that it needs to
+     * acknowledge device compliance.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED =
+            "android.app.action.COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED";
+
+    /**
      * An {@code int} extra specifying an {@link OperationSafetyReason}.
      *
      * @hide
@@ -1116,6 +1126,29 @@
         onOperationSafetyStateChanged(context, reason, isSafe);
     }
 
+    /**
+     * Called to notify a profile owner of an organization-owned device that it needs to acknowledge
+     * device compliance to allow the user to turn the profile off if needed according to the
+     * maximum profile time off policy.
+     *
+     * Default implementation acknowledges compliance immediately. DPC may prefer to override this
+     * implementation to delay acknowledgement until a successful policy sync. Until compliance is
+     * acknowledged the user is still free to turn the profile off, but the timer won't be reset,
+     * so personal apps will be suspended sooner. This callback is delivered using a foreground
+     * broadcast and should be handled quickly.
+     *
+     * @param context the running context as per {@link #onReceive}
+     * @param intent The received intent as per {@link #onReceive}.
+     *
+     * @see DevicePolicyManager#acknowledgeDeviceCompliant()
+     * @see DevicePolicyManager#isComplianceAcknowledgementRequired()
+     * @see DevicePolicyManager#setManagedProfileMaximumTimeOff(ComponentName, long)
+     */
+    public void onComplianceAcknowledgementRequired(
+            @NonNull Context context, @NonNull Intent intent) {
+        getManager(context).acknowledgeDeviceCompliant();
+    }
+
     private boolean hasRequiredExtra(Intent intent, String extra) {
         if (intent.hasExtra(extra)) return true;
 
@@ -1204,6 +1237,8 @@
                     intent.getParcelableExtra(Intent.EXTRA_USER));
         } else if (ACTION_OPERATION_SAFETY_STATE_CHANGED.equals(action)) {
             onOperationSafetyStateChanged(context, intent);
+        } else if (ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED.equals(action)) {
+            onComplianceAcknowledgementRequired(context, intent);
         }
     }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index d3534f9..26e6741 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3306,6 +3306,41 @@
             "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED";
 
     /**
+     * A {@code boolean} metadata to be included in a mainline module's {@code <application>}
+     * manifest element, which declares that the module should be considered a required app for
+     * managed users.
+     * <p>Being declared as a required app prevents removal of this package during the
+     * provisioning process.
+     * @hide
+     */
+    @SystemApi
+    public static final String REQUIRED_APP_MANAGED_USER = "android.app.REQUIRED_APP_MANAGED_USER";
+
+    /**
+     * A {@code boolean} metadata to be included in a mainline module's {@code <application>}
+     * manifest element, which declares that the module should be considered a required app for
+     * managed devices.
+     * <p>Being declared as a required app prevents removal of this package during the
+     * provisioning process.
+     * @hide
+     */
+    @SystemApi
+    public static final String REQUIRED_APP_MANAGED_DEVICE =
+            "android.app.REQUIRED_APP_MANAGED_DEVICE";
+
+    /**
+     * A {@code boolean} metadata to be included in a mainline module's {@code <application>}
+     * manifest element, which declares that the module should be considered a required app for
+     * managed profiles.
+     * <p>Being declared as a required app prevents removal of this package during the
+     * provisioning process.
+     * @hide
+     */
+    @SystemApi
+    public static final String REQUIRED_APP_MANAGED_PROFILE =
+            "android.app.REQUIRED_APP_MANAGED_PROFILE";
+
+    /**
      * Called by an application that is administering the device to set the password restrictions it
      * is imposing. After setting this, the user will not be able to enter a new password that is
      * not at least as restrictive as what has been set. Note that the current password will remain
@@ -13374,6 +13409,63 @@
     }
 
     /**
+     * Called by a profile owner of an organization-owned managed profile to acknowledge that the
+     * device is compliant and the user can turn the profile off if needed according to the maximum
+     * time off policy.
+     *
+     * This method should be called when the device is deemed compliant after getting
+     * {@link DeviceAdminReceiver#onComplianceAcknowledgementRequired(Context, Intent)} callback in
+     * case it is overridden. Before this method is called the user is still free to turn the
+     * profile off, but the timer won't be reset, so personal apps will be suspended sooner.
+     *
+     * DPCs only need acknowledging device compliance if they override
+     * {@link DeviceAdminReceiver#onComplianceAcknowledgementRequired(Context, Intent)}, otherwise
+     * compliance is acknowledged automatically.
+     *
+     * @throws IllegalStateException if the user isn't unlocked
+     * @see #isComplianceAcknowledgementRequired()
+     * @see #setManagedProfileMaximumTimeOff(ComponentName, long)
+     * @see DeviceAdminReceiver#onComplianceAcknowledgementRequired(Context, Intent)
+     */
+    public void acknowledgeDeviceCompliant() {
+        throwIfParentInstance("acknowledgeDeviceCompliant");
+        if (mService != null) {
+            try {
+                mService.acknowledgeDeviceCompliant();
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Called by a profile owner of an organization-owned managed profile to query whether it needs
+     * to acknowledge device compliance to allow the user to turn the profile off if needed
+     * according to the maximum profile time off policy.
+     *
+     * Normally when acknowledgement is needed the DPC gets a
+     * {@link DeviceAdminReceiver#onComplianceAcknowledgementRequired(Context, Intent)} callback.
+     * But if the callback was not delivered or handled for some reason, this method can be used to
+     * verify if acknowledgement is needed.
+     *
+     * @throws IllegalStateException if the user isn't unlocked
+     * @see #acknowledgeDeviceCompliant()
+     * @see #setManagedProfileMaximumTimeOff(ComponentName, long)
+     * @see DeviceAdminReceiver#onComplianceAcknowledgementRequired(Context, Intent)
+     */
+    public boolean isComplianceAcknowledgementRequired() {
+        throwIfParentInstance("isComplianceAcknowledgementRequired");
+        if (mService != null) {
+            try {
+                return mService.isComplianceAcknowledgementRequired();
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
+
+    /**
      * Returns {@code true} when {@code userId} has a profile owner that is capable of resetting
      * password in RUNNING_LOCKED state. For that it should have at least one direct boot aware
      * component and have an active password reset token. Can only be called by the system.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 0ad92b7..5e49a98 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -495,6 +495,10 @@
 
     long getManagedProfileMaximumTimeOff(in ComponentName admin);
     void setManagedProfileMaximumTimeOff(in ComponentName admin, long timeoutMs);
+
+    void acknowledgeDeviceCompliant();
+    boolean isComplianceAcknowledgementRequired();
+
     boolean canProfileOwnerResetPasswordWhenLocked(int userId);
 
     void setNextOperationSafety(int operation, int reason);
diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java
index ab38832..74e1ece 100644
--- a/core/java/android/app/compat/CompatChanges.java
+++ b/core/java/android/app/compat/CompatChanges.java
@@ -104,16 +104,15 @@
      *
      * @param packageName The package name of the app in question.
      * @param overrides A map from changeId to the override applied for this change id.
-     * @hide
      */
-    @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG)
-    public static void setPackageOverride(String packageName,
-            Map<Long, PackageOverride> overrides) {
+    @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
+    public static void setPackageOverride(@NonNull String packageName,
+            @NonNull Map<Long, PackageOverride> overrides) {
         IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
                 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
         CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(overrides);
         try {
-            platformCompat.setOverridesFromInstaller(config, packageName);
+            platformCompat.setOverridesOnReleaseBuilds(config, packageName);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/compat/PackageOverride.java b/core/java/android/app/compat/PackageOverride.java
index 9f97cd4..59b3555 100644
--- a/core/java/android/app/compat/PackageOverride.java
+++ b/core/java/android/app/compat/PackageOverride.java
@@ -17,8 +17,9 @@
 package android.app.compat;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
-import android.os.Parcelable;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -32,15 +33,16 @@
  *
  * @hide
  */
-public class PackageOverride implements Parcelable {
+@SystemApi
+public final class PackageOverride {
 
+    /** @hide */
     @IntDef({
             VALUE_UNDEFINED,
             VALUE_ENABLED,
             VALUE_DISABLED
     })
     @Retention(RetentionPolicy.SOURCE)
-    /** @hide */
     public @interface EvaluatedOverride {
     }
 
@@ -75,10 +77,6 @@
         this.mEnabled = enabled;
     }
 
-    private PackageOverride(Parcel in) {
-        this(in.readLong(), in.readLong(), in.readBoolean());
-    }
-
     /**
      * Evaluate the override for the given {@code versionCode}. If no override is defined for
      * the specified version code, {@link #VALUE_UNDEFINED} is returned.
@@ -114,25 +112,23 @@
     }
 
     /** Returns the enabled value for the override. */
-    public boolean getEnabled() {
+    public boolean isEnabled() {
         return mEnabled;
     }
 
     /** @hide */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** @hide */
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(Parcel dest) {
         dest.writeLong(mMinVersionCode);
         dest.writeLong(mMaxVersionCode);
         dest.writeBoolean(mEnabled);
     }
 
     /** @hide */
+    public static PackageOverride createFromParcel(Parcel in) {
+        return new PackageOverride(in.readLong(), in.readLong(), in.readBoolean());
+    }
+
+    /** @hide */
     @Override
     public String toString() {
         if (mMinVersionCode == Long.MIN_VALUE && mMaxVersionCode == Long.MAX_VALUE) {
@@ -141,25 +137,10 @@
         return String.format("[%d,%d,%b]", mMinVersionCode, mMaxVersionCode, mEnabled);
     }
 
-    /** @hide */
-    public static final Creator<PackageOverride> CREATOR =
-            new Creator<PackageOverride>() {
-
-                @Override
-                public PackageOverride createFromParcel(Parcel in) {
-                    return new PackageOverride(in);
-                }
-
-                @Override
-                public PackageOverride[] newArray(int size) {
-                    return new PackageOverride[size];
-                }
-            };
-
     /**
      * Builder to construct a PackageOverride.
      */
-    public static class Builder {
+    public static final class Builder {
         private long mMinVersionCode = Long.MIN_VALUE;
         private long mMaxVersionCode = Long.MAX_VALUE;
         private boolean mEnabled;
@@ -169,6 +150,7 @@
          *
          * default value: {@code Long.MIN_VALUE}.
          */
+        @NonNull
         public Builder setMinVersionCode(long minVersionCode) {
             mMinVersionCode = minVersionCode;
             return this;
@@ -179,6 +161,7 @@
          *
          * default value: {@code Long.MAX_VALUE}.
          */
+        @NonNull
         public Builder setMaxVersionCode(long maxVersionCode) {
             mMaxVersionCode = maxVersionCode;
             return this;
@@ -189,6 +172,7 @@
          *
          * default value: {@code false}.
          */
+        @NonNull
         public Builder setEnabled(boolean enabled) {
             mEnabled = enabled;
             return this;
@@ -200,6 +184,7 @@
          * @throws IllegalArgumentException if {@code minVersionCode} is larger than
          *                                  {@code maxVersionCode}.
          */
+        @NonNull
         public PackageOverride build() {
             if (mMinVersionCode > mMaxVersionCode) {
                 throw new IllegalArgumentException("minVersionCode must not be larger than "
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 4fb5577..632572d 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -567,6 +567,7 @@
      * @return true if priority is set, false on error
      * @hide
      * @deprecated Replaced with {@link #setConnectionPolicy(BluetoothDevice, int)}
+     * @removed
      */
     @Deprecated
     @SystemApi
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 86bd8a2..2ce7156 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -425,6 +425,32 @@
                     mContext.getPackageName(), deviceAddress);
         } catch (RemoteException e) {
             ExceptionUtils.propagateIfInstanceOf(e.getCause(), DeviceNotAssociatedException.class);
+        }
+    }
+
+    /**
+     * Associates given device with given app for the given user directly, without UI prompt.
+     *
+     * @return whether successful
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES)
+    public boolean associate(
+            @NonNull String packageName,
+            @NonNull MacAddress macAddress) {
+        if (!checkFeaturePresent()) {
+            return false;
+        }
+        Objects.requireNonNull(packageName, "package name cannot be null");
+        Objects.requireNonNull(macAddress, "mac address cannot be null");
+
+        UserHandle user = android.os.Process.myUserHandle();
+        try {
+            return mService.createAssociation(
+                    packageName, macAddress.toString(), user.getIdentifier());
+        } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index 95d3515..83db358 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -51,4 +51,6 @@
     void unregisterDevicePresenceListenerService(in String packageName, in String deviceAddress);
 
     boolean canPairWithoutPrompt(in String packageName, in String deviceMacAddress, int userId);
+
+    boolean createAssociation(in String packageName, in String macAddress, int userId);
 }
diff --git a/core/java/android/content/pm/AppSearchPerson.java b/core/java/android/content/pm/AppSearchPerson.java
index 66295eb..9283e5f 100644
--- a/core/java/android/content/pm/AppSearchPerson.java
+++ b/core/java/android/content/pm/AppSearchPerson.java
@@ -107,7 +107,7 @@
     public static class Builder extends GenericDocument.Builder<Builder> {
 
         public Builder(@NonNull final String id) {
-            super(id, SCHEMA_TYPE);
+            super(/*namespace=*/ "", id, SCHEMA_TYPE);
         }
 
         /** @hide */
diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java
index c04d3be..b2478ca 100644
--- a/core/java/android/content/pm/AppSearchShortcutInfo.java
+++ b/core/java/android/content/pm/AppSearchShortcutInfo.java
@@ -217,9 +217,8 @@
     @NonNull
     public static AppSearchShortcutInfo instance(@NonNull final ShortcutInfo shortcutInfo) {
         Objects.requireNonNull(shortcutInfo);
-        return new Builder(shortcutInfo.getId())
+        return new Builder(shortcutInfo.getPackage(), shortcutInfo.getId())
                 .setActivity(shortcutInfo.getActivity())
-                .setNamespace(shortcutInfo.getPackage())
                 .setShortLabel(shortcutInfo.getShortLabel())
                 .setShortLabelResId(shortcutInfo.getShortLabelResourceId())
                 .setShortLabelResName(shortcutInfo.getTitleResName())
@@ -345,8 +344,8 @@
     @VisibleForTesting
     public static class Builder extends GenericDocument.Builder<Builder> {
 
-        public Builder(String id) {
-            super(id, SCHEMA_TYPE);
+        public Builder(String packageName, String id) {
+            super(/*namespace=*/ packageName, id, SCHEMA_TYPE);
         }
 
         /**
@@ -574,16 +573,6 @@
         /**
          * @hide
          */
-        public Builder setPackageName(@Nullable final String packageName) {
-            if (!TextUtils.isEmpty(packageName)) {
-                setNamespace(packageName);
-            }
-            return this;
-        }
-
-        /**
-         * @hide
-         */
         public Builder setFlags(@ShortcutInfo.ShortcutFlags final int flags) {
             setPropertyLong(KEY_FLAGS, flattenFlags(flags));
             return this;
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 0da453d..5f3ec36 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1363,7 +1363,7 @@
      * Indicates if the application has requested GWP-ASan to be enabled, disabled, or left
      * unspecified. Processes can override this setting.
      */
-    private @GwpAsanMode int gwpAsanMode;
+    private @GwpAsanMode int gwpAsanMode = GWP_ASAN_DEFAULT;
 
     /**
      * Default (unspecified) setting of Memtag.
@@ -1402,13 +1402,38 @@
      * Indicates if the application has requested Memtag to be enabled, disabled, or left
      * unspecified. Processes can override this setting.
      */
-    private @MemtagMode int memtagMode;
+    private @MemtagMode int memtagMode = MEMTAG_DEFAULT;
+
+    /**
+     * Default (unspecified) setting of nativeHeapZeroInitialized.
+     */
+    public static final int ZEROINIT_DEFAULT = -1;
+
+    /**
+     * Disable zero-initialization of the native heap in this application or process.
+     */
+    public static final int ZEROINIT_DISABLED = 0;
+
+    /**
+     * Enable zero-initialization of the native heap in this application or process.
+     */
+    public static final int ZEROINIT_ENABLED = 1;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = {"ZEROINIT_"}, value = {
+            ZEROINIT_DEFAULT,
+            ZEROINIT_DISABLED,
+            ZEROINIT_ENABLED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NativeHeapZeroInitialized {}
 
     /**
      * Enable automatic zero-initialization of native heap memory allocations.
      */
-    @Nullable
-    private Boolean nativeHeapZeroInit;
+    private @NativeHeapZeroInitialized int nativeHeapZeroInitialized = ZEROINIT_DEFAULT;
 
     /**
      * If {@code true} this app requests optimized external storage access.
@@ -1570,8 +1595,8 @@
             if (memtagMode != MEMTAG_DEFAULT) {
                 pw.println(prefix + "memtagMode=" + memtagMode);
             }
-            if (nativeHeapZeroInit != null) {
-                pw.println(prefix + "nativeHeapZeroInit=" + nativeHeapZeroInit);
+            if (nativeHeapZeroInitialized != ZEROINIT_DEFAULT) {
+                pw.println(prefix + "nativeHeapZeroInitialized=" + nativeHeapZeroInitialized);
             }
             if (requestOptimizedExternalStorageAccess != null) {
                 pw.println(prefix + "requestOptimizedExternalStorageAccess="
@@ -1686,8 +1711,9 @@
             if (memtagMode != MEMTAG_DEFAULT) {
                 proto.write(ApplicationInfoProto.Detail.ENABLE_MEMTAG, memtagMode);
             }
-            if (nativeHeapZeroInit != null) {
-                proto.write(ApplicationInfoProto.Detail.NATIVE_HEAP_ZERO_INIT, nativeHeapZeroInit);
+            if (nativeHeapZeroInitialized != ZEROINIT_DEFAULT) {
+                proto.write(ApplicationInfoProto.Detail.NATIVE_HEAP_ZERO_INIT,
+                        nativeHeapZeroInitialized);
             }
             proto.end(detailToken);
         }
@@ -1802,7 +1828,7 @@
         zygotePreloadName = orig.zygotePreloadName;
         gwpAsanMode = orig.gwpAsanMode;
         memtagMode = orig.memtagMode;
-        nativeHeapZeroInit = orig.nativeHeapZeroInit;
+        nativeHeapZeroInitialized = orig.nativeHeapZeroInitialized;
         requestOptimizedExternalStorageAccess = orig.requestOptimizedExternalStorageAccess;
     }
 
@@ -1891,7 +1917,7 @@
         dest.writeString8(zygotePreloadName);
         dest.writeInt(gwpAsanMode);
         dest.writeInt(memtagMode);
-        sForBoolean.parcel(nativeHeapZeroInit, dest, parcelableFlags);
+        dest.writeInt(nativeHeapZeroInitialized);
         sForBoolean.parcel(requestOptimizedExternalStorageAccess, dest, parcelableFlags);
     }
 
@@ -1977,7 +2003,7 @@
         zygotePreloadName = source.readString8();
         gwpAsanMode = source.readInt();
         memtagMode = source.readInt();
-        nativeHeapZeroInit = sForBoolean.unparcel(source);
+        nativeHeapZeroInitialized = source.readInt();
         requestOptimizedExternalStorageAccess = sForBoolean.unparcel(source);
     }
 
@@ -2382,7 +2408,9 @@
     /** {@hide} */ public void setSplitResourcePaths(String[] splitResourcePaths) { splitPublicSourceDirs = splitResourcePaths; }
     /** {@hide} */ public void setGwpAsanMode(@GwpAsanMode int value) { gwpAsanMode = value; }
     /** {@hide} */ public void setMemtagMode(@MemtagMode int value) { memtagMode = value; }
-    /** {@hide} */ public void setNativeHeapZeroInit(@Nullable Boolean value) { nativeHeapZeroInit = value; }
+    /** {@hide} */ public void setNativeHeapZeroInitialized(@NativeHeapZeroInitialized int value) {
+        nativeHeapZeroInitialized = value;
+    }
     /** {@hide} */
     public void setRequestOptimizedExternalStorageAccess(@Nullable Boolean value) {
         requestOptimizedExternalStorageAccess = value;
@@ -2400,8 +2428,22 @@
     /** {@hide} */ public String[] getSplitResourcePaths() { return splitPublicSourceDirs; }
     @GwpAsanMode
     public int getGwpAsanMode() { return gwpAsanMode; }
+
+    /**
+     * Returns whether the application has requested Memtag to be enabled, disabled, or left
+     * unspecified. Processes can override this setting.
+     */
     @MemtagMode
-    public int getMemtagMode() { return memtagMode; }
-    @Nullable
-    public Boolean isNativeHeapZeroInit() { return nativeHeapZeroInit; }
+    public int getMemtagMode() {
+        return memtagMode;
+    }
+
+    /**
+     * Returns whether the application has requested automatic zero-initialization of native heap
+     * memory allocations to be enabled or disabled.
+     */
+    @NativeHeapZeroInitialized
+    public int getNativeHeapZeroInitialized() {
+        return nativeHeapZeroInitialized;
+    }
 }
diff --git a/core/java/android/content/pm/ProcessInfo.java b/core/java/android/content/pm/ProcessInfo.java
index 3dd5ee1..632c0f5 100644
--- a/core/java/android/content/pm/ProcessInfo.java
+++ b/core/java/android/content/pm/ProcessInfo.java
@@ -62,8 +62,7 @@
     /**
      * Enable automatic zero-initialization of native heap memory allocations.
      */
-    @Nullable
-    public Boolean nativeHeapZeroInit;
+    public @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized;
 
     @Deprecated
     public ProcessInfo(@NonNull ProcessInfo orig) {
@@ -71,7 +70,7 @@
         this.deniedPermissions = orig.deniedPermissions;
         this.gwpAsanMode = orig.gwpAsanMode;
         this.memtagMode = orig.memtagMode;
-        this.nativeHeapZeroInit = orig.nativeHeapZeroInit;
+        this.nativeHeapZeroInitialized = orig.nativeHeapZeroInitialized;
     }
 
 
@@ -101,7 +100,7 @@
      * @param memtagMode
      *   Indicates if the process has requested Memtag to be enabled (in sync or async mode),
      *   disabled, or left unspecified.
-     * @param nativeHeapZeroInit
+     * @param nativeHeapZeroInitialized
      *   Enable automatic zero-initialization of native heap memory allocations.
      */
     @DataClass.Generated.Member
@@ -110,7 +109,7 @@
             @Nullable ArraySet<String> deniedPermissions,
             @ApplicationInfo.GwpAsanMode int gwpAsanMode,
             @ApplicationInfo.MemtagMode int memtagMode,
-            @Nullable Boolean nativeHeapZeroInit) {
+            @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized) {
         this.name = name;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, name);
@@ -121,7 +120,9 @@
         this.memtagMode = memtagMode;
         com.android.internal.util.AnnotationValidations.validate(
                 ApplicationInfo.MemtagMode.class, null, memtagMode);
-        this.nativeHeapZeroInit = nativeHeapZeroInit;
+        this.nativeHeapZeroInitialized = nativeHeapZeroInitialized;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -145,13 +146,12 @@
 
         byte flg = 0;
         if (deniedPermissions != null) flg |= 0x2;
-        if (nativeHeapZeroInit != null) flg |= 0x10;
         dest.writeByte(flg);
         dest.writeString(name);
         sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
         dest.writeInt(gwpAsanMode);
         dest.writeInt(memtagMode);
-        if (nativeHeapZeroInit != null) dest.writeBoolean(nativeHeapZeroInit);
+        dest.writeInt(nativeHeapZeroInitialized);
     }
 
     @Override
@@ -170,7 +170,7 @@
         ArraySet<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
         int _gwpAsanMode = in.readInt();
         int _memtagMode = in.readInt();
-        Boolean _nativeHeapZeroInit = (flg & 0x10) == 0 ? null : (Boolean) in.readBoolean();
+        int _nativeHeapZeroInitialized = in.readInt();
 
         this.name = _name;
         com.android.internal.util.AnnotationValidations.validate(
@@ -182,7 +182,9 @@
         this.memtagMode = _memtagMode;
         com.android.internal.util.AnnotationValidations.validate(
                 ApplicationInfo.MemtagMode.class, null, memtagMode);
-        this.nativeHeapZeroInit = _nativeHeapZeroInit;
+        this.nativeHeapZeroInitialized = _nativeHeapZeroInitialized;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -202,10 +204,10 @@
     };
 
     @DataClass.Generated(
-            time = 1611614699049L,
+            time = 1615850184524L,
             codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/content/pm/ProcessInfo.java",
-            inputSignatures = "public @android.annotation.NonNull java.lang.String name\npublic @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringArraySet.class) android.util.ArraySet<java.lang.String> deniedPermissions\npublic @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\npublic @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\npublic @android.annotation.Nullable java.lang.Boolean nativeHeapZeroInit\nclass ProcessInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
+            inputSignatures = "public @android.annotation.NonNull java.lang.String name\npublic @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringArraySet.class) android.util.ArraySet<java.lang.String> deniedPermissions\npublic @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\npublic @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\npublic @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\nclass ProcessInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/SuspendDialogInfo.java b/core/java/android/content/pm/SuspendDialogInfo.java
index 60f3218..23945ee 100644
--- a/core/java/android/content/pm/SuspendDialogInfo.java
+++ b/core/java/android/content/pm/SuspendDialogInfo.java
@@ -65,16 +65,20 @@
     private static final String TAG = SuspendDialogInfo.class.getSimpleName();
     private static final String XML_ATTR_ICON_RES_ID = "iconResId";
     private static final String XML_ATTR_TITLE_RES_ID = "titleResId";
+    private static final String XML_ATTR_TITLE = "title";
     private static final String XML_ATTR_DIALOG_MESSAGE_RES_ID = "dialogMessageResId";
     private static final String XML_ATTR_DIALOG_MESSAGE = "dialogMessage";
     private static final String XML_ATTR_BUTTON_TEXT_RES_ID = "buttonTextResId";
+    private static final String XML_ATTR_BUTTON_TEXT = "buttonText";
     private static final String XML_ATTR_BUTTON_ACTION = "buttonAction";
 
     private final int mIconResId;
     private final int mTitleResId;
+    private final String mTitle;
     private final int mDialogMessageResId;
     private final String mDialogMessage;
     private final int mNeutralButtonTextResId;
+    private final String mNeutralButtonText;
     private final int mNeutralButtonAction;
 
     /**
@@ -129,6 +133,16 @@
     }
 
     /**
+     * @return the title to be shown on the dialog. Returns {@code null} if {@link #getTitleResId()}
+     * returns a valid resource id
+     * @hide
+     */
+    @Nullable
+    public String getTitle() {
+        return mTitle;
+    }
+
+    /**
      * @return the resource id of the text to be shown in the dialog's body
      * @hide
      */
@@ -148,7 +162,7 @@
     }
 
     /**
-     * @return the text to be shown
+     * @return the text to be shown on the neutral button
      * @hide
      */
     @StringRes
@@ -157,6 +171,16 @@
     }
 
     /**
+     * @return the text to be shown on the neutral button. Returns {@code null} if
+     * {@link #getNeutralButtonTextResId()} returns a valid resource id
+     * @hide
+     */
+    @Nullable
+    public String getNeutralButtonText() {
+        return mNeutralButtonText;
+    }
+
+    /**
      * @return The {@link ButtonAction} that happens on tapping this button
      * @hide
      */
@@ -174,6 +198,8 @@
         }
         if (mTitleResId != ID_NULL) {
             out.attributeInt(null, XML_ATTR_TITLE_RES_ID, mTitleResId);
+        } else {
+            XmlUtils.writeStringAttribute(out, XML_ATTR_TITLE, mTitle);
         }
         if (mDialogMessageResId != ID_NULL) {
             out.attributeInt(null, XML_ATTR_DIALOG_MESSAGE_RES_ID, mDialogMessageResId);
@@ -182,6 +208,8 @@
         }
         if (mNeutralButtonTextResId != ID_NULL) {
             out.attributeInt(null, XML_ATTR_BUTTON_TEXT_RES_ID, mNeutralButtonTextResId);
+        } else {
+            XmlUtils.writeStringAttribute(out, XML_ATTR_BUTTON_TEXT, mNeutralButtonText);
         }
         out.attributeInt(null, XML_ATTR_BUTTON_ACTION, mNeutralButtonAction);
     }
@@ -194,8 +222,10 @@
         try {
             final int iconId = in.getAttributeInt(null, XML_ATTR_ICON_RES_ID, ID_NULL);
             final int titleId = in.getAttributeInt(null, XML_ATTR_TITLE_RES_ID, ID_NULL);
+            final String title = XmlUtils.readStringAttribute(in, XML_ATTR_TITLE);
             final int buttonTextId =
                     in.getAttributeInt(null, XML_ATTR_BUTTON_TEXT_RES_ID, ID_NULL);
+            final String buttonText = XmlUtils.readStringAttribute(in, XML_ATTR_BUTTON_TEXT);
             final int buttonAction =
                     in.getAttributeInt(null, XML_ATTR_BUTTON_ACTION, BUTTON_ACTION_MORE_DETAILS);
             final int dialogMessageResId =
@@ -207,9 +237,13 @@
             }
             if (titleId != ID_NULL) {
                 dialogInfoBuilder.setTitle(titleId);
+            } else if (title != null) {
+                dialogInfoBuilder.setTitle(title);
             }
             if (buttonTextId != ID_NULL) {
                 dialogInfoBuilder.setNeutralButtonText(buttonTextId);
+            } else if (buttonText != null) {
+                dialogInfoBuilder.setNeutralButtonText(buttonText);
             }
             if (dialogMessageResId != ID_NULL) {
                 dialogInfoBuilder.setMessage(dialogMessageResId);
@@ -227,7 +261,9 @@
     public int hashCode() {
         int hashCode = mIconResId;
         hashCode = 31 * hashCode + mTitleResId;
+        hashCode = 31 * hashCode + Objects.hashCode(mTitle);
         hashCode = 31 * hashCode + mNeutralButtonTextResId;
+        hashCode = 31 * hashCode + Objects.hashCode(mNeutralButtonText);
         hashCode = 31 * hashCode + mDialogMessageResId;
         hashCode = 31 * hashCode + Objects.hashCode(mDialogMessage);
         hashCode = 31 * hashCode + mNeutralButtonAction;
@@ -245,10 +281,12 @@
         final SuspendDialogInfo otherDialogInfo = (SuspendDialogInfo) obj;
         return mIconResId == otherDialogInfo.mIconResId
                 && mTitleResId == otherDialogInfo.mTitleResId
+                && Objects.equals(mTitle, otherDialogInfo.mTitle)
                 && mDialogMessageResId == otherDialogInfo.mDialogMessageResId
+                && Objects.equals(mDialogMessage, otherDialogInfo.mDialogMessage)
                 && mNeutralButtonTextResId == otherDialogInfo.mNeutralButtonTextResId
-                && mNeutralButtonAction == otherDialogInfo.mNeutralButtonAction
-                && Objects.equals(mDialogMessage, otherDialogInfo.mDialogMessage);
+                && Objects.equals(mNeutralButtonText, otherDialogInfo.mNeutralButtonText)
+                && mNeutralButtonAction == otherDialogInfo.mNeutralButtonAction;
     }
 
     @NonNull
@@ -264,11 +302,19 @@
             builder.append("mTitleResId = 0x");
             builder.append(Integer.toHexString(mTitleResId));
             builder.append(" ");
+        } else if (mTitle != null) {
+            builder.append("mTitle = \"");
+            builder.append(mTitle);
+            builder.append("\"");
         }
         if (mNeutralButtonTextResId != ID_NULL) {
             builder.append("mNeutralButtonTextResId = 0x");
             builder.append(Integer.toHexString(mNeutralButtonTextResId));
             builder.append(" ");
+        } else if (mNeutralButtonText != null) {
+            builder.append("mNeutralButtonText = \"");
+            builder.append(mNeutralButtonText);
+            builder.append("\"");
         }
         if (mDialogMessageResId != ID_NULL) {
             builder.append("mDialogMessageResId = 0x");
@@ -294,27 +340,33 @@
     public void writeToParcel(Parcel dest, int parcelableFlags) {
         dest.writeInt(mIconResId);
         dest.writeInt(mTitleResId);
+        dest.writeString(mTitle);
         dest.writeInt(mDialogMessageResId);
         dest.writeString(mDialogMessage);
         dest.writeInt(mNeutralButtonTextResId);
+        dest.writeString(mNeutralButtonText);
         dest.writeInt(mNeutralButtonAction);
     }
 
     private SuspendDialogInfo(Parcel source) {
         mIconResId = source.readInt();
         mTitleResId = source.readInt();
+        mTitle = source.readString();
         mDialogMessageResId = source.readInt();
         mDialogMessage = source.readString();
         mNeutralButtonTextResId = source.readInt();
+        mNeutralButtonText = source.readString();
         mNeutralButtonAction = source.readInt();
     }
 
     SuspendDialogInfo(Builder b) {
         mIconResId = b.mIconResId;
         mTitleResId = b.mTitleResId;
+        mTitle = (mTitleResId == ID_NULL) ? b.mTitle : null;
         mDialogMessageResId = b.mDialogMessageResId;
         mDialogMessage = (mDialogMessageResId == ID_NULL) ? b.mDialogMessage : null;
         mNeutralButtonTextResId = b.mNeutralButtonTextResId;
+        mNeutralButtonText = (mNeutralButtonTextResId == ID_NULL) ? b.mNeutralButtonText : null;
         mNeutralButtonAction = b.mNeutralButtonAction;
     }
 
@@ -338,8 +390,10 @@
         private int mDialogMessageResId = ID_NULL;
         private String mDialogMessage;
         private int mTitleResId = ID_NULL;
+        private String mTitle;
         private int mIconResId = ID_NULL;
         private int mNeutralButtonTextResId = ID_NULL;
+        private String mNeutralButtonText;
         private int mNeutralButtonAction = BUTTON_ACTION_MORE_DETAILS;
 
         /**
@@ -370,6 +424,21 @@
         }
 
         /**
+         * Set the title text of the dialog. Ignored if a resource id is set via
+         * {@link #setTitle(int)}
+         *
+         * @param title The title of the dialog.
+         * @return this builder object.
+         * @see #setTitle(int)
+         */
+        @NonNull
+        public Builder setTitle(@NonNull String title) {
+            Preconditions.checkStringNotEmpty(title, "Title cannot be null or empty");
+            mTitle = title;
+            return this;
+        }
+
+        /**
          * Set the text to show in the body of the dialog. Ignored if a resource id is set via
          * {@link #setMessage(int)}.
          * <p>
@@ -427,6 +496,22 @@
         }
 
         /**
+         * Set the text to be shown on the neutral button. Ignored if a resource id is set via
+         * {@link #setNeutralButtonText(int)}
+         *
+         * @param neutralButtonText The title of the dialog.
+         * @return this builder object.
+         * @see #setNeutralButtonText(int)
+         */
+        @NonNull
+        public Builder setNeutralButtonText(@NonNull String neutralButtonText) {
+            Preconditions.checkStringNotEmpty(neutralButtonText,
+                    "Button text cannot be null or empty");
+            mNeutralButtonText = neutralButtonText;
+            return this;
+        }
+
+        /**
          * Set the action expected to happen on neutral button tap. Defaults to
          * {@link #BUTTON_ACTION_MORE_DETAILS} if this is not provided.
          *
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 4dc9ce8..1c65e00 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.FeatureGroupInfo;
 import android.content.pm.FeatureInfo;
@@ -251,11 +252,12 @@
 
     ParsingPackage setEnabled(boolean enabled);
 
-    ParsingPackage setGwpAsanMode(int gwpAsanMode);
+    ParsingPackage setGwpAsanMode(@ApplicationInfo.GwpAsanMode int gwpAsanMode);
 
-    ParsingPackage setMemtagMode(int memtagMode);
+    ParsingPackage setMemtagMode(@ApplicationInfo.MemtagMode int memtagMode);
 
-    ParsingPackage setNativeHeapZeroInit(@Nullable Boolean nativeHeapZeroInit);
+    ParsingPackage setNativeHeapZeroInitialized(
+            @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized);
 
     ParsingPackage setRequestOptimizedExternalStorageAccess(
             @Nullable Boolean requestOptimizedExternalStorageAccess);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 065ed2e..60aac76 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -382,12 +382,14 @@
 
     private int autoRevokePermissions;
 
-    protected int gwpAsanMode;
-    protected int memtagMode;
+    @ApplicationInfo.GwpAsanMode
+    private int gwpAsanMode;
 
-    @Nullable
-    @DataClass.ParcelWith(ForBoolean.class)
-    private Boolean nativeHeapZeroInit;
+    @ApplicationInfo.MemtagMode
+    private int memtagMode;
+
+    @ApplicationInfo.NativeHeapZeroInitialized
+    private int nativeHeapZeroInitialized;
 
     @Nullable
     @DataClass.ParcelWith(ForBoolean.class)
@@ -1071,7 +1073,7 @@
         appInfo.zygotePreloadName = zygotePreloadName;
         appInfo.setGwpAsanMode(gwpAsanMode);
         appInfo.setMemtagMode(memtagMode);
-        appInfo.setNativeHeapZeroInit(nativeHeapZeroInit);
+        appInfo.setNativeHeapZeroInitialized(nativeHeapZeroInitialized);
         appInfo.setRequestOptimizedExternalStorageAccess(requestOptimizedExternalStorageAccess);
         appInfo.setBaseCodePath(mBaseApkPath);
         appInfo.setBaseResourcePath(mBaseApkPath);
@@ -1207,7 +1209,7 @@
         dest.writeLong(this.mBooleans);
         dest.writeMap(this.mProperties);
         dest.writeInt(this.memtagMode);
-        sForBoolean.parcel(this.nativeHeapZeroInit, dest, flags);
+        dest.writeInt(this.nativeHeapZeroInitialized);
         sForBoolean.parcel(this.requestOptimizedExternalStorageAccess, dest, flags);
     }
 
@@ -1331,7 +1333,7 @@
         this.mBooleans = in.readLong();
         this.mProperties = in.createTypedArrayMap(Property.CREATOR);
         this.memtagMode = in.readInt();
-        this.nativeHeapZeroInit = sForBoolean.unparcel(in);
+        this.nativeHeapZeroInitialized = in.readInt();
         this.requestOptimizedExternalStorageAccess = sForBoolean.unparcel(in);
         assignDerivedFields();
     }
@@ -2096,20 +2098,22 @@
         return getBoolean(Booleans.DIRECT_BOOT_AWARE);
     }
 
+    @ApplicationInfo.GwpAsanMode
     @Override
     public int getGwpAsanMode() {
         return gwpAsanMode;
     }
 
+    @ApplicationInfo.MemtagMode
     @Override
     public int getMemtagMode() {
         return memtagMode;
     }
 
-    @Nullable
+    @ApplicationInfo.NativeHeapZeroInitialized
     @Override
-    public Boolean isNativeHeapZeroInit() {
-        return nativeHeapZeroInit;
+    public int getNativeHeapZeroInitialized() {
+        return nativeHeapZeroInitialized;
     }
 
     @Nullable
@@ -2550,20 +2554,21 @@
     }
 
     @Override
-    public ParsingPackageImpl setGwpAsanMode(int value) {
+    public ParsingPackageImpl setGwpAsanMode(@ApplicationInfo.GwpAsanMode int value) {
         gwpAsanMode = value;
         return this;
     }
 
     @Override
-    public ParsingPackageImpl setMemtagMode(int value) {
+    public ParsingPackageImpl setMemtagMode(@ApplicationInfo.MemtagMode int value) {
         memtagMode = value;
         return this;
     }
 
     @Override
-    public ParsingPackageImpl setNativeHeapZeroInit(@Nullable Boolean value) {
-        nativeHeapZeroInit = value;
+    public ParsingPackageImpl setNativeHeapZeroInitialized(
+            @ApplicationInfo.NativeHeapZeroInitialized int value) {
+        nativeHeapZeroInitialized = value;
         return this;
     }
 
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 47dfa9d..cfd828e 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -887,20 +887,22 @@
      * @see ApplicationInfo#gwpAsanMode
      * @see R.styleable#AndroidManifest_gwpAsanMode
      */
+    @ApplicationInfo.GwpAsanMode
     int getGwpAsanMode();
 
     /**
      * @see ApplicationInfo#memtagMode
      * @see R.styleable#AndroidManifest_memtagMode
      */
+    @ApplicationInfo.MemtagMode
     int getMemtagMode();
 
-      /**
-     * @see ApplicationInfo#nativeHeapZeroInit
-     * @see R.styleable#AndroidManifest_nativeHeapZeroInit
+    /**
+     * @see ApplicationInfo#nativeHeapZeroInitialized
+     * @see R.styleable#AndroidManifest_nativeHeapZeroInitialized
      */
-    @Nullable
-    Boolean isNativeHeapZeroInit();
+    @ApplicationInfo.NativeHeapZeroInitialized
+    int getNativeHeapZeroInitialized();
 
     @Nullable
     Boolean hasRequestOptimizedExternalStorageAccess();
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 0e1574c..9f69d0c 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -2008,9 +2008,11 @@
 
             pkg.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestApplication_gwpAsanMode, -1));
             pkg.setMemtagMode(sa.getInt(R.styleable.AndroidManifestApplication_memtagMode, -1));
-            if (sa.hasValue(R.styleable.AndroidManifestApplication_nativeHeapZeroInit)) {
-                pkg.setNativeHeapZeroInit(sa.getBoolean(
-                        R.styleable.AndroidManifestApplication_nativeHeapZeroInit, false));
+            if (sa.hasValue(R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized)) {
+                Boolean v = sa.getBoolean(
+                        R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized, false);
+                pkg.setNativeHeapZeroInitialized(
+                        v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
             }
             if (sa.hasValue(
                     R.styleable.AndroidManifestApplication_requestOptimizedExternalStorageAccess)) {
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcess.java b/core/java/android/content/pm/parsing/component/ParsedProcess.java
index 89fef9d..54a60d3 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcess.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcess.java
@@ -42,10 +42,12 @@
     @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class)
     protected Set<String> deniedPermissions = emptySet();
 
+    @ApplicationInfo.GwpAsanMode
     protected int gwpAsanMode = ApplicationInfo.GWP_ASAN_DEFAULT;
+    @ApplicationInfo.MemtagMode
     protected int memtagMode = ApplicationInfo.MEMTAG_DEFAULT;
-    @Nullable
-    protected Boolean nativeHeapZeroInit = null;
+    @ApplicationInfo.NativeHeapZeroInitialized
+    protected int nativeHeapZeroInitialized = ApplicationInfo.ZEROINIT_DEFAULT;
 
     public ParsedProcess() {
     }
@@ -78,9 +80,9 @@
     public ParsedProcess(
             @NonNull String name,
             @NonNull Set<String> deniedPermissions,
-            int gwpAsanMode,
-            int memtagMode,
-            @Nullable Boolean nativeHeapZeroInit) {
+            @ApplicationInfo.GwpAsanMode int gwpAsanMode,
+            @ApplicationInfo.MemtagMode int memtagMode,
+            @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized) {
         this.name = name;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, name);
@@ -88,8 +90,14 @@
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, deniedPermissions);
         this.gwpAsanMode = gwpAsanMode;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
         this.memtagMode = memtagMode;
-        this.nativeHeapZeroInit = nativeHeapZeroInit;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.MemtagMode.class, null, memtagMode);
+        this.nativeHeapZeroInitialized = nativeHeapZeroInitialized;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -105,18 +113,18 @@
     }
 
     @DataClass.Generated.Member
-    public int getGwpAsanMode() {
+    public @ApplicationInfo.GwpAsanMode int getGwpAsanMode() {
         return gwpAsanMode;
     }
 
     @DataClass.Generated.Member
-    public int getMemtagMode() {
+    public @ApplicationInfo.MemtagMode int getMemtagMode() {
         return memtagMode;
     }
 
     @DataClass.Generated.Member
-    public @Nullable Boolean getNativeHeapZeroInit() {
-        return nativeHeapZeroInit;
+    public @ApplicationInfo.NativeHeapZeroInitialized int getNativeHeapZeroInitialized() {
+        return nativeHeapZeroInitialized;
     }
 
     @DataClass.Generated.Member
@@ -136,14 +144,11 @@
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
-        byte flg = 0;
-        if (nativeHeapZeroInit != null) flg |= 0x10;
-        dest.writeByte(flg);
         dest.writeString(name);
         sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
         dest.writeInt(gwpAsanMode);
         dest.writeInt(memtagMode);
-        if (nativeHeapZeroInit != null) dest.writeBoolean(nativeHeapZeroInit);
+        dest.writeInt(nativeHeapZeroInitialized);
     }
 
     @Override
@@ -157,12 +162,11 @@
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
-        byte flg = in.readByte();
         String _name = in.readString();
         Set<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
         int _gwpAsanMode = in.readInt();
         int _memtagMode = in.readInt();
-        Boolean _nativeHeapZeroInit = (flg & 0x10) == 0 ? null : (Boolean) in.readBoolean();
+        int _nativeHeapZeroInitialized = in.readInt();
 
         this.name = _name;
         com.android.internal.util.AnnotationValidations.validate(
@@ -171,8 +175,14 @@
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, deniedPermissions);
         this.gwpAsanMode = _gwpAsanMode;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
         this.memtagMode = _memtagMode;
-        this.nativeHeapZeroInit = _nativeHeapZeroInit;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.MemtagMode.class, null, memtagMode);
+        this.nativeHeapZeroInitialized = _nativeHeapZeroInitialized;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -192,10 +202,10 @@
     };
 
     @DataClass.Generated(
-            time = 1611615591258L,
+            time = 1615850515058L,
             codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcess.java",
-            inputSignatures = "protected @android.annotation.NonNull java.lang.String name\nprotected @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprotected  int gwpAsanMode\nprotected  int memtagMode\nprotected @android.annotation.Nullable java.lang.Boolean nativeHeapZeroInit\npublic  void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
+            inputSignatures = "protected @android.annotation.NonNull java.lang.String name\nprotected @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprotected @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprotected @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprotected @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic  void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
index 2579774..e417e74 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
@@ -17,6 +17,7 @@
 package android.content.pm.parsing.component;
 
 import android.annotation.NonNull;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.ParsingUtils;
 import android.content.pm.parsing.result.ParseInput;
@@ -104,9 +105,11 @@
 
             proc.gwpAsanMode = sa.getInt(R.styleable.AndroidManifestProcess_gwpAsanMode, -1);
             proc.memtagMode = sa.getInt(R.styleable.AndroidManifestProcess_memtagMode, -1);
-            if (sa.hasValue(R.styleable.AndroidManifestProcess_nativeHeapZeroInit)) {
-                proc.nativeHeapZeroInit =
-                        sa.getBoolean(R.styleable.AndroidManifestProcess_nativeHeapZeroInit, false);
+            if (sa.hasValue(R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized)) {
+                Boolean v = sa.getBoolean(
+                        R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized, false);
+                proc.nativeHeapZeroInitialized =
+                        v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED;
             }
         } finally {
             sa.recycle();
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
index d2d1441..33920c6 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
@@ -179,10 +179,7 @@
      */
     @SystemApi
     @Nullable
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.DOMAIN_VERIFICATION_AGENT,
-            android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
-    })
+    @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
     public DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName)
             throws NameNotFoundException {
         try {
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index 5eaa766..2a349e9 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -46,7 +46,7 @@
  * The indices used to retrieve values from this structure correspond to
  * the positions of the attributes given to obtainStyledAttributes.
  */
-public class TypedArray {
+public class TypedArray implements AutoCloseable {
 
     static TypedArray obtain(Resources res, int len) {
         TypedArray attrs = res.mTypedArrayPool.acquire();
@@ -1253,6 +1253,17 @@
     }
 
     /**
+     * Recycles the TypedArray, to be re-used by a later caller. After calling
+     * this function you must not ever touch the typed array again.
+     *
+     * @see #recycle()
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    public void close() {
+        recycle();
+    }
+
+    /**
      * Extracts theme attributes from a typed array for later resolution using
      * {@link android.content.res.Resources.Theme#resolveAttributes(int[], int[])}.
      * Removes the entries from the typed array so that subsequent calls to typed
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 07ebbaf..6654c2c 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1210,6 +1210,25 @@
             new Key<android.util.Range<Float>>("android.control.zoomRatioRange", new TypeReference<android.util.Range<Float>>() {{ }});
 
     /**
+     * <p>List of available high speed video size, fps range and max batch size configurations
+     * supported by the camera device, in the format of
+     * (width, height, fps_min, fps_max, batch_size_max),
+     * when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p>Analogous to android.control.availableHighSpeedVideoConfigurations, for configurations
+     * which are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p><b>Range of valid values:</b><br></p>
+     * <p>For each configuration, the fps_max &gt;= 120fps.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.HighSpeedVideoConfiguration[]> CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+            new Key<android.hardware.camera2.params.HighSpeedVideoConfiguration[]>("android.control.availableHighSpeedVideoConfigurationsMaximumResolution", android.hardware.camera2.params.HighSpeedVideoConfiguration[].class);
+
+    /**
      * <p>List of edge enhancement modes for {@link CaptureRequest#EDGE_MODE android.edge.mode} that are supported by this camera
      * device.</p>
      * <p>Full-capability camera devices must always support OFF; camera devices that support
@@ -1770,6 +1789,48 @@
             new Key<float[]>("android.lens.distortion", float[].class);
 
     /**
+     * <p>The correction coefficients to correct for this camera device's
+     * radial and tangential lens distortion for a
+     * CaptureRequest with {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p>Analogous to {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}, when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p><b>Units</b>:
+     * Unitless coefficients.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
+     *
+     * @see CameraCharacteristics#LENS_DISTORTION
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     */
+    @PublicKey
+    @NonNull
+    public static final Key<float[]> LENS_DISTORTION_MAXIMUM_RESOLUTION =
+            new Key<float[]>("android.lens.distortionMaximumResolution", float[].class);
+
+    /**
+     * <p>The parameters for this camera device's intrinsic
+     * calibration when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p>Analogous to {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}, when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p><b>Units</b>:
+     * Pixels in the
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution}
+     * coordinate system.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
+     *
+     * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     */
+    @PublicKey
+    @NonNull
+    public static final Key<float[]> LENS_INTRINSIC_CALIBRATION_MAXIMUM_RESOLUTION =
+            new Key<float[]>("android.lens.intrinsicCalibrationMaximumResolution", float[].class);
+
+    /**
      * <p>List of noise reduction modes for {@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode} that are supported
      * by this camera device.</p>
      * <p>Full-capability camera devices will always support OFF and FAST.</p>
@@ -2056,6 +2117,8 @@
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA SECURE_IMAGE_DATA}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA SYSTEM_CAMERA}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING OFFLINE_PROCESSING}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR ULTRA_HIGH_RESOLUTION_SENSOR}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING REMOSAIC_REPROCESSING}</li>
      * </ul>
      *
      * <p>This key is available on all devices.</p>
@@ -2077,6 +2140,8 @@
      * @see #REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA
      * @see #REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA
      * @see #REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING
      */
     @PublicKey
     @NonNull
@@ -2535,8 +2600,6 @@
      * set to either OFF or FAST.</p>
      * <p>When multiple streams are used in a request, the minimum frame
      * duration will be max(individual stream min durations).</p>
-     * <p>The minimum frame duration of a stream (of a particular format, size)
-     * is the same regardless of whether the stream is input or output.</p>
      * <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} and
      * android.scaler.availableStallDurations for more details about
      * calculating the max frame rate.</p>
@@ -2916,10 +2979,10 @@
      * configurations which belong to this physical camera, and it will advertise and will only
      * advertise the maximum supported resolutions for a particular format.</p>
      * <p>If this camera device isn't a physical camera device constituting a logical camera,
-     * but a standalone ULTRA_HIGH_RESOLUTION_SENSOR camera, this field represents the
-     * multi-resolution input/output stream configurations of default mode and max resolution
-     * modes. The sizes will be the maximum resolution of a particular format for default mode
-     * and max resolution mode.</p>
+     * but a standalone {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * camera, this field represents the multi-resolution input/output stream configurations of
+     * default mode and max resolution modes. The sizes will be the maximum resolution of a
+     * particular format for default mode and max resolution mode.</p>
      * <p>This field will only be advertised if the device is a physical camera of a
      * logical multi-camera device or an ultra high resolution sensor camera. For a logical
      * multi-camera, the camera API will derive the logical camera’s multi-resolution stream
@@ -2977,6 +3040,132 @@
             new Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap>("android.scaler.multiResolutionStreamConfigurationMap", android.hardware.camera2.params.MultiResolutionStreamConfigurationMap.class);
 
     /**
+     * <p>The available stream configurations that this
+     * camera device supports (i.e. format, width, height, output/input stream) for a
+     * CaptureRequest with {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p>Analogous to android.scaler.availableStreamConfigurations, for configurations
+     * which are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p>Not all output formats may be supported in a configuration with
+     * an input stream of a particular format. For more details, see
+     * android.scaler.availableInputOutputFormatsMapMaximumResolution.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.StreamConfiguration[]> SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+            new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.scaler.availableStreamConfigurationsMaximumResolution", android.hardware.camera2.params.StreamConfiguration[].class);
+
+    /**
+     * <p>This lists the minimum frame duration for each
+     * format/size combination when the camera device is sent a CaptureRequest with
+     * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p>Analogous to android.scaler.availableMinFrameDurations, for configurations
+     * which are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p>When multiple streams are used in a request (if supported, when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}
+     * is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }), the
+     * minimum frame duration will be max(individual stream min durations).</p>
+     * <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} and
+     * android.scaler.availableStallDurationsMaximumResolution for more details about
+     * calculating the max frame rate.</p>
+     * <p><b>Units</b>: (format, width, height, ns) x n</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#SENSOR_FRAME_DURATION
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> SCALER_AVAILABLE_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION =
+            new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.scaler.availableMinFrameDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+    /**
+     * <p>This lists the maximum stall duration for each
+     * output format/size combination when CaptureRequests are submitted with
+     * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }</p>
+     * <p>Analogous to android.scaler.availableMinFrameDurations, for configurations
+     * which are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p><b>Units</b>: (format, width, height, ns) x n</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION =
+            new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.scaler.availableStallDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+    /**
+     * <p>The available stream configurations that this
+     * camera device supports when given a CaptureRequest with {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}
+     * set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION };
+     * also includes the minimum frame durations
+     * and the stall durations for each format/size combination.</p>
+     * <p>Analogous to {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} for CaptureRequests where
+     * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     */
+    @PublicKey
+    @NonNull
+    @SyntheticKey
+    public static final Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION =
+            new Key<android.hardware.camera2.params.StreamConfigurationMap>("android.scaler.streamConfigurationMapMaximumResolution", android.hardware.camera2.params.StreamConfigurationMap.class);
+
+    /**
+     * <p>The mapping of image formats that are supported by this
+     * camera device for input streams, to their corresponding output formats, when
+     * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p>Analogous to android.scaler.availableInputOutputFormatsMap for CaptureRequests where
+     * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.ReprocessFormatsMap> SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP_MAXIMUM_RESOLUTION =
+            new Key<android.hardware.camera2.params.ReprocessFormatsMap>("android.scaler.availableInputOutputFormatsMapMaximumResolution", android.hardware.camera2.params.ReprocessFormatsMap.class);
+
+    /**
+     * <p>An array of mandatory stream combinations which are applicable when
+     * {@link android.hardware.camera2.CaptureRequest } has {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} set
+     * to {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.
+     * This is an app-readable conversion of the maximum resolution mandatory stream combination
+     * {@link android.hardware.camera2.CameraDevice#createCaptureSession tables}.</p>
+     * <p>The array of
+     * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is
+     * generated according to the documented
+     * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for each
+     * device which has the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * capability.
+     * Clients can use the array as a quick reference to find an appropriate camera stream
+     * combination.
+     * The mandatory stream combination array will be {@code null} in case the device is not an
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * device.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     */
+    @PublicKey
+    @NonNull
+    @SyntheticKey
+    public static final Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS =
+            new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryMaximumResolutionStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class);
+
+    /**
      * <p>The area of the image sensor which corresponds to active pixels after any geometric
      * distortion correction has been applied.</p>
      * <p>This is the rectangle representing the size of the active region of the sensor (i.e.
@@ -3292,6 +3481,101 @@
             new Key<android.graphics.Rect>("android.sensor.info.preCorrectionActiveArraySize", android.graphics.Rect.class);
 
     /**
+     * <p>The area of the image sensor which corresponds to active pixels after any geometric
+     * distortion correction has been applied, when the sensor runs in maximum resolution mode.</p>
+     * <p>Analogous to {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}
+     * is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.
+     * Refer to {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} for details, with sensor array related keys
+     * replaced with their
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }
+     * counterparts.
+     * This key will only be present for devices which advertise the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * capability.</p>
+     * <p><b>Units</b>: Pixel coordinates on the image sensor</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     */
+    @PublicKey
+    @NonNull
+    public static final Key<android.graphics.Rect> SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION =
+            new Key<android.graphics.Rect>("android.sensor.info.activeArraySizeMaximumResolution", android.graphics.Rect.class);
+
+    /**
+     * <p>Dimensions of the full pixel array, possibly
+     * including black calibration pixels, when the sensor runs in maximum resolution mode.
+     * Analogous to {@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize}, when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is
+     * set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p>The pixel count of the full pixel array of the image sensor, which covers
+     * {@link CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE android.sensor.info.physicalSize} area. This represents the full pixel dimensions of
+     * the raw buffers produced by this sensor, when it runs in maximum resolution mode. That
+     * is, when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.
+     * This key will only be present for devices which advertise the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * capability.</p>
+     * <p><b>Units</b>: Pixels</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     */
+    @PublicKey
+    @NonNull
+    public static final Key<android.util.Size> SENSOR_INFO_PIXEL_ARRAY_SIZE_MAXIMUM_RESOLUTION =
+            new Key<android.util.Size>("android.sensor.info.pixelArraySizeMaximumResolution", android.util.Size.class);
+
+    /**
+     * <p>The area of the image sensor which corresponds to active pixels prior to the
+     * application of any geometric distortion correction, when the sensor runs in maximum
+     * resolution mode. This key must be used for crop / metering regions, only when
+     * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p>Analogous to {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize},
+     * when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.
+     * This key will only be present for devices which advertise the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * capability.</p>
+     * <p><b>Units</b>: Pixel coordinates on the image sensor</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     */
+    @PublicKey
+    @NonNull
+    public static final Key<android.graphics.Rect> SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION =
+            new Key<android.graphics.Rect>("android.sensor.info.preCorrectionActiveArraySizeMaximumResolution", android.graphics.Rect.class);
+
+    /**
+     * <p>Dimensions of the group of pixels which are under the same color filter.
+     * This specifies the width and height (pair of integers) of the group of pixels which fall
+     * under the same color filter for ULTRA_HIGH_RESOLUTION sensors.</p>
+     * <p>Sensors can have pixels grouped together under the same color filter in order
+     * to improve various aspects of imaging such as noise reduction, low light
+     * performance etc. These groups can be of various sizes such as 2X2 (quad bayer),
+     * 3X3 (nona-bayer). This key specifies the length and width of the pixels grouped under
+     * the same color filter.</p>
+     * <p>This key will not be present if REMOSAIC_REPROCESSING is not supported, since RAW images
+     * will have a regular bayer pattern.</p>
+     * <p>This key will not be present for sensors which don't have the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * capability.</p>
+     * <p><b>Units</b>: Pixels</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     */
+    @PublicKey
+    @NonNull
+    public static final Key<android.util.Size> SENSOR_INFO_BINNING_FACTOR =
+            new Key<android.util.Size>("android.sensor.info.binningFactor", android.util.Size.class);
+
+    /**
      * <p>The standard reference illuminant used as the scene light source when
      * calculating the {@link CameraCharacteristics#SENSOR_COLOR_TRANSFORM1 android.sensor.colorTransform1},
      * {@link CameraCharacteristics#SENSOR_CALIBRATION_TRANSFORM1 android.sensor.calibrationTransform1}, and
@@ -4150,6 +4434,111 @@
             new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDynamicDepthStallDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class);
 
     /**
+     * <p>The available depth dataspace stream
+     * configurations that this camera device supports
+     * (i.e. format, width, height, output/input stream) when a CaptureRequest is submitted with
+     * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p>Analogous to android.depth.availableDepthStreamConfigurations, for configurations which
+     * are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.StreamConfiguration[]> DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+            new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.depth.availableDepthStreamConfigurationsMaximumResolution", android.hardware.camera2.params.StreamConfiguration[].class);
+
+    /**
+     * <p>This lists the minimum frame duration for each
+     * format/size combination for depth output formats when a CaptureRequest is submitted with
+     * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p>Analogous to android.depth.availableDepthMinFrameDurations, for configurations which
+     * are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} and
+     * android.scaler.availableStallDurationsMaximumResolution for more details about
+     * calculating the max frame rate.</p>
+     * <p><b>Units</b>: (format, width, height, ns) x n</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#SENSOR_FRAME_DURATION
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION =
+            new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDepthMinFrameDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+    /**
+     * <p>This lists the maximum stall duration for each
+     * output format/size combination for depth streams for CaptureRequests where
+     * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p>Analogous to android.depth.availableDepthStallDurations, for configurations which
+     * are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p><b>Units</b>: (format, width, height, ns) x n</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION =
+            new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDepthStallDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+    /**
+     * <p>The available dynamic depth dataspace stream
+     * configurations that this camera device supports (i.e. format, width, height,
+     * output/input stream) for CaptureRequests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p>Analogous to android.depth.availableDynamicDepthStreamConfigurations, for configurations
+     * which are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.StreamConfiguration[]> DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+            new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.depth.availableDynamicDepthStreamConfigurationsMaximumResolution", android.hardware.camera2.params.StreamConfiguration[].class);
+
+    /**
+     * <p>This lists the minimum frame duration for each
+     * format/size combination for dynamic depth output streams  for CaptureRequests where
+     * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p>Analogous to android.depth.availableDynamicDepthMinFrameDurations, for configurations
+     * which are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p><b>Units</b>: (format, width, height, ns) x n</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION =
+            new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDynamicDepthMinFrameDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+    /**
+     * <p>This lists the maximum stall duration for each
+     * output format/size combination for dynamic depth streams for CaptureRequests where
+     * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p>Analogous to android.depth.availableDynamicDepthStallDurations, for configurations
+     * which are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p><b>Units</b>: (format, width, height, ns) x n</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION =
+            new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDynamicDepthStallDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+    /**
      * <p>String containing the ids of the underlying physical cameras.</p>
      * <p>For a logical camera, this is concatenation of all underlying physical camera IDs.
      * The null terminator for physical camera ID must be preserved so that the whole string
@@ -4286,6 +4675,47 @@
     public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> HEIC_AVAILABLE_HEIC_STALL_DURATIONS =
             new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.heic.availableHeicStallDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class);
 
+    /**
+     * <p>The available HEIC (ISO/IEC 23008-12) stream
+     * configurations that this camera device supports
+     * (i.e. format, width, height, output/input stream).</p>
+     * <p>Refer to android.heic.availableHeicStreamConfigurations for details.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.StreamConfiguration[]> HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+            new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.heic.availableHeicStreamConfigurationsMaximumResolution", android.hardware.camera2.params.StreamConfiguration[].class);
+
+    /**
+     * <p>This lists the minimum frame duration for each
+     * format/size combination for HEIC output formats for CaptureRequests where
+     * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p>Refer to android.heic.availableHeicMinFrameDurations for details.</p>
+     * <p><b>Units</b>: (format, width, height, ns) x n</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION =
+            new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.heic.availableHeicMinFrameDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+    /**
+     * <p>This lists the maximum stall duration for each
+     * output format/size combination for HEIC streams for CaptureRequests where
+     * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+     * <p>Refer to android.heic.availableHeicStallDurations for details.</p>
+     * <p><b>Units</b>: (format, width, height, ns) x n</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> HEIC_AVAILABLE_HEIC_STALL_DURATIONS_MAXIMUM_RESOLUTION =
+            new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.heic.availableHeicStallDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
     /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * End generated code
      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index b7b1a14..726bca4 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -507,6 +507,18 @@
         return new CameraExtensionCharacteristics(mContext, cameraId, chars);
     }
 
+    private Map<String, CameraCharacteristics> getPhysicalIdToCharsMap(
+            CameraCharacteristics chars) throws CameraAccessException {
+        HashMap<String, CameraCharacteristics> physicalIdsToChars =
+                new HashMap<String, CameraCharacteristics>();
+        Set<String> physicalCameraIds = chars.getPhysicalCameraIds();
+        for (String physicalCameraId : physicalCameraIds) {
+            CameraCharacteristics physicalChars = getCameraCharacteristics(physicalCameraId);
+            physicalIdsToChars.put(physicalCameraId, physicalChars);
+        }
+        return physicalIdsToChars;
+    }
+
     /**
      * Helper for opening a connection to a camera with the given ID.
      *
@@ -535,17 +547,18 @@
             throws CameraAccessException {
         CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
         CameraDevice device = null;
-
+        Map<String, CameraCharacteristics> physicalIdsToChars =
+                getPhysicalIdToCharsMap(characteristics);
         synchronized (mLock) {
 
             ICameraDeviceUser cameraUser = null;
-
             android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
                     new android.hardware.camera2.impl.CameraDeviceImpl(
                         cameraId,
                         callback,
                         executor,
                         characteristics,
+                        physicalIdsToChars,
                         mContext.getApplicationInfo().targetSdkVersion,
                         mContext);
 
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 924dcee..d4da3b9 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -586,7 +586,7 @@
      *   that is, {@link android.graphics.ImageFormat#PRIVATE } is included in the lists of
      *   formats returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats } and {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputFormats }.</li>
      * <li>{@link android.hardware.camera2.params.StreamConfigurationMap#getValidOutputFormatsForInput }
-     *   returns non empty int[] for each supported input format returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats }.</li>
+     *   returns non-empty int[] for each supported input format returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats }.</li>
      * <li>Each size returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputSizes getInputSizes(ImageFormat.PRIVATE)} is also included in {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes getOutputSizes(ImageFormat.PRIVATE)}</li>
      * <li>Using {@link android.graphics.ImageFormat#PRIVATE } does not cause a frame rate drop
      *   relative to the sensor's maximum capture rate (at that resolution).</li>
@@ -1114,6 +1114,63 @@
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING = 15;
 
+    /**
+     * <p>This camera device is capable of producing ultra high resolution images in
+     * addition to the image sizes described in the
+     * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}.
+     * It can operate in 'default' mode and 'max resolution' mode. It generally does this
+     * by binning pixels in 'default' mode and not binning them in 'max resolution' mode.
+     * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}</code> describes the streams supported in 'default'
+     * mode.
+     * The stream configurations supported in 'max resolution' mode are described by
+     * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION android.scaler.streamConfigurationMapMaximumResolution}</code>.</p>
+     *
+     * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+     * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR = 16;
+
+    /**
+     * <p>The device supports reprocessing from the <code>RAW_SENSOR</code> format with a bayer pattern
+     * given by {@link CameraCharacteristics#SENSOR_INFO_BINNING_FACTOR android.sensor.info.binningFactor} (m x n group of pixels with the same
+     * color filter) to a remosaiced regular bayer pattern.</p>
+     * <p>This capability will only be present for devices with
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * capability. When
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * devices do not advertise this capability,
+     * {@link android.graphics.ImageFormat#RAW_SENSOR } images will already have a
+     * regular bayer pattern.</p>
+     * <p>If a <code>RAW_SENSOR</code> stream is requested along with another non-RAW stream in a
+     * {@link android.hardware.camera2.CaptureRequest } (if multiple streams are supported
+     * when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }),
+     * the <code>RAW_SENSOR</code> stream will have a regular bayer pattern.</p>
+     * <p>This capability requires the camera device to support the following :
+     * * The {@link android.hardware.camera2.params.StreamConfigurationMap } mentioned below
+     *   refers to the one, described by
+     *   <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION android.scaler.streamConfigurationMapMaximumResolution}</code>.
+     * * One input stream is supported, that is, <code>{@link CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS android.request.maxNumInputStreams} == 1</code>.
+     * * {@link android.graphics.ImageFormat#RAW_SENSOR } is supported as an output/input
+     *   format, that is, {@link android.graphics.ImageFormat#RAW_SENSOR } is included in the
+     *   lists of formats returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats } and {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputFormats }.
+     * * {@link android.hardware.camera2.params.StreamConfigurationMap#getValidOutputFormatsForInput }
+     *   returns non-empty int[] for each supported input format returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats }.
+     * * Each size returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputSizes getInputSizes(ImageFormat.RAW_SENSOR)} is also included in {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes getOutputSizes(ImageFormat.RAW_SENSOR)}
+     * * Using {@link android.graphics.ImageFormat#RAW_SENSOR } does not cause a frame rate
+     *   drop relative to the sensor's maximum capture rate (at that resolution).
+     * * No CaptureRequest controls will be applicable when a request has an input target
+     *   with {@link android.graphics.ImageFormat#RAW_SENSOR } format.</p>
+     *
+     * @see CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS
+     * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION
+     * @see CameraCharacteristics#SENSOR_INFO_BINNING_FACTOR
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING = 17;
+
     //
     // Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
     //
@@ -2955,6 +3012,27 @@
     public static final int SENSOR_TEST_PATTERN_MODE_CUSTOM1 = 256;
 
     //
+    // Enumeration values for CaptureRequest#SENSOR_PIXEL_MODE
+    //
+
+    /**
+     * <p>This is the default sensor pixel mode. This is the only sensor pixel mode
+     * supported unless a camera device advertises
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }.</p>
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     */
+    public static final int SENSOR_PIXEL_MODE_DEFAULT = 0;
+
+    /**
+     * <p>This sensor pixel mode is offered by devices with capability
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }.
+     * In this mode, sensors typically do not bin pixels, as a result can offer larger
+     * image sizes.</p>
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     */
+    public static final int SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION = 1;
+
+    //
     // Enumeration values for CaptureRequest#SHADING_MODE
     //
 
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 9ac2ff5..906256d 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1391,6 +1391,13 @@
      * scene as they do before. See {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details. Whether to use
      * activeArraySize or preCorrectionActiveArraySize still depends on distortion correction
      * mode.</p>
+     * <p>For camera devices with the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * capability,
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} /
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the
+     * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
      * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
      * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
      * distortion correction capability and mode</p>
@@ -1405,7 +1412,10 @@
      * @see CaptureRequest#DISTORTION_CORRECTION_MODE
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
      */
     @PublicKey
     @NonNull
@@ -1603,6 +1613,12 @@
      * scene as they do before. See {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details. Whether to use
      * activeArraySize or preCorrectionActiveArraySize still depends on distortion correction
      * mode.</p>
+     * <p>For camera devices with the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * capability, {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} /
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the
+     * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
      * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
      * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
      * distortion correction capability and mode</p>
@@ -1617,7 +1633,10 @@
      * @see CaptureRequest#DISTORTION_CORRECTION_MODE
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
      */
     @PublicKey
     @NonNull
@@ -1808,6 +1827,12 @@
      * the scene as they do before. See {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details. Whether to use
      * activeArraySize or preCorrectionActiveArraySize still depends on distortion correction
      * mode.</p>
+     * <p>For camera devices with the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * capability, {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} /
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the
+     * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
      * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
      * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
      * distortion correction capability and mode</p>
@@ -1822,7 +1847,10 @@
      * @see CaptureRequest#DISTORTION_CORRECTION_MODE
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
      */
     @PublicKey
     @NonNull
@@ -2896,6 +2924,12 @@
      * coordinate system is post-zoom, meaning that the activeArraySize or
      * preCorrectionActiveArraySize covers the camera device's field of view "after" zoom.  See
      * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details.</p>
+     * <p>For camera devices with the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * capability, {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} /
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the
+     * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
      * <p><b>Units</b>: Pixel coordinates relative to
      * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
      * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on distortion correction
@@ -2908,7 +2942,10 @@
      * @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
      * @see CameraCharacteristics#SCALER_CROPPING_TYPE
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
      */
     @PublicKey
     @NonNull
@@ -3214,6 +3251,53 @@
             new Key<Integer>("android.sensor.testPatternMode", int.class);
 
     /**
+     * <p>Switches sensor pixel mode between maximum resolution mode and default mode.</p>
+     * <p>This key controls whether the camera sensor operates in
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }
+     * mode or not. By default, all camera devices operate in
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT } mode.
+     * When operating in
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT } mode, sensors
+     * with {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * capability would typically perform pixel binning in order to improve low light
+     * performance, noise reduction etc. However, in
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }
+     * mode (supported only
+     * by {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * sensors), sensors typically operate in unbinned mode allowing for a larger image size.
+     * The stream configurations supported in
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }
+     * mode are also different from those of
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT } mode.
+     * They can be queried through
+     * {@link android.hardware.camera2.CameraCharacteristics#get } with
+     * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION) }.
+     * Unless reported by both
+     * {@link android.hardware.camera2.params.StreamConfigurationMap }s, the outputs from
+     * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION android.scaler.streamConfigurationMapMaximumResolution}</code> and
+     * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}</code>
+     * must not be mixed in the same CaptureRequest. In other words, these outputs are
+     * exclusive to each other.
+     * This key does not need to be set for reprocess requests.</p>
+     * <p><b>Possible values:</b></p>
+     * <ul>
+     *   <li>{@link #SENSOR_PIXEL_MODE_DEFAULT DEFAULT}</li>
+     *   <li>{@link #SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION MAXIMUM_RESOLUTION}</li>
+     * </ul>
+     *
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+     * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION
+     * @see #SENSOR_PIXEL_MODE_DEFAULT
+     * @see #SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION
+     */
+    @PublicKey
+    @NonNull
+    public static final Key<Integer> SENSOR_PIXEL_MODE =
+            new Key<Integer>("android.sensor.pixelMode", int.class);
+
+    /**
      * <p>Quality of lens shading correction applied
      * to the image data.</p>
      * <p>When set to OFF mode, no lens shading correction will be applied by the
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index e7457e7..6ff68c1 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -812,6 +812,13 @@
      * scene as they do before. See {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details. Whether to use
      * activeArraySize or preCorrectionActiveArraySize still depends on distortion correction
      * mode.</p>
+     * <p>For camera devices with the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * capability,
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} /
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the
+     * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
      * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
      * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
      * distortion correction capability and mode</p>
@@ -826,7 +833,10 @@
      * @see CaptureRequest#DISTORTION_CORRECTION_MODE
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
      */
     @PublicKey
     @NonNull
@@ -1274,6 +1284,12 @@
      * scene as they do before. See {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details. Whether to use
      * activeArraySize or preCorrectionActiveArraySize still depends on distortion correction
      * mode.</p>
+     * <p>For camera devices with the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * capability, {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} /
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the
+     * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
      * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
      * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
      * distortion correction capability and mode</p>
@@ -1288,7 +1304,10 @@
      * @see CaptureRequest#DISTORTION_CORRECTION_MODE
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
      */
     @PublicKey
     @NonNull
@@ -1890,6 +1909,12 @@
      * the scene as they do before. See {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details. Whether to use
      * activeArraySize or preCorrectionActiveArraySize still depends on distortion correction
      * mode.</p>
+     * <p>For camera devices with the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * capability, {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} /
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the
+     * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
      * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
      * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
      * distortion correction capability and mode</p>
@@ -1904,7 +1929,10 @@
      * @see CaptureRequest#DISTORTION_CORRECTION_MODE
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
      */
     @PublicKey
     @NonNull
@@ -3565,6 +3593,12 @@
      * coordinate system is post-zoom, meaning that the activeArraySize or
      * preCorrectionActiveArraySize covers the camera device's field of view "after" zoom.  See
      * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details.</p>
+     * <p>For camera devices with the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * capability, {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} /
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the
+     * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
      * <p><b>Units</b>: Pixel coordinates relative to
      * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
      * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on distortion correction
@@ -3577,7 +3611,10 @@
      * @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
      * @see CameraCharacteristics#SCALER_CROPPING_TYPE
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
      */
     @PublicKey
     @NonNull
@@ -4107,6 +4144,69 @@
             new Key<Integer>("android.sensor.dynamicWhiteLevel", int.class);
 
     /**
+     * <p>Switches sensor pixel mode between maximum resolution mode and default mode.</p>
+     * <p>This key controls whether the camera sensor operates in
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }
+     * mode or not. By default, all camera devices operate in
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT } mode.
+     * When operating in
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT } mode, sensors
+     * with {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * capability would typically perform pixel binning in order to improve low light
+     * performance, noise reduction etc. However, in
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }
+     * mode (supported only
+     * by {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * sensors), sensors typically operate in unbinned mode allowing for a larger image size.
+     * The stream configurations supported in
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }
+     * mode are also different from those of
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT } mode.
+     * They can be queried through
+     * {@link android.hardware.camera2.CameraCharacteristics#get } with
+     * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION) }.
+     * Unless reported by both
+     * {@link android.hardware.camera2.params.StreamConfigurationMap }s, the outputs from
+     * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION android.scaler.streamConfigurationMapMaximumResolution}</code> and
+     * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}</code>
+     * must not be mixed in the same CaptureRequest. In other words, these outputs are
+     * exclusive to each other.
+     * This key does not need to be set for reprocess requests.</p>
+     * <p><b>Possible values:</b></p>
+     * <ul>
+     *   <li>{@link #SENSOR_PIXEL_MODE_DEFAULT DEFAULT}</li>
+     *   <li>{@link #SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION MAXIMUM_RESOLUTION}</li>
+     * </ul>
+     *
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+     * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION
+     * @see #SENSOR_PIXEL_MODE_DEFAULT
+     * @see #SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION
+     */
+    @PublicKey
+    @NonNull
+    public static final Key<Integer> SENSOR_PIXEL_MODE =
+            new Key<Integer>("android.sensor.pixelMode", int.class);
+
+    /**
+     * <p>Whether <code>RAW</code> images requested have their bayer pattern as described by
+     * {@link CameraCharacteristics#SENSOR_INFO_BINNING_FACTOR android.sensor.info.binningFactor}.</p>
+     * <p>This key will only be present in devices advertisting the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+     * capability which also advertise <code>REMOSAIC_REPROCESSING</code> capability. On all other devices
+     * RAW targets will have a regular bayer pattern.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#SENSOR_INFO_BINNING_FACTOR
+     */
+    @PublicKey
+    @NonNull
+    public static final Key<Boolean> SENSOR_RAW_BINNING_FACTOR_USED =
+            new Key<Boolean>("android.sensor.rawBinningFactorUsed", boolean.class);
+
+    /**
      * <p>Quality of lens shading correction applied
      * to the image data.</p>
      * <p>When set to OFF mode, no lens shading correction will be applied by the
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index 0a42981..2920e67 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -20,6 +20,7 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession;
 import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.CameraOfflineSession;
 import android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallback;
 import android.hardware.camera2.CaptureRequest;
@@ -81,11 +82,17 @@
         if (request == null) {
             throw new IllegalArgumentException("Input capture request must not be null");
         }
+        CameraCharacteristics.Key<StreamConfigurationMap> ck =
+                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP;
+        Integer sensorPixelMode = request.get(CaptureRequest.SENSOR_PIXEL_MODE);
+        if (sensorPixelMode != null && sensorPixelMode ==
+                CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) {
+            ck = CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION;
+        }
         Collection<Surface> outputSurfaces = request.getTargets();
         Range<Integer> fpsRange = request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
 
-        StreamConfigurationMap config =
-                mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+        StreamConfigurationMap config = mCharacteristics.get(ck);
         SurfaceUtils.checkConstrainedHighSpeedSurfaces(outputSurfaces, fpsRange, config);
 
         // Request list size: to limit the preview to 30fps, need use maxFps/30; to maximize
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 4defd23..b578bf8 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -20,6 +20,7 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.graphics.ImageFormat;
 import android.hardware.ICameraService;
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCaptureSession;
@@ -62,6 +63,8 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
@@ -114,6 +117,7 @@
 
     private final String mCameraId;
     private final CameraCharacteristics mCharacteristics;
+    private final Map<String, CameraCharacteristics> mPhysicalIdsToChars;
     private final int mTotalPartialCount;
     private final Context mContext;
 
@@ -257,7 +261,9 @@
     };
 
     public CameraDeviceImpl(String cameraId, StateCallback callback, Executor executor,
-                        CameraCharacteristics characteristics, int appTargetSdkVersion,
+                        CameraCharacteristics characteristics,
+                        Map<String, CameraCharacteristics> physicalIdsToChars,
+                        int appTargetSdkVersion,
                         Context ctx) {
         if (cameraId == null || callback == null || executor == null || characteristics == null) {
             throw new IllegalArgumentException("Null argument given");
@@ -266,6 +272,7 @@
         mDeviceCallback = callback;
         mDeviceExecutor = executor;
         mCharacteristics = characteristics;
+        mPhysicalIdsToChars = physicalIdsToChars;
         mAppTargetSdkVersion = appTargetSdkVersion;
         mContext = ctx;
 
@@ -1357,11 +1364,71 @@
         }
     }
 
+    private boolean checkInputConfigurationWithStreamConfigurationsAs(
+            InputConfiguration inputConfig, StreamConfigurationMap configMap) {
+        int[] inputFormats = configMap.getInputFormats();
+        boolean validFormat = false;
+        int inputFormat = inputConfig.getFormat();
+        for (int format : inputFormats) {
+            if (format == inputFormat) {
+                validFormat = true;
+            }
+        }
+
+        if (validFormat == false) {
+            return false;
+        }
+
+        boolean validSize = false;
+        Size[] inputSizes = configMap.getInputSizes(inputFormat);
+        for (Size s : inputSizes) {
+            if (inputConfig.getWidth() == s.getWidth() &&
+                    inputConfig.getHeight() == s.getHeight()) {
+                validSize = true;
+            }
+        }
+
+        if (validSize == false) {
+            return false;
+        }
+        return true;
+    }
+
+    private boolean checkInputConfigurationWithStreamConfigurations(
+            InputConfiguration inputConfig, boolean maxResolution) {
+        // Check if either this logical camera or any of its physical cameras support the
+        // input config. If they do, the input config is valid.
+        CameraCharacteristics.Key<StreamConfigurationMap> ck =
+                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP;
+
+        if (maxResolution) {
+            ck = CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION;
+        }
+
+        StreamConfigurationMap configMap = mCharacteristics.get(ck);
+
+        if (configMap != null &&
+                checkInputConfigurationWithStreamConfigurationsAs(inputConfig, configMap)) {
+            return true;
+        }
+
+        for (Map.Entry<String, CameraCharacteristics> entry : mPhysicalIdsToChars.entrySet()) {
+            configMap = entry.getValue().get(ck);
+
+            if (configMap != null &&
+                    checkInputConfigurationWithStreamConfigurationsAs(inputConfig, configMap)) {
+                // Input config supported.
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void checkInputConfiguration(InputConfiguration inputConfig) {
         if (inputConfig == null) {
             return;
         }
-
+        int inputFormat = inputConfig.getFormat();
         if (inputConfig.isMultiResolution()) {
             MultiResolutionStreamConfigurationMap configMap = mCharacteristics.get(
                     CameraCharacteristics.SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP);
@@ -1369,19 +1436,19 @@
             int[] inputFormats = configMap.getInputFormats();
             boolean validFormat = false;
             for (int format : inputFormats) {
-                if (format == inputConfig.getFormat()) {
+                if (format == inputFormat) {
                     validFormat = true;
                 }
             }
 
             if (validFormat == false) {
                 throw new IllegalArgumentException("multi-resolution input format " +
-                        inputConfig.getFormat() + " is not valid");
+                        inputFormat + " is not valid");
             }
 
             boolean validSize = false;
             Collection<MultiResolutionStreamInfo> inputStreamInfo =
-                    configMap.getInputInfo(inputConfig.getFormat());
+                    configMap.getInputInfo(inputFormat);
             for (MultiResolutionStreamInfo info : inputStreamInfo) {
                 if (inputConfig.getWidth() == info.getWidth() &&
                         inputConfig.getHeight() == info.getHeight()) {
@@ -1394,34 +1461,11 @@
                         inputConfig.getWidth() + "x" + inputConfig.getHeight() + " is not valid");
             }
         } else {
-            StreamConfigurationMap configMap = mCharacteristics.get(
-                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
-
-            int[] inputFormats = configMap.getInputFormats();
-            boolean validFormat = false;
-            for (int format : inputFormats) {
-                if (format == inputConfig.getFormat()) {
-                    validFormat = true;
-                }
-            }
-
-            if (validFormat == false) {
-                throw new IllegalArgumentException("input format " + inputConfig.getFormat() +
-                        " is not valid");
-            }
-
-            boolean validSize = false;
-            Size[] inputSizes = configMap.getInputSizes(inputConfig.getFormat());
-            for (Size s : inputSizes) {
-                if (inputConfig.getWidth() == s.getWidth() &&
-                        inputConfig.getHeight() == s.getHeight()) {
-                    validSize = true;
-                }
-            }
-
-            if (validSize == false) {
-                throw new IllegalArgumentException("input size " + inputConfig.getWidth() + "x" +
-                        inputConfig.getHeight() + " is not valid");
+            if (!checkInputConfigurationWithStreamConfigurations(inputConfig, /*maxRes*/false) &&
+                    !checkInputConfigurationWithStreamConfigurations(inputConfig, /*maxRes*/true)) {
+                throw new IllegalArgumentException("Input config with format " +
+                        inputFormat + " and size " + inputConfig.getWidth() + "x" +
+                        inputConfig.getHeight() + " not supported by camera id " + mCameraId);
             }
         }
     }
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 0cdf744..aa84b02 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -327,6 +327,10 @@
     private static final String GPS_PROCESS = "GPS";
     private static final int FACE_LANDMARK_SIZE = 6;
 
+    private static final int MANDATORY_STREAM_CONFIGURATIONS_DEFAULT = 0;
+    private static final int MANDATORY_STREAM_CONFIGURATIONS_MAX_RESOLUTION = 1;
+    private static final int MANDATORY_STREAM_CONFIGURATIONS_CONCURRENT = 2;
+
     private static String translateLocationProviderToProcess(final String provider) {
         if (provider == null) {
             return null;
@@ -644,6 +648,15 @@
                         return (T) metadata.getStreamConfigurationMap();
                     }
                 });
+         sGetCommandMap.put(
+                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION.getNativeKey(),
+                        new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getStreamConfigurationMapMaximumResolution();
+                    }
+                });
         sGetCommandMap.put(
                 CameraCharacteristics.SCALER_MANDATORY_STREAM_COMBINATIONS.getNativeKey(),
                         new GetCommand() {
@@ -664,6 +677,16 @@
                 });
 
         sGetCommandMap.put(
+                CameraCharacteristics.SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS.getNativeKey(),
+                        new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getMandatoryMaximumResolutionStreamCombinations();
+                    }
+                });
+
+        sGetCommandMap.put(
                 CameraCharacteristics.CONTROL_MAX_REGIONS_AE.getNativeKey(), new GetCommand() {
                     @Override
                     @SuppressWarnings("unchecked")
@@ -1285,12 +1308,12 @@
         return recommendedConfigurations;
     }
 
-    private boolean isBurstSupported() {
+    private boolean isCapabilitySupported(int capabilityRequested) {
         boolean ret = false;
 
         int[] capabilities = getBase(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
         for (int capability : capabilities) {
-            if (capability == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE) {
+            if (capabilityRequested == capability) {
                 ret = true;
                 break;
             }
@@ -1299,8 +1322,18 @@
         return ret;
     }
 
+    private boolean isUltraHighResolutionSensor() {
+        return isCapabilitySupported(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR);
+
+    }
+    private boolean isBurstSupported() {
+        return isCapabilitySupported(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE);
+    }
+
     private MandatoryStreamCombination[] getMandatoryStreamCombinationsHelper(
-            boolean getConcurrent) {
+            int mandatoryStreamsType) {
         int[] capabilities = getBase(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
         ArrayList<Integer> caps = new ArrayList<Integer>();
         caps.ensureCapacity(capabilities.length);
@@ -1309,20 +1342,25 @@
         }
         int hwLevel = getBase(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
         MandatoryStreamCombination.Builder build = new MandatoryStreamCombination.Builder(
-                mCameraId, hwLevel, mDisplaySize, caps, getStreamConfigurationMap());
+                mCameraId, hwLevel, mDisplaySize, caps, getStreamConfigurationMap(),
+                getStreamConfigurationMapMaximumResolution());
 
         List<MandatoryStreamCombination> combs = null;
-        if (getConcurrent) {
-            combs = build.getAvailableMandatoryConcurrentStreamCombinations();
-        } else {
-            combs = build.getAvailableMandatoryStreamCombinations();
+        switch (mandatoryStreamsType) {
+            case MANDATORY_STREAM_CONFIGURATIONS_CONCURRENT:
+                combs = build.getAvailableMandatoryConcurrentStreamCombinations();
+                break;
+            case MANDATORY_STREAM_CONFIGURATIONS_MAX_RESOLUTION:
+                combs = build.getAvailableMandatoryMaximumResolutionStreamCombinations();
+                break;
+            default:
+                combs = build.getAvailableMandatoryStreamCombinations();
         }
         if ((combs != null) && (!combs.isEmpty())) {
             MandatoryStreamCombination[] combArray = new MandatoryStreamCombination[combs.size()];
             combArray = combs.toArray(combArray);
             return combArray;
         }
-
         return null;
     }
 
@@ -1330,11 +1368,18 @@
         if (!mHasMandatoryConcurrentStreams) {
             return null;
         }
-        return getMandatoryStreamCombinationsHelper(true);
+        return getMandatoryStreamCombinationsHelper(MANDATORY_STREAM_CONFIGURATIONS_CONCURRENT);
+    }
+
+    private MandatoryStreamCombination[] getMandatoryMaximumResolutionStreamCombinations() {
+        if (!isUltraHighResolutionSensor()) {
+            return null;
+        }
+        return getMandatoryStreamCombinationsHelper(MANDATORY_STREAM_CONFIGURATIONS_MAX_RESOLUTION);
     }
 
     private MandatoryStreamCombination[] getMandatoryStreamCombinations() {
-        return getMandatoryStreamCombinationsHelper(false);
+        return getMandatoryStreamCombinationsHelper(MANDATORY_STREAM_CONFIGURATIONS_DEFAULT);
     }
 
     private StreamConfigurationMap getStreamConfigurationMap() {
@@ -1377,6 +1422,50 @@
                 listHighResolution);
     }
 
+    private StreamConfigurationMap getStreamConfigurationMapMaximumResolution() {
+        if (!isUltraHighResolutionSensor()) {
+            return null;
+        }
+        StreamConfiguration[] configurations = getBase(
+                CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION);
+        StreamConfigurationDuration[] minFrameDurations = getBase(
+                CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION);
+        StreamConfigurationDuration[] stallDurations = getBase(
+                CameraCharacteristics.SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION);
+        StreamConfiguration[] depthConfigurations = getBase(
+                CameraCharacteristics.DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION);
+        StreamConfigurationDuration[] depthMinFrameDurations = getBase(
+                CameraCharacteristics.DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION);
+        StreamConfigurationDuration[] depthStallDurations = getBase(
+                CameraCharacteristics.DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION);
+        StreamConfiguration[] dynamicDepthConfigurations = getBase(
+                CameraCharacteristics.DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION);
+        StreamConfigurationDuration[] dynamicDepthMinFrameDurations = getBase(
+                CameraCharacteristics.DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION);
+        StreamConfigurationDuration[] dynamicDepthStallDurations = getBase(
+                CameraCharacteristics.DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION);
+        StreamConfiguration[] heicConfigurations = getBase(
+                CameraCharacteristics.HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION);
+        StreamConfigurationDuration[] heicMinFrameDurations = getBase(
+                CameraCharacteristics.HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION);
+        StreamConfigurationDuration[] heicStallDurations = getBase(
+                CameraCharacteristics.HEIC_AVAILABLE_HEIC_STALL_DURATIONS_MAXIMUM_RESOLUTION);
+        HighSpeedVideoConfiguration[] highSpeedVideoConfigurations = getBase(
+                CameraCharacteristics.CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS_MAXIMUM_RESOLUTION);
+        ReprocessFormatsMap inputOutputFormatsMap = getBase(
+                CameraCharacteristics.SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP_MAXIMUM_RESOLUTION);
+        // TODO: Is this correct, burst capability shouldn't necessarily correspond to max res mode
+        boolean listHighResolution = isBurstSupported();
+        return new StreamConfigurationMap(
+                configurations, minFrameDurations, stallDurations,
+                depthConfigurations, depthMinFrameDurations, depthStallDurations,
+                dynamicDepthConfigurations, dynamicDepthMinFrameDurations,
+                dynamicDepthStallDurations, heicConfigurations,
+                heicMinFrameDurations, heicStallDurations,
+                highSpeedVideoConfigurations, inputOutputFormatsMap,
+                listHighResolution, false);
+    }
+
     private <T> Integer getMaxRegions(Key<T> key) {
         final int AE = 0;
         final int AWB = 1;
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 8a0172e..34116aa 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -267,8 +267,8 @@
                 mStreamsInformation.hashCode());
     }
 
-    private static enum SizeThreshold { VGA, PREVIEW, RECORD, MAXIMUM, s720p, s1440p }
-    private static enum ReprocessType { NONE, PRIVATE, YUV }
+    private static enum SizeThreshold { VGA, PREVIEW, RECORD, MAXIMUM, s720p, s1440p, FULL_RES }
+    private static enum ReprocessType { NONE, PRIVATE, YUV, REMOSAIC }
     private static final class StreamTemplate {
         public int mFormat;
         public SizeThreshold mSizeThreshold;
@@ -691,6 +691,18 @@
                 "Depth capture for mesh based object rendering"),
     };
 
+    private static StreamCombinationTemplate sUltraHighResolutionStreamCombinations[] = {
+        new StreamCombinationTemplate(new StreamTemplate [] {
+                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES) },
+                "Full res YUV image capture"),
+        new StreamCombinationTemplate(new StreamTemplate [] {
+                new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.FULL_RES) },
+                "Full res RAW capture"),
+        new StreamCombinationTemplate(new StreamTemplate [] {
+                new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES) },
+                "Full res JPEG still image capture"),
+    };
+
     /**
      * Helper builder class to generate a list of available mandatory stream combinations.
      * @hide
@@ -700,6 +712,7 @@
         private List<Integer> mCapabilities;
         private int mHwLevel, mCameraId;
         private StreamConfigurationMap mStreamConfigMap;
+        private StreamConfigurationMap mStreamConfigMapMaximumResolution;
         private boolean mIsHiddenPhysicalCamera;
 
         private final Size kPreviewSizeBound = new Size(1920, 1088);
@@ -712,13 +725,17 @@
          * @param displaySize The device display size.
          * @param capabilities The camera device capabilities.
          * @param sm The camera device stream configuration map.
+         * @param smMaxResolution The camera device stream configuration map when it runs in max
+         *                        resolution mode.
          */
         public Builder(int cameraId, int hwLevel, @NonNull Size displaySize,
-                @NonNull List<Integer> capabilities, @NonNull StreamConfigurationMap sm) {
+                @NonNull List<Integer> capabilities, @NonNull StreamConfigurationMap sm,
+                StreamConfigurationMap smMaxResolution) {
             mCameraId = cameraId;
             mDisplaySize = displaySize;
             mCapabilities = capabilities;
             mStreamConfigMap = sm;
+            mStreamConfigMapMaximumResolution = smMaxResolution;
             mHwLevel = hwLevel;
             mIsHiddenPhysicalCamera =
                     CameraManager.isHiddenPhysicalCamera(Integer.toString(mCameraId));
@@ -797,6 +814,97 @@
         }
 
         /**
+         * Retrieve a list of all available mandatory stream combinations supported when
+         * {@link CaptureRequest#ANDROID_SENSOR_PIXEL_MODE} is set to
+         * {@link CameraMetadata#ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION}.
+         *
+         * @return a non-modifiable list of supported mandatory stream combinations or
+         *         null in case device is not backward compatible or the method encounters
+         *         an error.
+         */
+        public @NonNull List<MandatoryStreamCombination>
+                getAvailableMandatoryMaximumResolutionStreamCombinations() {
+
+            ArrayList<StreamCombinationTemplate> chosenStreamCombinations =
+                    new ArrayList<StreamCombinationTemplate>();
+
+            chosenStreamCombinations.addAll(Arrays.asList(sUltraHighResolutionStreamCombinations));
+
+            ArrayList<MandatoryStreamCombination> availableStreamCombinations =
+                    new ArrayList<MandatoryStreamCombination>();
+            boolean addRemosaicReprocessing = isRemosaicReprocessingSupported();
+            int remosaicSize = 0;
+            if (addRemosaicReprocessing) {
+                remosaicSize = 1;
+            }
+            availableStreamCombinations.ensureCapacity(
+                    chosenStreamCombinations.size() + remosaicSize);
+            fillMandatoryOutputStreamCombinations(availableStreamCombinations,
+                    chosenStreamCombinations, mStreamConfigMapMaximumResolution);
+            if (isRemosaicReprocessingSupported()) {
+                // Add reprocess mandatory streams
+                ArrayList<MandatoryStreamInformation> streamsInfo =
+                        new ArrayList<MandatoryStreamInformation>();
+
+                ArrayList<Size> inputSize = new ArrayList<Size>();
+                Size maxRawInputSize = getMaxSize(mStreamConfigMapMaximumResolution.getInputSizes(
+                        ImageFormat.RAW_SENSOR));
+                inputSize.add(maxRawInputSize);
+
+                streamsInfo.add(new MandatoryStreamInformation(inputSize,
+                        ImageFormat.RAW_SENSOR, /*isInput*/true));
+                streamsInfo.add(new MandatoryStreamInformation(inputSize,
+                        ImageFormat.RAW_SENSOR));
+                MandatoryStreamCombination streamCombination;
+                streamCombination = new MandatoryStreamCombination(streamsInfo,
+                        "Remosaic reprocessing", true);
+                availableStreamCombinations.add(streamCombination);
+            }
+            return Collections.unmodifiableList(availableStreamCombinations);
+        }
+
+        private void fillMandatoryOutputStreamCombinations(
+                ArrayList<MandatoryStreamCombination> availableStreamCombinations,
+                ArrayList<StreamCombinationTemplate> chosenStreamCombinations,
+                StreamConfigurationMap streamConfigMap) {
+
+            for (StreamCombinationTemplate combTemplate : chosenStreamCombinations) {
+                ArrayList<MandatoryStreamInformation> streamsInfo =
+                        new ArrayList<MandatoryStreamInformation>();
+                streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
+
+                for (StreamTemplate template : combTemplate.mStreamTemplates) {
+                    MandatoryStreamInformation streamInfo;
+                    List<Size> sizes = new ArrayList<Size>();
+                    Size sizeChosen =
+                            getMaxSize(streamConfigMap.getOutputSizes(
+                                    template.mFormat));
+                    sizes.add(sizeChosen);
+                    try {
+                        streamInfo = new MandatoryStreamInformation(sizes, template.mFormat);
+                    } catch (IllegalArgumentException e) {
+                        String cause = "No available sizes found for format: " + template.mFormat
+                                + " size threshold: " + template.mSizeThreshold + " combination: "
+                                + combTemplate.mDescription;
+                        throw new RuntimeException(cause, e);
+                    }
+                    streamsInfo.add(streamInfo);
+                }
+
+                MandatoryStreamCombination streamCombination;
+                try {
+                    streamCombination = new MandatoryStreamCombination(streamsInfo,
+                            combTemplate.mDescription, /*isReprocess*/false);
+                } catch (IllegalArgumentException e) {
+                    String cause =  "No stream information for mandatory combination: "
+                            + combTemplate.mDescription;
+                    throw new RuntimeException(cause, e);
+                }
+                availableStreamCombinations.add(streamCombination);
+            }
+        }
+
+        /**
          * Retrieve a list of all available mandatory stream combinations.
          *
          * @return a non-modifiable list of supported mandatory stream combinations or
@@ -948,7 +1056,6 @@
                         inputSize.add(maxYUVInputSize);
                         format = ImageFormat.YUV_420_888;
                     }
-
                     streamsInfo.add(new MandatoryStreamInformation(inputSize, format,
                                 /*isInput*/true));
                     streamsInfo.add(new MandatoryStreamInformation(inputSize, format));
@@ -974,7 +1081,6 @@
                                 combTemplate.mDescription);
                         return null;
                     }
-
                     streamsInfo.add(streamInfo);
                 }
 
@@ -1220,6 +1326,14 @@
         }
 
         /**
+         * Check whether the current device supports YUV reprocessing.
+         */
+        private boolean isRemosaicReprocessingSupported() {
+            return isCapabilitySupported(
+                    CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING);
+        }
+
+        /**
          * Return the maximum supported video size using the camcorder profile information.
          *
          * @return Maximum supported video size.
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index e31bd60..84736dc 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -19,6 +19,7 @@
 
 import static com.android.internal.util.Preconditions.*;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -26,6 +27,7 @@
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.MultiResolutionImageReader;
 import android.hardware.camera2.params.MultiResolutionStreamInfo;
 import android.hardware.camera2.utils.HashCodeHelpers;
@@ -33,10 +35,13 @@
 import android.media.ImageReader;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.Size;
 import android.view.Surface;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -145,6 +150,13 @@
      */
     public static final int SURFACE_GROUP_ID_NONE = -1;
 
+    /** @hide */
+     @Retention(RetentionPolicy.SOURCE)
+     @IntDef(prefix = {"SENSOR_PIXEL_MODE_"}, value =
+         {CameraMetadata.SENSOR_PIXEL_MODE_DEFAULT,
+          CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION})
+     public @interface SensorPixelMode {};
+
     /**
      * Create a new {@link OutputConfiguration} instance with a {@link Surface}.
      *
@@ -306,6 +318,7 @@
         mIsShared = false;
         mPhysicalCameraId = null;
         mIsMultiResolution = false;
+        mSensorPixelModesUsed = new ArrayList<Integer>();
     }
 
     /**
@@ -399,6 +412,7 @@
         mIsShared = false;
         mPhysicalCameraId = null;
         mIsMultiResolution = false;
+        mSensorPixelModesUsed = new ArrayList<Integer>();
     }
 
     /**
@@ -485,6 +499,81 @@
     }
 
     /**
+     * Add a sensor pixel mode that this OutputConfiguration will be used in.
+     *
+     * <p> In the case that this output stream configuration (format, width, height) is
+     * available through {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}
+     * configurations and
+     * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION},
+     * configurations, the camera sub-system will assume that this {@link OutputConfiguration} will
+     * be used only with {@link android.hardware.camera2.CaptureRequest}s which has
+     * {@link android.hardware.camera2.CaptureRequest#SENSOR_PIXEL_MODE} set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT}.
+     * In such cases, if clients intend to use the
+     * {@link OutputConfiguration}(s) in a {@link android.hardware.camera2.CaptureRequest} with
+     * other sensor pixel modes, they must specify which
+     * {@link android.hardware.camera2.CaptureRequest#SENSOR_PIXEL_MODE}(s) they will use this
+     * {@link OutputConfiguration} with, by calling this method.
+     *
+     * In case this output stream configuration (format, width, height) is only in
+     * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION},
+     * configurations, this output target must only be used with
+     * {@link android.hardware.camera2.CaptureRequest}s which has
+     * {@link android.hardware.camera2.CaptureRequest#SENSOR_PIXEL_MODE} set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION} and that
+     * is what the camera sub-system will assume. If clients add
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT} in this
+     * case, session configuration will fail, if this {@link OutputConfiguration} is included.
+     *
+     * In case this output stream configuration (format, width, height) is only in
+     * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP},
+     * configurations, this output target must only be used with
+     * {@link android.hardware.camera2.CaptureRequest}s which has
+     * {@link android.hardware.camera2.CaptureRequest#SENSOR_PIXEL_MODE} set to
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT} and that is what
+     * the camera sub-system will assume. If clients add
+     * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION} in this
+     * case, session configuration will fail, if this {@link OutputConfiguration} is included.
+     *
+     * @param sensorPixelModeUsed The sensor pixel mode this OutputConfiguration will be used with
+     * </p>
+     *
+     */
+    public void addSensorPixelModeUsed(@SensorPixelMode int sensorPixelModeUsed) {
+        // Verify that the values are in range.
+        if (sensorPixelModeUsed != CameraMetadata.SENSOR_PIXEL_MODE_DEFAULT &&
+                sensorPixelModeUsed != CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) {
+            throw new IllegalArgumentException("Not a valid sensor pixel mode " +
+                    sensorPixelModeUsed);
+        }
+
+        if (mSensorPixelModesUsed.contains(sensorPixelModeUsed)) {
+            // Already added, ignore;
+            return;
+        }
+        mSensorPixelModesUsed.add(sensorPixelModeUsed);
+    }
+
+    /**
+     * Remove a sensor pixel mode, previously added through addSensorPixelModeUsed, from this
+     * OutputConfiguration.
+     *
+     * <p> Sensor pixel modes added via calls to {@link #addSensorPixelModeUsed} can also be removed
+     * from the OutputConfiguration.</p>
+     *
+     * @param sensorPixelModeUsed The sensor pixel mode to be removed.
+     *
+     * @throws IllegalArgumentException If the sensor pixel mode wasn't previously added
+     *                                  through {@link #addSensorPixelModeUsed}.
+     */
+    public void removeSensorPixelModeUsed(@SensorPixelMode int sensorPixelModeUsed) {
+      if (!mSensorPixelModesUsed.remove(Integer.valueOf(sensorPixelModeUsed))) {
+            throw new IllegalArgumentException("sensorPixelMode " + sensorPixelModeUsed +
+                    "is not part of this output configuration");
+      }
+    }
+
+    /**
      * Check if this configuration is for a physical camera.
      *
      * <p>This returns true if the output configuration was for a physical camera making up a
@@ -625,6 +714,7 @@
         this.mIsShared = other.mIsShared;
         this.mPhysicalCameraId = other.mPhysicalCameraId;
         this.mIsMultiResolution = other.mIsMultiResolution;
+        this.mSensorPixelModesUsed = other.mSensorPixelModesUsed;
     }
 
     /**
@@ -642,7 +732,8 @@
         source.readTypedList(surfaces, Surface.CREATOR);
         String physicalCameraId = source.readString();
         boolean isMultiResolutionOutput = source.readInt() == 1;
-
+        ArrayList<Integer> sensorPixelModesUsed = new ArrayList<Integer>();
+        source.readList(sensorPixelModesUsed, Integer.class.getClassLoader());
         checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
 
         mSurfaceGroupId = surfaceSetId;
@@ -666,6 +757,7 @@
         }
         mPhysicalCameraId = physicalCameraId;
         mIsMultiResolution = isMultiResolutionOutput;
+        mSensorPixelModesUsed = sensorPixelModesUsed;
     }
 
     /**
@@ -766,6 +858,7 @@
         dest.writeTypedList(mSurfaces);
         dest.writeString(mPhysicalCameraId);
         dest.writeInt(mIsMultiResolution ? 1 : 0);
+        dest.writeList(mSensorPixelModesUsed);
     }
 
     /**
@@ -798,7 +891,14 @@
                     !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId) ||
                     mIsMultiResolution != other.mIsMultiResolution)
                 return false;
-
+            if (mSensorPixelModesUsed.size() != other.mSensorPixelModesUsed.size()) {
+                return false;
+            }
+            for (int j = 0; j < mSensorPixelModesUsed.size(); j++) {
+                if (mSensorPixelModesUsed.get(j) != other.mSensorPixelModesUsed.get(j)) {
+                    return false;
+                }
+            }
             int minLen = Math.min(mSurfaces.size(), other.mSurfaces.size());
             for (int i = 0;  i < minLen; i++) {
                 if (mSurfaces.get(i) != other.mSurfaces.get(i))
@@ -823,7 +923,7 @@
                     mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace,
                     mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0,
                     mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
-                    mIsMultiResolution ? 1 : 0);
+                    mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode());
         }
 
         return HashCodeHelpers.hashCode(
@@ -831,7 +931,7 @@
                 mConfiguredSize.hashCode(), mConfiguredFormat,
                 mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0,
                 mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
-                mIsMultiResolution ? 1 : 0);
+                mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode());
     }
 
     private static final String TAG = "OutputConfiguration";
@@ -861,4 +961,6 @@
     // Flag indicating if this config is for a multi-resolution output with a
     // MultiResolutionImageReader
     private boolean mIsMultiResolution;
+    // The sensor pixel modes that this OutputConfiguration will use
+    private ArrayList<Integer> mSensorPixelModesUsed;
 }
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 6dd6744..2430c09 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -19,6 +19,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import android.Manifest;
+import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.LongDef;
 import android.annotation.NonNull;
@@ -921,6 +922,43 @@
         mGlobal.setTemporaryBrightness(displayId, brightness);
     }
 
+
+    /**
+     * Sets the brightness of the specified display.
+     * <p>
+     * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS}
+     * permission.
+     * </p>
+     *
+     * @param displayId the logical display id
+     * @param brightness The brightness value from 0.0f to 1.0f.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS)
+    public void setBrightness(int displayId, @FloatRange(from = 0f, to = 1f) float brightness) {
+        mGlobal.setBrightness(displayId, brightness);
+    }
+
+
+    /**
+     * Gets the brightness of the specified display.
+     * <p>
+     * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS}
+     * permission.
+     * </p>
+     *
+     * @param displayId The display of which brightness value to get from.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS)
+    @FloatRange(from = 0f, to = 1f)
+    public float getBrightness(int displayId) {
+        return mGlobal.getBrightness(displayId);
+    }
+
+
     /**
      * Temporarily sets the auto brightness adjustment factor.
      * <p>
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index fd0431c5..06efc4f 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -690,6 +690,37 @@
         }
     }
 
+
+    /**
+     * Sets the brightness of the display.
+     *
+     * @param brightness The brightness value from 0.0f to 1.0f.
+     *
+     * @hide
+     */
+    public void setBrightness(int displayId, float brightness) {
+        try {
+            mDm.setBrightness(displayId, brightness);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the brightness of the display.
+     *
+     * @param displayId The display from which to get the brightness
+     *
+     * @hide
+     */
+    public float getBrightness(int displayId) {
+        try {
+            return mDm.getBrightness(displayId);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Temporarily sets the auto brightness adjustment factor.
      * <p>
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index dee9144..3538ff1 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -119,6 +119,12 @@
     // Temporarily sets the display brightness.
     void setTemporaryBrightness(int displayId, float brightness);
 
+    // Saves the display brightness.
+    void setBrightness(int displayId, float brightness);
+
+    // Retrieves the display brightness.
+    float getBrightness(int displayId);
+
     // Temporarily sets the auto brightness adjustment factor.
     void setTemporaryAutoBrightnessAdjustment(float adjustment);
 
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 01fd396..c83ccfa 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -223,6 +223,14 @@
     public static final long BLOCK_FLAG_SLIPPERY = android.os.IInputConstants.BLOCK_FLAG_SLIPPERY;
 
     /**
+     * Check whether apps are using MotionEvent.getRawX/Y. This is implementation-specific, and
+     * thus undefined for most 3p app usages.
+     * @hide
+     */
+    @ChangeId
+    public static final long APP_USES_RAW_INPUT_COORDS = 179274888L;
+
+    /**
      * Input Event Injection Synchronization Mode: None.
      * Never blocks.  Injection is asynchronous and is assumed always to be successful.
      * @hide
diff --git a/core/java/android/net/netstats/provider/NetworkStatsProvider.java b/core/java/android/net/netstats/provider/NetworkStatsProvider.java
index 65b336a..23fc069 100644
--- a/core/java/android/net/netstats/provider/NetworkStatsProvider.java
+++ b/core/java/android/net/netstats/provider/NetworkStatsProvider.java
@@ -147,10 +147,7 @@
 
     /**
      * Notify system that the warning set by {@link #onSetWarningAndLimit} has been reached.
-     *
-     * @hide
      */
-    // TODO: Expose as system API.
     public void notifyWarningReached() {
         try {
             // Reuse the code path to notify warning reached with limit reached
@@ -198,7 +195,6 @@
      * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
      *                   from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no limit.
      */
-    // TODO: deprecate this once onSetWarningAndLimit is ready.
     public abstract void onSetLimit(@NonNull String iface, long quotaBytes);
 
     /**
@@ -217,10 +213,7 @@
      *                     there is no warning.
      * @param limitBytes the limit defined as the number of bytes, starting from zero and counting
      *                   from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no limit.
-     *
-     * @hide
      */
-    // TODO: Expose as system API.
     public void onSetWarningAndLimit(@NonNull String iface, long warningBytes, long limitBytes) {
         // Backward compatibility for those who didn't override this function.
         onSetLimit(iface, limitBytes);
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 4674aa2..c47fc57 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1004,6 +1004,24 @@
         public abstract long getCpuMeasuredBatteryConsumptionUC();
 
         /**
+         * Returns the battery consumption (in microcoulombs) of the uid's GNSS usage, derived from
+         * on device power measurement data.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getGnssMeasuredBatteryConsumptionUC();
+
+        /**
+         * Returns the battery consumption (in microcoulombs) of the uid's radio usage, derived from
+         * on device power measurement data.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getMobileRadioMeasuredBatteryConsumptionUC();
+
+        /**
          * Returns the battery consumption (in microcoulombs) of the screen while on and uid active,
          * derived from on device power measurement data.
          * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
@@ -2548,6 +2566,24 @@
     public abstract long getCpuMeasuredBatteryConsumptionUC();
 
     /**
+     * Returns the battery consumption (in microcoulombs) of the GNSS, derived from on device power
+     * measurement data.
+     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+     *
+     * {@hide}
+     */
+    public abstract long getGnssMeasuredBatteryConsumptionUC();
+
+    /**
+     * Returns the battery consumption (in microcoulombs) of the radio, derived from on device power
+     * measurement data.
+     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+     *
+     * {@hide}
+     */
+    public abstract long getMobileRadioMeasuredBatteryConsumptionUC();
+
+    /**
      * Returns the battery consumption (in microcoulombs) of the screen while on, derived from on
      * device power measurement data.
      * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index 9518bf1..85861bc 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -60,14 +60,18 @@
      */
     public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY = 2;
 
+    private static final long DEFAULT_MAX_STATS_AGE_MS = 5 * 60 * 1000;
+
     private final int mFlags;
     @NonNull
     private final int[] mUserIds;
+    private final long mMaxStatsAgeMs;
 
     private BatteryUsageStatsQuery(@NonNull Builder builder) {
         mFlags = builder.mFlags;
         mUserIds = builder.mUserIds != null ? builder.mUserIds.toArray()
                 : new int[]{UserHandle.USER_ALL};
+        mMaxStatsAgeMs = builder.mMaxStatsAgeMs;
     }
 
     @BatteryUsageStatsFlags
@@ -94,10 +98,19 @@
         return (mFlags & FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL) != 0;
     }
 
+    /**
+     * Returns the client's tolerance for stale battery stats. The data is allowed to be up to
+     * this many milliseconds out-of-date.
+     */
+    public long getMaxStatsAge() {
+        return mMaxStatsAgeMs;
+    }
+
     private BatteryUsageStatsQuery(Parcel in) {
         mFlags = in.readInt();
         mUserIds = new int[in.readInt()];
         in.readIntArray(mUserIds);
+        mMaxStatsAgeMs = in.readLong();
     }
 
     @Override
@@ -105,6 +118,7 @@
         dest.writeInt(mFlags);
         dest.writeInt(mUserIds.length);
         dest.writeIntArray(mUserIds);
+        dest.writeLong(mMaxStatsAgeMs);
     }
 
     @Override
@@ -132,6 +146,7 @@
     public static final class Builder {
         private int mFlags;
         private IntArray mUserIds;
+        private long mMaxStatsAgeMs = DEFAULT_MAX_STATS_AGE_MS;
 
         /**
          * Builds a read-only BatteryUsageStatsQuery object.
@@ -170,5 +185,14 @@
             mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL;
             return this;
         }
+
+        /**
+         * Set the client's tolerance for stale battery stats. The data may be up to
+         * this many milliseconds out-of-date.
+         */
+        public Builder setMaxStatsAgeMs(long maxStatsAgeMs) {
+            mMaxStatsAgeMs = maxStatsAgeMs;
+            return this;
+        }
     }
 }
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 7669586cf..baa25f0 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -80,6 +80,9 @@
     private static final String LOG_TAG = PermissionManager.class.getName();
 
     /** @hide */
+    public static final String LOG_TAG_TRACE_GRANTS = "PermissionGrantTrace";
+
+    /** @hide */
     public static final String KILL_APP_REASON_PERMISSIONS_REVOKED =
             "permissions revoked";
     /** @hide */
@@ -103,6 +106,8 @@
      * Note: Changing this won't do anything on its own - you should also change the filtering in
      * {@link #shouldTraceGrant}.
      *
+     * See log output for tag {@link #LOG_TAG_TRACE_GRANTS}
+     *
      * @hide
      */
     public static final boolean DEBUG_TRACE_GRANTS = false;
@@ -319,8 +324,10 @@
     }
 
     /** @hide */
-    public static boolean shouldTraceGrant(String packageName, String permissionName, int userId) {
+    public static boolean shouldTraceGrant(
+            @NonNull String packageName, @NonNull String permissionName, int userId) {
         // To be modified when debugging
+        // template: if ("".equals(packageName) && "".equals(permissionName)) return true;
         return false;
     }
 
@@ -348,7 +355,8 @@
             @NonNull String permissionName, @NonNull UserHandle user) {
         if (DEBUG_TRACE_GRANTS
                 && shouldTraceGrant(packageName, permissionName, user.getIdentifier())) {
-            Log.i(LOG_TAG, "App " + mContext.getPackageName() + " is granting " + packageName + " "
+            Log.i(LOG_TAG_TRACE_GRANTS, "App " + mContext.getPackageName() + " is granting "
+                    + packageName + " "
                     + permissionName + " for user " + user.getIdentifier(), new RuntimeException());
         }
         try {
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 31cf63c..03b5a2e 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -257,6 +257,14 @@
     public static final String NAMESPACE_JOB_SCHEDULER = "jobscheduler";
 
     /**
+     * Namespace for all media related features.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_MEDIA = "media";
+
+    /**
      * Namespace for all media native related features.
      *
      * @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 61fbef5..5ab462b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -470,8 +470,8 @@
      * to be shown, with the "package" scheme. That is "package:com.my.app".
      * <p>
      * Output: Nothing.
-     * @hide
      */
+    @SuppressLint("ActionValue")
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_APP_OPEN_BY_DEFAULT_SETTINGS =
             "com.android.settings.APP_OPEN_BY_DEFAULT_SETTINGS";
@@ -6291,6 +6291,20 @@
                 "selected_input_method_subtype";
 
         /**
+         * The {@link android.view.inputmethod.InputMethodInfo.InputMethodInfo#getId() ID} of the
+         * default voice input method.
+         * <p>
+         * This stores the last known default voice IME. If the related system config value changes,
+         * this is reset by InputMethodManagerService.
+         * <p>
+         * This IME is not necessarily in the enabled IME list. That state is still stored in
+         * {@link #ENABLED_INPUT_METHODS}.
+         *
+         * @hide
+         */
+        public static final String DEFAULT_VOICE_INPUT_METHOD = "default_voice_input_method";
+
+        /**
          * Setting to record the history of input method subtype, holding the pair of ID of IME
          * and its last used subtype.
          * @hide
@@ -9162,6 +9176,22 @@
         public static final String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
 
         /**
+         * Whether the assistant can be triggered by a touch gesture.
+         *
+         * @hide
+         */
+        public static final String ASSIST_TOUCH_GESTURE_ENABLED =
+                "assist_touch_gesture_enabled";
+
+        /**
+         * Whether the assistant can be triggered by long-pressing the home button
+         *
+         * @hide
+         */
+        public static final String ASSIST_LONG_PRESS_HOME_ENABLED =
+                "assist_long_press_home_enabled";
+
+        /**
          * Control whether Trust Agents are in active unlock or extend unlock mode.
          * @hide
          */
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 04a4ca4..13274c6 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -38,6 +38,8 @@
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
 
+import com.android.internal.os.IResultReceiver;
+
 /**
  * An {@code AutofillService} is a service used to automatically fill the contents of the screen
  * on behalf of a given user - for more information about autofill, read
@@ -575,6 +577,20 @@
      */
     public static final String SERVICE_META_DATA = "android.autofill";
 
+    /**
+     * Name of the {@link IResultReceiver} extra used to return the primary result of a request.
+     *
+     * @hide
+     */
+    public static final String EXTRA_RESULT = "result";
+
+    /**
+     * Name of the {@link IResultReceiver} extra used to return the error reason of a request.
+     *
+     * @hide
+     */
+    public static final String EXTRA_ERROR = "error";
+
     private final IAutoFillService mInterface = new IAutoFillService.Stub() {
         @Override
         public void onConnectedStateChanged(boolean connected) {
@@ -603,6 +619,14 @@
                     AutofillService::onSaveRequest,
                     AutofillService.this, request, new SaveCallback(callback)));
         }
+
+        @Override
+        public void onSavedPasswordCountRequest(IResultReceiver receiver) {
+            mHandler.sendMessage(obtainMessage(
+                    AutofillService::onSavedDatasetsInfoRequest,
+                    AutofillService.this,
+                    new SavedDatasetsInfoCallbackImpl(receiver, SavedDatasetsInfo.TYPE_PASSWORDS)));
+        }
     };
 
     private Handler mHandler;
@@ -673,6 +697,19 @@
             @NonNull SaveCallback callback);
 
     /**
+     * Called from system settings to display information about the datasets the user saved to this
+     * service.
+     *
+     * <p>There is no timeout for the request, but it's recommended to return the result within a
+     * few seconds, or the user may navigate away from the activity that would display the result.
+     *
+     * @param callback callback for responding to the request
+     */
+    public void onSavedDatasetsInfoRequest(@NonNull SavedDatasetsInfoCallback callback) {
+        callback.onError(SavedDatasetsInfoCallback.ERROR_UNSUPPORTED);
+    }
+
+    /**
      * Called when the Android system disconnects from the service.
      *
      * <p> At this point this service may no longer be an active {@link AutofillService}.
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index 23a1a3f..d88e094 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -31,4 +31,5 @@
     void onConnectedStateChanged(boolean connected);
     void onFillRequest(in FillRequest request, in IFillCallback callback);
     void onSaveRequest(in SaveRequest request, in ISaveCallback callback);
+    void onSavedPasswordCountRequest(in IResultReceiver receiver);
 }
diff --git a/core/java/android/service/autofill/SavedDatasetsInfo.java b/core/java/android/service/autofill/SavedDatasetsInfo.java
new file mode 100644
index 0000000..6a4d2b8
--- /dev/null
+++ b/core/java/android/service/autofill/SavedDatasetsInfo.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.StringDef;
+
+import com.android.internal.util.DataClass;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A result returned from
+ * {@link AutofillService#onSavedDatasetsInfoRequest(SavedDatasetsInfoCallback)}.
+ */
+@DataClass(
+        genToString = true,
+        genHiddenConstDefs = true,
+        genEqualsHashCode = true)
+public final class SavedDatasetsInfo {
+
+    /**
+     * Any other type of datasets.
+     */
+    public static final String TYPE_OTHER = "other";
+
+    /**
+     * Datasets such as login credentials.
+     */
+    public static final String TYPE_PASSWORDS = "passwords";
+
+    /**
+     * The type of the datasets that this info is about.
+     */
+    @NonNull
+    @Type
+    private final String mType;
+
+    /**
+     * The number of datasets of {@link #getType() this type} that the user has saved to the
+     * service.
+     */
+    @IntRange(from = 0)
+    private final int mCount;
+
+
+    // Code below generated by codegen v1.0.22.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/autofill/SavedDatasetsInfo.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /** @hide */
+    @StringDef(prefix = "TYPE_", value = {
+        TYPE_OTHER,
+        TYPE_PASSWORDS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @DataClass.Generated.Member
+    public @interface Type {}
+
+    /**
+     * Creates a new SavedDatasetsInfo.
+     *
+     * @param type
+     *   The type of the datasets.
+     * @param count
+     *   The number of datasets of this type that the user has saved to the service.
+     */
+    @DataClass.Generated.Member
+    public SavedDatasetsInfo(
+            @NonNull @Type String type,
+            @IntRange(from = 0) int count) {
+        this.mType = type;
+
+        if (!(java.util.Objects.equals(mType, TYPE_OTHER))
+                && !(java.util.Objects.equals(mType, TYPE_PASSWORDS))) {
+            throw new java.lang.IllegalArgumentException(
+                    "type was " + mType + " but must be one of: "
+                            + "TYPE_OTHER(" + TYPE_OTHER + "), "
+                            + "TYPE_PASSWORDS(" + TYPE_PASSWORDS + ")");
+        }
+
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mType);
+        this.mCount = count;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mCount,
+                "from", 0);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The type of the datasets.
+     */
+    @DataClass.Generated.Member
+    public @NonNull @Type String getType() {
+        return mType;
+    }
+
+    /**
+     * The number of datasets of this type that the user has saved to the service.
+     */
+    @DataClass.Generated.Member
+    public @IntRange(from = 0) int getCount() {
+        return mCount;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "SavedDatasetsInfo { " +
+                "type = " + mType + ", " +
+                "count = " + mCount +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(SavedDatasetsInfo other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        SavedDatasetsInfo that = (SavedDatasetsInfo) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mType, that.mType)
+                && mCount == that.mCount;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mType);
+        _hash = 31 * _hash + mCount;
+        return _hash;
+    }
+
+    @DataClass.Generated(
+            time = 1615325704446L,
+            codegenVersion = "1.0.22",
+            sourceFile = "frameworks/base/core/java/android/service/autofill/SavedDatasetsInfo.java",
+            inputSignatures = "public static final  java.lang.String TYPE_OTHER\npublic static final  java.lang.String TYPE_PASSWORDS\nprivate final @android.annotation.NonNull @android.service.autofill.SavedDatasetsInfo.Type java.lang.String mType\nprivate final @android.annotation.IntRange int mCount\nclass SavedDatasetsInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/service/autofill/SavedDatasetsInfoCallback.java b/core/java/android/service/autofill/SavedDatasetsInfoCallback.java
new file mode 100644
index 0000000..a47105a
--- /dev/null
+++ b/core/java/android/service/autofill/SavedDatasetsInfoCallback.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Set;
+
+/**
+ * Handles the response to
+ * {@link AutofillService#onSavedDatasetsInfoRequest(SavedDatasetsInfoCallback)}.
+ * <p>
+ * Use {@link #onSuccess(Set)} to return the computed info about the datasets the user saved to this
+ * service. If there was an error querying the info, or if the service is unable to do so at this
+ * time (for example, if the user isn't logged in), call {@link #onError(int)}.
+ * <p>
+ * This callback can be used only once.
+ */
+public interface SavedDatasetsInfoCallback {
+
+    /** @hide */
+    @IntDef(prefix = {"ERROR_"}, value = {
+            ERROR_OTHER,
+            ERROR_UNSUPPORTED,
+            ERROR_NEEDS_USER_ACTION
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface Error {
+    }
+
+    /**
+     * The result could not be computed for any other reason.
+     */
+    int ERROR_OTHER = 0;
+    /**
+     * The service does not support this request.
+     */
+    int ERROR_UNSUPPORTED = 1;
+    /**
+     * The result cannot be computed until the user takes some action, such as setting up their
+     * account.
+     */
+    int ERROR_NEEDS_USER_ACTION = 2;
+
+    /**
+     * Successfully respond to the request with the info on each type of saved datasets.
+     */
+    void onSuccess(@NonNull Set<SavedDatasetsInfo> results);
+
+    /**
+     * Respond to the request with an error. System settings may display a suitable notice to the
+     * user.
+     */
+    void onError(@Error int error);
+}
diff --git a/core/java/android/service/autofill/SavedDatasetsInfoCallbackImpl.java b/core/java/android/service/autofill/SavedDatasetsInfoCallbackImpl.java
new file mode 100644
index 0000000..b8a8cde
--- /dev/null
+++ b/core/java/android/service/autofill/SavedDatasetsInfoCallbackImpl.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import static android.service.autofill.AutofillService.EXTRA_ERROR;
+import static android.service.autofill.AutofillService.EXTRA_RESULT;
+
+import static com.android.internal.util.Preconditions.checkArgumentInRange;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.os.DeadObjectException;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.os.IResultReceiver;
+
+import java.util.Set;
+
+final class SavedDatasetsInfoCallbackImpl implements SavedDatasetsInfoCallback {
+    private static final String TAG = "AutofillService";
+
+    @NonNull
+    private final IResultReceiver mReceiver;
+    @NonNull
+    private final String mType;
+
+    /**
+     * Creates a {@link SavedDatasetsInfoCallback} that returns the {@link
+     * SavedDatasetsInfo#getCount() number} of saved datasets of {@code type} to the {@code
+     * receiver}.
+     */
+    SavedDatasetsInfoCallbackImpl(@NonNull IResultReceiver receiver, @NonNull String type) {
+        mReceiver = requireNonNull(receiver);
+        mType = requireNonNull(type);
+    }
+
+    @Override
+    public void onSuccess(@NonNull Set<SavedDatasetsInfo> results) {
+        requireNonNull(results);
+        if (results.isEmpty()) {
+            send(1, null);
+            return;
+        }
+        int count = -1;
+        for (SavedDatasetsInfo info : results) {
+            if (mType.equals(info.getType())) {
+                count = info.getCount();
+            }
+        }
+        if (count < 0) {
+            send(1, null);
+            return;
+        }
+        Bundle bundle = new Bundle(/* capacity= */ 1);
+        bundle.putInt(EXTRA_RESULT, count);
+        send(0, bundle);
+    }
+
+    @Override
+    public void onError(@Error int error) {
+        checkArgumentInRange(error, ERROR_OTHER, ERROR_NEEDS_USER_ACTION, "error");
+        Bundle bundle = new Bundle(/* capacity= */ 1);
+        bundle.putInt(EXTRA_ERROR, error);
+        send(1, bundle);
+    }
+
+    private void send(int resultCode, Bundle bundle) {
+        try {
+            mReceiver.send(resultCode, bundle);
+        } catch (DeadObjectException e) {
+            Log.w(TAG, "Failed to send onSavedPasswordCountRequest result: " + e);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+}
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index aeeaa97..d9a310f 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -618,7 +618,7 @@
                         mFirstOnSuccessTime = SystemClock.elapsedRealtime();
                         duration = mFirstOnSuccessTime - mFirstRequestTime;
                         if (sDebug) {
-                            Log.d(TAG, "Service responded nothing in " + formatDuration(duration));
+                            Log.d(TAG, "Inline response in " + formatDuration(duration));
                         }
                     }
                 } break;
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 1ea40be..94ca68f 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -44,6 +44,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
+import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.SharedMemory;
@@ -342,14 +343,35 @@
         // Raw data associated with the event.
         // This is the audio that triggered the keyphrase if {@code isTriggerAudio} is true.
         private final byte[] mData;
+        private final HotwordDetectedResult mHotwordDetectedResult;
+        private final ParcelFileDescriptor mAudioStream;
 
         private EventPayload(boolean triggerAvailable, boolean captureAvailable,
                 AudioFormat audioFormat, int captureSession, byte[] data) {
+            this(triggerAvailable, captureAvailable, audioFormat, captureSession, data, null,
+                    null);
+        }
+
+        EventPayload(AudioFormat audioFormat, HotwordDetectedResult hotwordDetectedResult) {
+            this(false, false, audioFormat, -1, null, hotwordDetectedResult, null);
+        }
+
+        EventPayload(AudioFormat audioFormat,
+                HotwordDetectedResult hotwordDetectedResult,
+                ParcelFileDescriptor audioStream) {
+            this(false, false, audioFormat, -1, null, hotwordDetectedResult, audioStream);
+        }
+
+        private EventPayload(boolean triggerAvailable, boolean captureAvailable,
+                AudioFormat audioFormat, int captureSession, byte[] data,
+                HotwordDetectedResult hotwordDetectedResult, ParcelFileDescriptor audioStream) {
             mTriggerAvailable = triggerAvailable;
             mCaptureAvailable = captureAvailable;
             mCaptureSession = captureSession;
             mAudioFormat = audioFormat;
             mData = data;
+            mHotwordDetectedResult = hotwordDetectedResult;
+            mAudioStream = audioStream;
         }
 
         /**
@@ -405,6 +427,33 @@
                 return null;
             }
         }
+
+        /**
+         * Returns {@link HotwordDetectedResult} associated with the hotword event, passed from
+         * {@link HotwordDetectionService}.
+         */
+        @Nullable
+        public HotwordDetectedResult getHotwordDetectedResult() {
+            return mHotwordDetectedResult;
+        }
+
+        /**
+         * Returns a stream with bytes corresponding to the open audio stream with hotword data.
+         *
+         * <p>This data represents an audio stream in the format returned by
+         * {@link #getCaptureAudioFormat}.
+         *
+         * <p>Clients are expected to start consuming the stream within 1 second of receiving the
+         * event.
+         *
+         * <p>When this method returns a non-null, clients must close this stream when it's no
+         * longer needed. Failing to do so will result in microphone being open for longer periods
+         * of time, and app being attributed for microphone usage.
+         */
+        @Nullable
+        public ParcelFileDescriptor getAudioStream() {
+            return mAudioStream;
+        }
     }
 
     /**
@@ -508,7 +557,7 @@
         mTargetSdkVersion = targetSdkVersion;
         mSupportHotwordDetectionService = supportHotwordDetectionService;
         if (mSupportHotwordDetectionService) {
-            setHotwordDetectionServiceConfig(options, sharedMemory);
+            updateState(options, sharedMemory);
         }
         try {
             Identity identity = new Identity();
@@ -524,30 +573,28 @@
     /**
      * Set configuration and pass read-only data to hotword detection service.
      *
-     * @param options Application configuration data provided by the
-     * {@link VoiceInteractionService}. PersistableBundle does not allow any remotable objects or
+     * @param options Application configuration data to provide to the
+     * {@link HotwordDetectionService}. PersistableBundle does not allow any remotable objects or
      * other contents that can be used to communicate with other processes.
-     * @param sharedMemory The unrestricted data blob provided by the
-     * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+     * @param sharedMemory The unrestricted data blob to provide to the
+     * {@link HotwordDetectionService}. Use this to provide the hotword models data or other
      * such data to the trusted process.
      *
-     * @throws IllegalStateException if it doesn't support hotword detection service.
-     *
-     * @hide
+     * @throws IllegalStateException if this AlwaysOnHotwordDetector wasn't specified to use a
+     * {@link HotwordDetectionService} when it was created.
      */
-    public final void setHotwordDetectionServiceConfig(@Nullable PersistableBundle options,
+    public final void updateState(@Nullable PersistableBundle options,
             @Nullable SharedMemory sharedMemory) {
         if (DBG) {
-            Slog.d(TAG, "setHotwordDetectionServiceConfig()");
+            Slog.d(TAG, "updateState()");
         }
         if (!mSupportHotwordDetectionService) {
             throw new IllegalStateException(
-                    "setHotwordDetectionServiceConfig called, but it doesn't support hotword"
-                            + " detection service");
+                    "updateState called, but it doesn't support hotword detection service");
         }
 
         try {
-            mModelManagementService.setHotwordDetectionServiceConfig(options, sharedMemory);
+            mModelManagementService.updateState(options, sharedMemory);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index 686268c..db984c2 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -82,10 +82,10 @@
         }
 
         @Override
-        public void setConfig(PersistableBundle options, SharedMemory sharedMemory)
+        public void updateState(PersistableBundle options, SharedMemory sharedMemory)
                 throws RemoteException {
             if (DBG) {
-                Log.d(TAG, "#setConfig");
+                Log.d(TAG, "#updateState");
             }
             mHandler.sendMessage(obtainMessage(HotwordDetectionService::onUpdateState,
                     HotwordDetectionService.this,
@@ -139,14 +139,14 @@
     /**
      * Called when the {@link VoiceInteractionService#createAlwaysOnHotwordDetector(String, Locale,
      * PersistableBundle, SharedMemory, AlwaysOnHotwordDetector.Callback)} or
-     * {@link AlwaysOnHotwordDetector#setHotwordDetectionServiceConfig(PersistableBundle,
-     * SharedMemory)} requests an update of the hotword detection parameters.
+     * {@link AlwaysOnHotwordDetector#updateState(PersistableBundle, SharedMemory)} requests an
+     * update of the hotword detection parameters.
      *
-     * @param options Application configuration data provided by the
-     * {@link VoiceInteractionService}. PersistableBundle does not allow any remotable objects or
+     * @param options Application configuration data to provide to the
+     * {@link HotwordDetectionService}. PersistableBundle does not allow any remotable objects or
      * other contents that can be used to communicate with other processes.
-     * @param sharedMemory The unrestricted data blob provided by the
-     * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+     * @param sharedMemory The unrestricted data blob to provide to the
+     * {@link HotwordDetectionService}. Use this to provide the hotword models data or other
      * such data to the trusted process.
      *
      * @hide
diff --git a/core/java/android/service/voice/IHotwordDetectionService.aidl b/core/java/android/service/voice/IHotwordDetectionService.aidl
index 8d01dd1..0791f1c 100644
--- a/core/java/android/service/voice/IHotwordDetectionService.aidl
+++ b/core/java/android/service/voice/IHotwordDetectionService.aidl
@@ -34,5 +34,5 @@
     long timeoutMillis,
     in IDspHotwordDetectionCallback callback);
 
-    void setConfig(in PersistableBundle options, in SharedMemory sharedMemory);
+    void updateState(in PersistableBundle options, in SharedMemory sharedMemory);
 }
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index d47ae27..1da7dc4 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -343,11 +343,14 @@
     /**
      *  Listen for display info changed event.
      *
-     *  Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
-     *  READ_PHONE_STATE} or that the calling app has carrier privileges (see
-     *  {@link TelephonyManager#hasCarrierPrivileges}).
+     * For clients compiled on Android 11 SDK, requires permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling app has carrier
+     * privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     * For clients compiled on Android 12 SDK or newer,
+     * {@link android.Manifest.permission#READ_PHONE_STATE} or carrier privileges is not required
+     * anymore.
      *
-     *  @see #onDisplayInfoChanged
+     * @see #onDisplayInfoChanged
      * @deprecated Use {@link TelephonyCallback.DisplayInfoListener} instead.
      */
     @Deprecated
@@ -981,8 +984,12 @@
      * <p> The {@link TelephonyDisplayInfo} contains status information shown to the user based on
      * carrier policy.
      *
-     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling
-     * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     * For clients compiled on Android 11 SDK, requires permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling app has carrier
+     * privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     * For clients compiled on Android 12 SDK or newer,
+     * {@link android.Manifest.permission#READ_PHONE_STATE} or carrier privileges is not required
+     * anymore.
      *
      * @param telephonyDisplayInfo The display information.
      * @deprecated Use {@link TelephonyCallback.DisplayInfoListener} instead.
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index d000000..18949cd 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -1057,7 +1057,6 @@
          *
          * @param telephonyDisplayInfo The display information.
          */
-        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
         public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo);
     }
 
diff --git a/core/java/android/uwb/AngleMeasurement.java b/core/java/android/uwb/AngleMeasurement.java
index 8c771ba..3d60373 100644
--- a/core/java/android/uwb/AngleMeasurement.java
+++ b/core/java/android/uwb/AngleMeasurement.java
@@ -48,7 +48,10 @@
      * @throws IllegalArgumentException if the radians, errorRadians, or confidenceLevel is out of
      *                                  allowed range
      */
-    public AngleMeasurement(double radians, double errorRadians, double confidenceLevel) {
+    public AngleMeasurement(
+            @FloatRange(from = -Math.PI, to = +Math.PI) double radians,
+            @FloatRange(from = 0.0, to = +Math.PI) double errorRadians,
+            @FloatRange(from = 0.0, to = 1.0) double confidenceLevel) {
         if (radians < -Math.PI || radians > Math.PI) {
             throw new IllegalArgumentException("Invalid radians: " + radians);
         }
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 98b7dbf..8db6456 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -64,8 +64,14 @@
     }
 
     @Override
-    void hide(boolean animationFinished, @AnimationType int animationType) {
+    public void hide() {
         super.hide();
+        mIsRequestedVisibleAwaitingControl = false;
+    }
+
+    @Override
+    void hide(boolean animationFinished, @AnimationType int animationType) {
+        hide();
 
         if (animationFinished) {
             // remove IME surface as IME has finished hide animation.
@@ -122,6 +128,9 @@
             hide();
             removeSurface();
         }
+        if (control != null) {
+            mIsRequestedVisibleAwaitingControl = false;
+        }
     }
 
     @Override
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 6801c27..dea32cd 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import static android.hardware.input.InputManager.APP_USES_RAW_INPUT_COORDS;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -23,6 +24,7 @@
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
+import android.compat.Compatibility;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Matrix;
 import android.os.Build;
@@ -2672,6 +2674,7 @@
      * @see #AXIS_X
      */
     public final float getRawX() {
+        Compatibility.reportChange(APP_USES_RAW_INPUT_COORDS);
         return nativeGetRawAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT);
     }
 
@@ -2685,6 +2688,7 @@
      * @see #AXIS_Y
      */
     public final float getRawY() {
+        Compatibility.reportChange(APP_USES_RAW_INPUT_COORDS);
         return nativeGetRawAxisValue(mNativePtr, AXIS_Y, 0, HISTORY_CURRENT);
     }
 
@@ -2701,6 +2705,7 @@
      * @see #AXIS_X
      */
     public float getRawX(int pointerIndex) {
+        Compatibility.reportChange(APP_USES_RAW_INPUT_COORDS);
         return nativeGetRawAxisValue(mNativePtr, AXIS_X, pointerIndex, HISTORY_CURRENT);
     }
 
@@ -2717,6 +2722,7 @@
      * @see #AXIS_Y
      */
     public float getRawY(int pointerIndex) {
+        Compatibility.reportChange(APP_USES_RAW_INPUT_COORDS);
         return nativeGetRawAxisValue(mNativePtr, AXIS_Y, pointerIndex, HISTORY_CURRENT);
     }
 
diff --git a/core/java/android/view/SoundEffectConstants.java b/core/java/android/view/SoundEffectConstants.java
index f177451..bd86a47 100644
--- a/core/java/android/view/SoundEffectConstants.java
+++ b/core/java/android/view/SoundEffectConstants.java
@@ -16,11 +16,14 @@
 
 package android.view;
 
+import android.annotation.IntDef;
 import android.media.AudioManager;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Random;
 
 /**
@@ -34,25 +37,55 @@
 
     public static final int CLICK = 0;
 
+    /** Effect id for a navigation left */
     public static final int NAVIGATION_LEFT = 1;
+    /** Effect id for a navigation up */
     public static final int NAVIGATION_UP = 2;
+    /** Effect id for a navigation right */
     public static final int NAVIGATION_RIGHT = 3;
+    /** Effect id for a navigation down */
     public static final int NAVIGATION_DOWN = 4;
-    /** Sound effect for a repeatedly triggered navigation, e.g. due to long pressing a button */
+    /** Effect id for a repeatedly triggered navigation left, e.g. due to long pressing a button */
     public static final int NAVIGATION_REPEAT_LEFT = 5;
-    /** @see #NAVIGATION_REPEAT_LEFT */
+    /** Effect id for a repeatedly triggered navigation up, e.g. due to long pressing a button */
     public static final int NAVIGATION_REPEAT_UP = 6;
-    /** @see #NAVIGATION_REPEAT_LEFT */
+    /** Effect id for a repeatedly triggered navigation right, e.g. due to long pressing a button */
     public static final int NAVIGATION_REPEAT_RIGHT = 7;
-    /** @see #NAVIGATION_REPEAT_LEFT */
+    /** Effect id for a repeatedly triggered navigation down, e.g. due to long pressing a button */
     public static final int NAVIGATION_REPEAT_DOWN = 8;
 
+    /** @hide */
+    @IntDef(value = {
+            CLICK,
+            NAVIGATION_LEFT,
+            NAVIGATION_UP,
+            NAVIGATION_RIGHT,
+            NAVIGATION_DOWN,
+            NAVIGATION_REPEAT_LEFT,
+            NAVIGATION_REPEAT_UP,
+            NAVIGATION_REPEAT_RIGHT,
+            NAVIGATION_REPEAT_DOWN
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SoundEffect {}
+
+    /** @hide */
+    @IntDef(prefix = { "NAVIGATION_" }, value = {
+            NAVIGATION_LEFT,
+            NAVIGATION_UP,
+            NAVIGATION_RIGHT,
+            NAVIGATION_DOWN,
+            NAVIGATION_REPEAT_LEFT,
+            NAVIGATION_REPEAT_UP,
+            NAVIGATION_REPEAT_RIGHT,
+            NAVIGATION_REPEAT_DOWN
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NavigationSoundEffect {}
+
     /**
      * Get the sonification constant for the focus directions.
-     * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
-     *     {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD}
-     *     or {@link View#FOCUS_BACKWARD}
-
+     * @param direction The direction of the focus.
      * @return The appropriate sonification constant.
      * @throws {@link IllegalArgumentException} when the passed direction is not one of the
      *     documented values.
@@ -76,16 +109,14 @@
 
     /**
      * Get the sonification constant for the focus directions
-     * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
-     *     {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD}
-     *     or {@link View#FOCUS_BACKWARD}
+     * @param direction The direction of the focus.
      * @param repeating True if the user long-presses a direction
      * @return The appropriate sonification constant
      * @throws IllegalArgumentException when the passed direction is not one of the
      *      documented values.
      */
-    public static int getConstantForFocusDirection(@View.FocusDirection int direction,
-            boolean repeating) {
+    public static @NavigationSoundEffect int getConstantForFocusDirection(
+            @View.FocusDirection int direction, boolean repeating) {
         if (repeating) {
             switch (direction) {
                 case View.FOCUS_RIGHT:
@@ -112,7 +143,7 @@
      * @hide
      */
     @VisibleForTesting(visibility = Visibility.PACKAGE)
-    public static boolean isNavigationRepeat(int effectId) {
+    public static boolean isNavigationRepeat(@NavigationSoundEffect int effectId) {
         return effectId == SoundEffectConstants.NAVIGATION_REPEAT_DOWN
                 || effectId == SoundEffectConstants.NAVIGATION_REPEAT_LEFT
                 || effectId == SoundEffectConstants.NAVIGATION_REPEAT_RIGHT
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index f1eef9f..82106b0 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1389,6 +1389,8 @@
             // If we are using BLAST, merge the transaction with the viewroot buffer transaction.
             viewRoot.mergeWithNextTransaction(mRtTransaction, frameNumber);
             return;
+        } else {
+            mRtTransaction.apply();
         }
     }
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7455b8b..a03e9e3 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -151,6 +151,8 @@
 import android.view.inspector.InspectableProperty;
 import android.view.inspector.InspectableProperty.EnumEntry;
 import android.view.inspector.InspectableProperty.FlagEntry;
+import android.view.translation.TranslationSpec.DataFormat;
+import android.view.translation.ViewTranslationCallback;
 import android.view.translation.ViewTranslationRequest;
 import android.view.translation.ViewTranslationResponse;
 import android.widget.Checkable;
@@ -5253,6 +5255,9 @@
     @Nullable
     private String[] mOnReceiveContentMimeTypes;
 
+    @Nullable
+    private ViewTranslationCallback mViewTranslationCallback;
+
     /**
      * Simple constructor to use when creating a view from code.
      *
@@ -26136,9 +26141,9 @@
      * <p>The sound effect will only be played if sound effects are enabled by the user, and
      * {@link #isSoundEffectsEnabled()} is true.
      *
-     * @param soundConstant One of the constants defined in {@link SoundEffectConstants}
+     * @param soundConstant One of the constants defined in {@link SoundEffectConstants}.
      */
-    public void playSoundEffect(int soundConstant) {
+    public void playSoundEffect(@SoundEffectConstants.SoundEffect int soundConstant) {
         if (mAttachInfo == null || mAttachInfo.mRootCallbacks == null || !isSoundEffectsEnabled()) {
             return;
         }
@@ -30717,71 +30722,62 @@
         }
     }
 
+    //TODO(b/1960696): update javadoc when dispatchRequestTranslation is ready.
     /**
-     * Returns a {@link ViewTranslationRequest} to the {@link onStartUiTranslation} which represents
-     * the content to be translated.
+     * Returns a {@link ViewTranslationRequest} which represents the content to be translated.
      *
-     * <p>The default implementation does nothing and return null.</p>
+     * <p>The default implementation does nothing and returns null.</p>
      *
-     * @hide
-     *
-     * @return the {@link ViewTranslationRequest} which contains the information to be translated.
+     * @param supportedFormats the supported translation formats. For now, the only possible value
+     * is the {@link android.view.translation.TranslationSpec#DATA_FORMAT_TEXT}.
+     * @return the {@link ViewTranslationRequest} which contains the information to be translated or
+     * {@code null} if this View doesn't support translation.
+     * The {@link AutofillId} must be set on the returned value.
      */
     @Nullable
-    //TODO(b/178046780): initial version for demo. Will mark public when the design is reviewed.
-    public ViewTranslationRequest onCreateTranslationRequest() {
+    public ViewTranslationRequest createTranslationRequest(
+            @NonNull @DataFormat int[] supportedFormats) {
         return null;
     }
 
     /**
-     * Called when the user wants to show the original text instead of the translated text.
+     * Returns a {@link ViewTranslationCallback} that is used to display/hide the translated
+     * information. If the View supports displaying translated content, it should implement
+     * {@link ViewTranslationCallback}.
      *
-     * @hide
+     * <p>The default implementation returns null if developers don't set the customized
+     * {@link ViewTranslationCallback} by {@link #setViewTranslationCallback} </p>
      *
-     * <p> The default implementation does nothing.
+     * @return a {@link ViewTranslationCallback} that is used to control how to display the
+     * translated information or {@code null} if this View doesn't support translation.
      */
-    //TODO(b/178046780): initial version for demo. Will mark public when the design is reviewed.
-    public void onPauseUiTranslation() {
-        // no-op
+    @Nullable
+    public ViewTranslationCallback getViewTranslationCallback() {
+        return mViewTranslationCallback;
     }
 
     /**
-     * User can switch back to show the original text, this method called when the user wants to
-     * re-show the translated text again.
+     * Sets a {@link ViewTranslationCallback} that is used to display/hide the translated
+     * information. Developers can provide the customized implementation for show/hide translated
+     * information.
      *
-     * @hide
-     *
-     * <p> The default implementation does nothing.</p>
+     * @param callback a {@link ViewTranslationCallback} that is used to control how to display the
+     * translated information
      */
-    //TODO(b/178046780): initial version for demo. Will mark public when the design is reviewed.
-    public void onRestoreUiTranslation() {
-        // no-op
+    public void setViewTranslationCallback(@NonNull ViewTranslationCallback callback) {
+        mViewTranslationCallback = callback;
     }
 
     /**
-     * Called when the user finish the Ui translation and no longer to show the translated text.
-     *
-     * @hide
-     *
-     * <p> The default implementation does nothing.</p>
-     */
-    //TODO(b/178046780): initial version for demo. Will mark public when the design is reviewed.
-    public void onFinishUiTranslation() {
-        // no-op
-    }
-
-    /**
-     * Called when the request from {@link onStartUiTranslation} is completed by the translation
-     * service so that the translation result can be shown.
-     *
-     * @hide
+     * Called when the content from {@link #createTranslationRequest} had been translated by the
+     * TranslationService.
      *
      * <p> The default implementation does nothing.</p>
      *
-     * @param response the translated information which can be shown in the view.
+     * @param response a {@link ViewTranslationResponse} that contains the translated information
+     * which can be shown in the view.
      */
-    //TODO(b/178046780): initial version for demo. Will mark public when the design is reviewed.
-    public void onTranslationComplete(@NonNull ViewTranslationResponse response) {
+    public void onTranslationResponse(@NonNull ViewTranslationResponse response) {
         // no-op
     }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index dbccf10..0a246a6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4293,7 +4293,7 @@
                 mChoreographer.getFrameTimeNanos() / TimeUtils.NANOS_PER_MS;
 
         boolean useAsyncReport = false;
-        if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
+        if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty || mNextDrawUseBlastSync) {
             if (isHardwareEnabled()) {
                 // If accessibility focus moved, always invalidate the root.
                 boolean invalidateRoot = accessibilityFocusDirty || mInvalidateRootRequested;
@@ -7754,7 +7754,7 @@
      * {@inheritDoc}
      */
     @Override
-    public void playSoundEffect(int effectId) {
+    public void playSoundEffect(@SoundEffectConstants.SoundEffect int effectId) {
         checkThread();
 
         try {
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 2b12230..ce01469 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -143,6 +143,9 @@
     private @Nullable ContentCaptureContext mClientContext;
     private @Nullable Insets mInsets;
 
+    /** Only used in the main Content Capture session, no need to parcel */
+    private boolean mTextHasComposingSpan;
+
     /** @hide */
     public ContentCaptureEvent(int sessionId, int type, long eventTime) {
         mSessionId = sessionId;
@@ -243,11 +246,21 @@
 
     /** @hide */
     @NonNull
-    public ContentCaptureEvent setText(@Nullable CharSequence text) {
+    public ContentCaptureEvent setText(@Nullable CharSequence text, boolean hasComposingSpan) {
         mText = text;
+        mTextHasComposingSpan = hasComposingSpan;
         return this;
     }
 
+    /**
+     * The value is not parcelled, become false after parcelled.
+     * @hide
+     */
+    @NonNull
+    public boolean getTextHasComposingSpan() {
+        return mTextHasComposingSpan;
+    }
+
     /** @hide */
     @NonNull
     public ContentCaptureEvent setInsets(@NonNull Insets insets) {
@@ -361,7 +374,7 @@
             throw new IllegalArgumentException("mergeEvent(): got "
                     + "TYPE_VIEW_DISAPPEARED event with neither id or ids: " + event);
         } else if (eventType == TYPE_VIEW_TEXT_CHANGED) {
-            setText(event.getText());
+            setText(event.getText(), event.getTextHasComposingSpan());
         } else {
             Log.e(TAG, "mergeEvent(" + getTypeAsString(eventType)
                     + ") does not support this event type.");
@@ -479,7 +492,7 @@
             if (node != null) {
                 event.setViewNode(node);
             }
-            event.setText(parcel.readCharSequence());
+            event.setText(parcel.readCharSequence(), false);
             if (type == TYPE_SESSION_STARTED || type == TYPE_SESSION_FINISHED) {
                 event.setParentSessionId(parcel.readInt());
             }
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 5ca793e..f196f75 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -43,12 +43,15 @@
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
 import android.os.RemoteException;
+import android.text.Spannable;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.LocalLog;
 import android.util.Log;
 import android.util.TimeUtils;
 import android.view.autofill.AutofillId;
 import android.view.contentcapture.ViewNode.ViewStructureImpl;
+import android.view.inputmethod.BaseInputConnection;
 
 import com.android.internal.os.IResultReceiver;
 
@@ -57,6 +60,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -147,6 +151,12 @@
     private final LocalLog mFlushHistory;
 
     /**
+     * If the event in the buffer is of type {@link TYPE_VIEW_TEXT_CHANGED}, this value
+     * indicates whether the event has composing span or not.
+     */
+    private final Map<AutofillId, Boolean> mLastComposingSpan = new ArrayMap<>();
+
+    /**
      * Binder object used to update the session state.
      */
     @NonNull
@@ -335,26 +345,47 @@
         // Some type of events can be merged together
         boolean addEvent = true;
 
-        if (!mEvents.isEmpty() && eventType == TYPE_VIEW_TEXT_CHANGED) {
-            final ContentCaptureEvent lastEvent = mEvents.get(mEvents.size() - 1);
+        if (eventType == TYPE_VIEW_TEXT_CHANGED) {
+            // We determine whether to add or merge the current event by following criteria:
+            // 1. Don't have composing span: always add.
+            // 2. Have composing span:
+            //    2.1 either last or current text is empty: add.
+            //    2.2 last event doesn't have composing span: add.
+            // Otherwise, merge.
 
-            // We merge two consecutive text change event, unless one of them clears the text.
-            if (lastEvent.getType() == TYPE_VIEW_TEXT_CHANGED
-                    && lastEvent.getId().equals(event.getId())) {
-                boolean bothNonEmpty = !TextUtils.isEmpty(lastEvent.getText())
-                        && !TextUtils.isEmpty(event.getText());
-                boolean equalContent = TextUtils.equals(lastEvent.getText(), event.getText());
-                if (equalContent) {
-                    addEvent = false;
-                } else if (bothNonEmpty) {
-                    lastEvent.mergeEvent(event);
-                    addEvent = false;
-                }
-                if (!addEvent && sVerbose) {
-                    Log.v(TAG, "Buffering VIEW_TEXT_CHANGED event, updated text="
-                            + getSanitizedString(event.getText()));
+            final CharSequence text = event.getText();
+            final boolean textHasComposingSpan = event.getTextHasComposingSpan();
+
+            if (textHasComposingSpan && !mLastComposingSpan.isEmpty()) {
+                final Boolean lastEventHasComposingSpan = mLastComposingSpan.get(event.getId());
+                if (lastEventHasComposingSpan != null && lastEventHasComposingSpan.booleanValue()) {
+                    ContentCaptureEvent lastEvent = null;
+                    for (int index = mEvents.size() - 1; index >= 0; index--) {
+                        final ContentCaptureEvent tmpEvent = mEvents.get(index);
+                        if (event.getId().equals(tmpEvent.getId())) {
+                            lastEvent = tmpEvent;
+                            break;
+                        }
+                    }
+                    if (lastEvent != null) {
+                        final CharSequence lastText = lastEvent.getText();
+                        final boolean bothNonEmpty = !TextUtils.isEmpty(lastText)
+                                && !TextUtils.isEmpty(text);
+                        boolean equalContent = TextUtils.equals(lastText, text);
+                        if (equalContent) {
+                            addEvent = false;
+                        } else if (bothNonEmpty && lastEventHasComposingSpan) {
+                            lastEvent.mergeEvent(event);
+                            addEvent = false;
+                        }
+                        if (!addEvent && sVerbose) {
+                            Log.v(TAG, "Buffering VIEW_TEXT_CHANGED event, updated text="
+                                    + getSanitizedString(text));
+                        }
+                    }
                 }
             }
+            mLastComposingSpan.put(event.getId(), textHasComposingSpan);
         }
 
         if (!mEvents.isEmpty() && eventType == TYPE_VIEW_DISAPPEARED) {
@@ -374,6 +405,11 @@
             mEvents.add(event);
         }
 
+        // TODO: we need to change when the flush happens so that we don't flush while the
+        //  composing span hasn't changed. But we might need to keep flushing the events for the
+        //  non-editable views and views that don't have the composing state; otherwise some other
+        //  Content Capture features may be delayed.
+
         final int numberEvents = mEvents.size();
 
         final boolean bufferEvent = numberEvents < maxBufferSize;
@@ -550,6 +586,7 @@
                 ? Collections.emptyList()
                 : mEvents;
         mEvents = null;
+        mLastComposingSpan.clear();
         return new ParceledListSlice<>(events);
     }
 
@@ -677,9 +714,16 @@
     }
 
     void notifyViewTextChanged(int sessionId, @NonNull AutofillId id, @Nullable CharSequence text) {
+        // Since the same CharSequence instance may be reused in the TextView, we need to make
+        // a copy of its content so that its value will not be changed by subsequent updates
+        // in the TextView.
+        final String eventText = text == null ? null : text.toString();
+        final boolean textHasComposingSpan =
+                text instanceof Spannable && BaseInputConnection.getComposingSpanStart(
+                        (Spannable) text) >= 0;
         mHandler.post(() -> sendEvent(
                 new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED)
-                        .setAutofillId(id).setText(text)));
+                        .setAutofillId(id).setText(eventText, textHasComposingSpan)));
     }
 
     /** Public because is also used by ViewRootImpl */
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index d79ecca..15d01ae 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -46,7 +46,7 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.function.Consumer;
+import java.util.function.BiConsumer;
 
 /**
  * A controller to manage the ui translation requests for the {@link Activity}.
@@ -77,6 +77,7 @@
     private final HandlerThread mWorkerThread;
     @NonNull
     private final Handler mWorkerHandler;
+    private int mCurrentState;
 
     public UiTranslationController(Activity activity, Context context) {
         mActivity = activity;
@@ -101,6 +102,9 @@
         }
         Log.i(TAG, "updateUiTranslationState state: " + stateToString(state)
                 + (DEBUG ? ", views: " + views : ""));
+        synchronized (mLock) {
+            mCurrentState = state;
+        }
         switch (state) {
             case STATE_UI_TRANSLATION_STARTED:
                 final Pair<TranslationSpec, TranslationSpec> specs =
@@ -114,14 +118,14 @@
                 }
                 break;
             case STATE_UI_TRANSLATION_PAUSED:
-                runForEachView(View::onPauseUiTranslation);
+                runForEachView((view, callback) -> callback.onHideTranslation(view));
                 break;
             case STATE_UI_TRANSLATION_RESUMED:
-                runForEachView(View::onRestoreUiTranslation);
+                runForEachView((view, callback) -> callback.onShowTranslation(view));
                 break;
             case STATE_UI_TRANSLATION_FINISHED:
                 destroyTranslators();
-                runForEachView(View::onFinishUiTranslation);
+                runForEachView((view, callback) -> callback.onClearTranslation(view));
                 synchronized (mLock) {
                     mViews.clear();
                 }
@@ -232,11 +236,16 @@
         }
         final SparseArray<ViewTranslationResponse> translatedResult =
                 response.getViewTranslationResponses();
+        // TODO(b/177960696): handle virtual views. Check the result if the AutofillId is virtual
+        // AutofillId?
         onTranslationCompleted(translatedResult);
     }
 
     private void onTranslationCompleted(SparseArray<ViewTranslationResponse> translatedResult) {
         if (!mActivity.isResumed()) {
+            if (DEBUG) {
+                Log.v(TAG, "onTranslationCompleted: Activity is not resumed.");
+            }
             return;
         }
         final int resultCount = translatedResult.size();
@@ -244,6 +253,11 @@
             Log.v(TAG, "onTranslationCompleted: receive " + resultCount + " responses.");
         }
         synchronized (mLock) {
+            if (mCurrentState == STATE_UI_TRANSLATION_FINISHED) {
+                Log.w(TAG, "onTranslationCompleted: the translation state is finished now. "
+                        + "Skip to show the translated text.");
+                return;
+            }
             for (int i = 0; i < resultCount; i++) {
                 final ViewTranslationResponse response = translatedResult.get(i);
                 final AutofillId autofillId = response.getAutofillId();
@@ -256,18 +270,28 @@
                             + " may be gone.");
                     continue;
                 }
-                mActivity.runOnUiThread(() -> view.onTranslationComplete(response));
+                mActivity.runOnUiThread(() -> {
+                    if (view.getViewTranslationCallback() == null) {
+                        if (DEBUG) {
+                            Log.d(TAG, view + " doesn't support showing translation because of "
+                                    + "null ViewTranslationCallback.");
+                        }
+                        return;
+                    }
+                    view.onTranslationResponse(response);
+                    view.getViewTranslationCallback().onShowTranslation(view);
+                });
             }
         }
     }
 
     /**
-     * Called when there is an ui translation request comes to request view translation.
+     * Creates a Translator for the given source and target translation specs and start the ui
+     * translation when the Translator is created successfully.
      */
     @WorkerThread
     private void createTranslatorAndStart(TranslationSpec sourceSpec, TranslationSpec destSpec,
             List<AutofillId> views) {
-        // Create Translator
         final Translator translator = createTranslatorIfNeeded(sourceSpec, destSpec);
         if (translator == null) {
             Log.w(TAG, "Can not create Translator for sourceSpec:" + sourceSpec + " destSpec:"
@@ -295,31 +319,51 @@
      */
     private void onUiTranslationStarted(Translator translator, List<AutofillId> views) {
         synchronized (mLock) {
-            // Find Views collect the translation data
-            final ArrayList<ViewTranslationRequest> requests = new ArrayList<>();
-            final ArrayList<View> foundViews = new ArrayList<>();
-            findViewsTraversalByAutofillIds(views, foundViews);
-            for (int i = 0; i < foundViews.size(); i++) {
-                final View view = foundViews.get(i);
-                final int currentCount = i;
-                mActivity.runOnUiThread(() -> {
-                    final ViewTranslationRequest request = view.onCreateTranslationRequest();
-                    if (request != null
-                            && request.getKeys().size() > 0) {
-                        requests.add(request);
-                    }
-                    if (currentCount == (foundViews.size() - 1)) {
-                        Log.v(TAG, "onUiTranslationStarted: collect " + requests.size()
-                                + " requests.");
-                        mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
-                                UiTranslationController::sendTranslationRequest,
-                                UiTranslationController.this, translator, requests));
-                    }
-                });
-            }
+            // TODO(b/177960696): handle virtual views. Need to check the requested view list is
+            //  virtual AutofillId or not
+            findViewsAndCollectViewTranslationRequest(translator, views);
         }
     }
 
+    /**
+     * If the translation requested views are not virtual view, we need to traverse the tree to
+     * find the views and get the View's ViewTranslationRequest.
+     */
+    private void findViewsAndCollectViewTranslationRequest(Translator translator,
+            List<AutofillId> views) {
+        // Find Views collect the translation data
+        final ArrayList<ViewTranslationRequest> requests = new ArrayList<>();
+            final ArrayList<View> foundViews = new ArrayList<>();
+        findViewsTraversalByAutofillIds(views, foundViews);
+        final int[] supportedFormats = getSupportedFormatsLocked();
+        for (int i = 0; i < foundViews.size(); i++) {
+            final View view = foundViews.get(i);
+            final int currentCount = i;
+            mActivity.runOnUiThread(() -> {
+                final ViewTranslationRequest request =
+                        view.createTranslationRequest(supportedFormats);
+                // TODO(b/177960696): handle null case, the developers may want to handle the
+                //  translation, call dispatchRequestTranslation() instead.
+                if (request != null
+                        && request.getKeys().size() > 0) {
+                    requests.add(request);
+                }
+                if (currentCount == (foundViews.size() - 1)) {
+                    Log.v(TAG, "onUiTranslationStarted: collect " + requests.size()
+                            + " requests.");
+                    mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
+                            UiTranslationController::sendTranslationRequest,
+                            UiTranslationController.this, translator, requests));
+                }
+            });
+        }
+    }
+
+    private int[] getSupportedFormatsLocked() {
+        // We only support text now
+        return new int[] {TranslationSpec.DATA_FORMAT_TEXT};
+    }
+
     private void findViewsTraversalByAutofillIds(List<AutofillId> sourceViewIds,
             ArrayList<View> foundViews) {
         final ArrayList<ViewRootImpl> roots =
@@ -356,20 +400,21 @@
         }
     }
 
-    private void runForEachView(Consumer<View> action) {
+    private void runForEachView(BiConsumer<View, ViewTranslationCallback> action) {
         synchronized (mLock) {
             final ArrayMap<AutofillId, WeakReference<View>> views = new ArrayMap<>(mViews);
             mActivity.runOnUiThread(() -> {
                 final int viewCounts = views.size();
                 for (int i = 0; i < viewCounts; i++) {
                     final View view = views.valueAt(i).get();
-                    if (view == null) {
+                    if (view == null || view.getViewTranslationCallback() == null) {
                         if (DEBUG) {
-                            Log.d(TAG, "View was gone for autofillid = " + views.keyAt(i));
+                            Log.d(TAG, "View was gone or ViewTranslationCallback for autofillid "
+                                    + "= " + views.keyAt(i));
                         }
                         continue;
                     }
-                    action.accept(view);
+                    action.accept(view, view.getViewTranslationCallback());
                 }
             });
         }
diff --git a/core/java/android/view/translation/ViewTranslationCallback.java b/core/java/android/view/translation/ViewTranslationCallback.java
new file mode 100644
index 0000000..c895984
--- /dev/null
+++ b/core/java/android/view/translation/ViewTranslationCallback.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.translation;
+
+import android.annotation.NonNull;
+import android.annotation.UiThread;
+import android.view.View;
+
+/**
+ * Callback for handling the translated information show or hide in the {@link View}. See {@link
+ * View#onTranslationResponse} for how to get the translated information.
+ */
+@UiThread
+public interface ViewTranslationCallback {
+    /**
+     * Called when the translated text is ready to show or if the user has requested to reshow the
+     * translated content after hiding it. This may be called before the translation results are
+     * returned through the {@link View#onTranslationResponse}.
+     *
+     * @return {@code true} if the View handles showing the translation.
+     */
+    boolean onShowTranslation(@NonNull View view);
+    /**
+     * Called when the user wants to show the original text instead of the translated text. This
+     * may be called before the translation results are returned through the
+     * {@link View#onTranslationResponse}.
+     *
+     * @return {@code true} if the View handles hiding the translation.
+     */
+    boolean onHideTranslation(@NonNull View view);
+    /**
+     * Called when the user finish the Ui translation and no longer to show the translated text.
+     *
+     * @return {@code true} if the View handles clearing the translation.
+     */
+    boolean onClearTranslation(@NonNull View view);
+}
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index a63305e..287c182 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -20,12 +20,10 @@
 import android.text.Editable;
 import android.text.Selection;
 import android.text.Spanned;
-import android.text.TextUtils;
 import android.text.method.WordIterator;
 import android.text.style.SpellCheckSpan;
 import android.text.style.SuggestionSpan;
 import android.util.Log;
-import android.util.LruCache;
 import android.util.Range;
 import android.view.textservice.SentenceSuggestionsInfo;
 import android.view.textservice.SpellCheckerSession;
@@ -98,10 +96,6 @@
 
     private Runnable mSpellRunnable;
 
-    private static final int SUGGESTION_SPAN_CACHE_SIZE = 10;
-    private final LruCache<Long, SuggestionSpan> mSuggestionSpanCache =
-            new LruCache<Long, SuggestionSpan>(SUGGESTION_SPAN_CACHE_SIZE);
-
     public SpellChecker(TextView textView) {
         mTextView = textView;
 
@@ -144,7 +138,6 @@
 
         // Remove existing misspelled SuggestionSpans
         mTextView.removeMisspelledSpans((Editable) mTextView.getText());
-        mSuggestionSpanCache.evictAll();
     }
 
     private void setLocale(Locale locale) {
@@ -410,16 +403,7 @@
                     }
                     if (spellCheckSpanStart >= 0 && spellCheckSpanEnd > spellCheckSpanStart
                             && end > start) {
-                        final Long key = Long.valueOf(TextUtils.packRangeInLong(start, end));
-                        final SuggestionSpan tempSuggestionSpan = mSuggestionSpanCache.get(key);
-                        if (tempSuggestionSpan != null) {
-                            if (DBG) {
-                                Log.i(TAG, "Remove existing misspelled span. "
-                                        + editable.subSequence(start, end));
-                            }
-                            editable.removeSpan(tempSuggestionSpan);
-                            mSuggestionSpanCache.remove(key);
-                        }
+                        removeErrorSuggestionSpan(editable, start, end, RemoveReason.OBSOLETE);
                     }
                 }
                 return spellCheckSpan;
@@ -428,6 +412,35 @@
         return null;
     }
 
+    private enum RemoveReason {
+        /**
+         * Indicates the previous SuggestionSpan is replaced by a new SuggestionSpan.
+         */
+        REPLACE,
+        /**
+         * Indicates the previous SuggestionSpan is removed because corresponding text is
+         * considered as valid words now.
+         */
+        OBSOLETE,
+    }
+
+    private static void removeErrorSuggestionSpan(
+            Editable editable, int start, int end, RemoveReason reason) {
+        SuggestionSpan[] spans = editable.getSpans(start, end, SuggestionSpan.class);
+        for (SuggestionSpan span : spans) {
+            if (editable.getSpanStart(span) == start
+                    && editable.getSpanEnd(span) == end
+                    && (span.getFlags() & (SuggestionSpan.FLAG_MISSPELLED
+                    | SuggestionSpan.FLAG_GRAMMAR_ERROR)) != 0) {
+                if (DBG) {
+                    Log.i(TAG, "Remove existing misspelled/grammar error span on "
+                            + editable.subSequence(start, end) + ", reason: " + reason);
+                }
+                editable.removeSpan(span);
+            }
+        }
+    }
+
     @Override
     public void onGetSuggestions(SuggestionsInfo[] results) {
         final Editable editable = (Editable) mTextView.getText();
@@ -543,16 +556,7 @@
         }
         SuggestionSpan suggestionSpan =
                 new SuggestionSpan(mTextView.getContext(), suggestions, flags);
-        final Long key = Long.valueOf(TextUtils.packRangeInLong(start, end));
-        final SuggestionSpan tempSuggestionSpan = mSuggestionSpanCache.get(key);
-        if (tempSuggestionSpan != null) {
-            if (DBG) {
-                Log.i(TAG, "Cached span on the same position is cleard. "
-                        + editable.subSequence(start, end));
-            }
-            editable.removeSpan(tempSuggestionSpan);
-        }
-        mSuggestionSpanCache.put(key, suggestionSpan);
+        removeErrorSuggestionSpan(editable, start, end, RemoveReason.REPLACE);
         editable.setSpan(suggestionSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
 
         mTextView.invalidateRegion(start, end, false /* No cursor involved */);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 940a3c9..6733c0d3 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -195,7 +195,9 @@
 import android.view.textservice.SpellCheckerSubtype;
 import android.view.textservice.TextServicesManager;
 import android.view.translation.TranslationRequestValue;
+import android.view.translation.TranslationSpec;
 import android.view.translation.UiTranslationController;
+import android.view.translation.ViewTranslationCallback;
 import android.view.translation.ViewTranslationRequest;
 import android.view.translation.ViewTranslationResponse;
 import android.widget.RemoteViews.RemoteView;
@@ -203,6 +205,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastMath;
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.EditableInputConnection;
@@ -737,7 +740,7 @@
     private MovementMethod mMovement;
 
     private TransformationMethod mTransformation;
-    private TranslationTransformationMethod mTranslationTransformation;
+    private TextViewTranslationCallback mDefaultTranslationCallback;
     @UnsupportedAppUsage
     private boolean mAllowTransformationLengthChange;
     @UnsupportedAppUsage
@@ -13857,136 +13860,104 @@
     }
 
     /**
-     * Provides a {@link ViewTranslationRequest} that represents the content to be translated via
-     * translation service.
+     * Returns a {@link ViewTranslationRequest} which represents the content to be translated.
      *
-     * <p>NOTE: When overriding the method, it should not translate the password. We also suggest
-     * that not translating the text is selectable or editable. We use the transformation method to
-     * implement showing the translated text. The TextView does not support the transformation
-     * method text length change. If the text is selectable or editable, it will crash while
-     * selecting the text. To support it, it needs broader changes to text APIs, we only allow to
-     * translate non selectable and editable text now.
+     * <p>NOTE: When overriding the method, it should not translate the password. If the subclass
+     * uses {@link TransformationMethod} to display the translated result, it's also not recommend
+     * to translate text is selectable or editable.
      *
-     * @hide
+     * @param supportedFormats the supported translation format. The value could be {@link
+     *                         android.view.translation.TranslationSpec#DATA_FORMAT_TEXT}.
+     * @return the {@link ViewTranslationRequest} which contains the information to be translated.
      */
     @Nullable
     @Override
-    public ViewTranslationRequest onCreateTranslationRequest() {
-        if (mText == null || mText.length() == 0) {
+    public ViewTranslationRequest createTranslationRequest(@NonNull int[] supportedFormats) {
+        if (supportedFormats == null || supportedFormats.length == 0) {
             // TODO(b/182433547): remove before S release
             if (UiTranslationController.DEBUG) {
-                Log.w(LOG_TAG, "Cannot create translation request for the empty text.");
+                Log.w(LOG_TAG, "Do not provide the support translation formats.");
             }
             return null;
         }
-        // Not translate password, editable text and not important for translation
-        // TODO(b/177214256): support selectable text translation. It needs to broader changes to
-        //  text selection apis, not support in S.
-        boolean isPassword = isAnyPasswordInputType() || hasPasswordTransformationMethod();
-        if (isTextEditable() || isPassword || isTextSelectable()) {
-            // TODO(b/182433547): remove before S release
-            if (UiTranslationController.DEBUG) {
-                Log.w(LOG_TAG, "Cannot create translation request. editable = " + isTextEditable()
-                        + ", isPassword = " + isPassword + ", selectable = " + isTextSelectable());
+        ViewTranslationRequest.Builder requestBuilder =
+                new ViewTranslationRequest.Builder(getAutofillId());
+        // Support Text translation
+        if (ArrayUtils.contains(supportedFormats, TranslationSpec.DATA_FORMAT_TEXT)) {
+            if (mText == null || mText.length() == 0) {
+                // TODO(b/182433547): remove before S release
+                if (UiTranslationController.DEBUG) {
+                    Log.w(LOG_TAG, "Cannot create translation request for the empty text.");
+                }
+                return null;
             }
-            return null;
+            boolean isPassword = isAnyPasswordInputType() || hasPasswordTransformationMethod();
+            // TODO(b/177214256): support selectable text translation.
+            //  We use the TransformationMethod to implement showing the translated text. The
+            //  TextView does not support the text length change for TransformationMethod. If the
+            //  text is selectable or editable, it will crash while selecting the text. To support
+            //  it, it needs broader changes to text APIs, we only allow to translate non selectable
+            //  and editable text in S.
+            if (isTextEditable() || isPassword || isTextSelectable()) {
+                // TODO(b/182433547): remove before S release
+                if (UiTranslationController.DEBUG) {
+                    Log.w(LOG_TAG, "Cannot create translation request. editable = "
+                            + isTextEditable() + ", isPassword = " + isPassword + ", selectable = "
+                            + isTextSelectable());
+                }
+                return null;
+            }
+            // TODO(b/176488462): apply the view's important for translation
+            requestBuilder.setValue(ViewTranslationRequest.ID_TEXT,
+                    TranslationRequestValue.forText(mText));
         }
-        // TODO(b/176488462): apply the view's important for translation property
-        // TODO(b/174283799): remove the spans from the mText and save the spans information
-        // TODO: use fixed ids for request texts.
-        ViewTranslationRequest request =
-                new ViewTranslationRequest.Builder(getAutofillId())
-                        .setValue(ViewTranslationRequest.ID_TEXT,
-                                TranslationRequestValue.forText(mText))
-                        .build();
-        return request;
+        return requestBuilder.build();
     }
 
     /**
-     * Provides the implementation that pauses the ongoing Ui translation, it will show the original
-     * text instead of the translated text and restore the original transformation method.
+     * Returns a {@link ViewTranslationCallback} that is used to display the translated information.
+     * The default implementation will use a {@link TransformationMethod} that allow to replace the
+     * current {@link TransformationMethod} to transform the original text to the translated text
+     * display.
      *
-     * <p>NOTE: If this method is overridden, other translation related methods such as
-     * {@link onRestoreUiTranslation}, {@link onFinishUiTranslation}, {@link onTranslationComplete}
-     * should also be overridden.
-     *
-     * @hide
+     * @return a {@link ViewTranslationCallback} that is used to control how to display the
+     * translated information or {@code null} if this View doesn't support translation.
      */
+    @Nullable
     @Override
-    public void onPauseUiTranslation() {
-        // Restore to original text content.
-        if (mTranslationTransformation != null) {
-            setTransformationMethod(mTranslationTransformation.getOriginalTransformationMethod());
-        } else {
-            // TODO(b/182433547): remove before S release
-            Log.w(LOG_TAG, "onPauseUiTranslation(): no translated text.");
+    public ViewTranslationCallback getViewTranslationCallback() {
+        return getDefaultViewTranslationCallback();
+    }
+
+    private ViewTranslationCallback getDefaultViewTranslationCallback() {
+        if (mDefaultTranslationCallback == null) {
+            mDefaultTranslationCallback = new TextViewTranslationCallback();
         }
+        return mDefaultTranslationCallback;
     }
 
     /**
-     * Provides the implementation that restoes the paused Ui translation, it will show the
-     * translated text again if the text had been translated. This method will replace the current
-     * tansformation method with {@link TranslationTransformationMethod}.
      *
-     * <p>NOTE: If this method is overridden, other translation related methods such as
-     * {@link onPauseUiTranslation}, {@link onFinishUiTranslation}, {@link onTranslationComplete}
-     * should also be overridden.
+     * Called when the content from {@link #createTranslationRequest} had been translated by the
+     * TranslationService. The default implementation will replace the current
+     * {@link TransformationMethod} to transform the original text to the translated text display.
      *
-     * @hide
+     * @param response a {@link ViewTranslationResponse} that contains the translated information
+     * which can be shown in the view.
      */
     @Override
-    public void onRestoreUiTranslation() {
-        if (mTranslationTransformation != null) {
-            setTransformationMethod(mTranslationTransformation);
-        } else {
-            // TODO(b/182433547): remove before S release
-            Log.w(LOG_TAG, "onRestoreUiTranslation(): no translated text.");
-        }
-    }
-
-    /**
-     * Provides the implementation that finishes the current Ui translation and it's no longer to
-     * show the translated text. This method restores the original transformation method and resets
-     * the saved {@link TranslationTransformationMethod}.
-     *
-     * <p>NOTE: If this method is overridden, other translation related methods such as
-     * {@link onPauseUiTranslation}, {@link onRestoreUiTranslation}, {@link onTranslationComplete}
-     * should also be overridden.
-     *
-     * @hide
-     */
-    @Override
-    public void onFinishUiTranslation() {
-        // Restore to original text content and clear TranslationTransformation
-        if (mTranslationTransformation != null) {
-            setTransformationMethod(mTranslationTransformation.getOriginalTransformationMethod());
-            mTranslationTransformation = null;
-        } else {
-            // TODO(b/182433547): remove before S release
-            Log.w(LOG_TAG, "onFinishUiTranslation(): no translated text.");
-        }
-    }
-
-    /**
-     * Default {@link TextView} implementation after the translation request is done by the
-     * translation service, it's ok to show the translated text. This method will save the original
-     * transformation method and replace the current transformation method with
-     * {@link TranslationTransformationMethod}.
-     *
-     * <p>NOTE: If this method is overridden, other translation related methods such as
-     * {@link onPauseUiTranslation}, {@link onRestoreUiTranslation}, {@link onFinishUiTranslation}
-     * should also be overridden.
-     *
-     * @hide
-     */
-    @Override
-    public void onTranslationComplete(@NonNull ViewTranslationResponse response) {
-        // Show the translated text.
-        TransformationMethod originalTranslationMethod = mTranslationTransformation != null
-                ? mTranslationTransformation.getOriginalTransformationMethod() : mTransformation;
-        mTranslationTransformation =
+    public void onTranslationResponse(@NonNull ViewTranslationResponse response) {
+        // TODO(b/183467275): Use the overridden ViewTranslationCallback instead of our default
+        //  implementation if the view has overridden getViewTranslationCallback.
+        TextViewTranslationCallback callback =
+                (TextViewTranslationCallback) getDefaultViewTranslationCallback();
+        TranslationTransformationMethod oldTranslationMethod =
+                callback.getTranslationTransformation();
+        TransformationMethod originalTranslationMethod = oldTranslationMethod != null
+                ? oldTranslationMethod.getOriginalTransformationMethod() : mTransformation;
+        TranslationTransformationMethod newTranslationMethod =
                 new TranslationTransformationMethod(response, originalTranslationMethod);
         // TODO(b/178353965): well-handle setTransformationMethod.
-        setTransformationMethod(mTranslationTransformation);
+        callback.setTranslationTransformation(newTranslationMethod);
     }
 }
diff --git a/core/java/android/widget/TextViewTranslationCallback.java b/core/java/android/widget/TextViewTranslationCallback.java
new file mode 100644
index 0000000..296d93c
--- /dev/null
+++ b/core/java/android/widget/TextViewTranslationCallback.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.annotation.NonNull;
+import android.text.method.TranslationTransformationMethod;
+import android.util.Log;
+import android.view.View;
+import android.view.translation.UiTranslationManager;
+import android.view.translation.ViewTranslationCallback;
+import android.view.translation.ViewTranslationResponse;
+
+/**
+ * Default implementation for {@link ViewTranslationCallback} for {@link TextView} components.
+ * This class handles how to display the translated information for {@link TextView}.
+ *
+ * @hide
+ */
+public class TextViewTranslationCallback implements ViewTranslationCallback {
+
+    private static final String TAG = "TextViewTranslationCallback";
+
+    private static final boolean DEBUG = Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG);
+
+    private TranslationTransformationMethod mTranslationTransformation;
+
+    /**
+     * Invoked by the platform when receiving the successful {@link ViewTranslationResponse} for the
+     * view that provides the translatable information by {@link View#createTranslationRequest} and
+     * sent by the platform.
+     */
+    void setTranslationTransformation(TranslationTransformationMethod method) {
+        if (method == null) {
+            if (DEBUG) {
+                Log.w(TAG, "setTranslationTransformation: should not set null "
+                        + "TranslationTransformationMethod");
+            }
+            return;
+        }
+        mTranslationTransformation = method;
+    }
+
+    TranslationTransformationMethod getTranslationTransformation() {
+        return mTranslationTransformation;
+    }
+
+    private void clearTranslationTransformation() {
+        if (DEBUG) {
+            Log.v(TAG, "clearTranslationTransformation: " + mTranslationTransformation);
+        }
+        mTranslationTransformation = null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean onShowTranslation(@NonNull View view) {
+        if (mTranslationTransformation != null) {
+            ((TextView) view).setTransformationMethod(mTranslationTransformation);
+        } else {
+            if (DEBUG) {
+                // TODO(b/182433547): remove before S release
+                Log.w(TAG, "onShowTranslation(): no translated text.");
+            }
+        }
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean onHideTranslation(@NonNull View view) {
+        // Restore to original text content.
+        if (mTranslationTransformation != null) {
+            ((TextView) view).setTransformationMethod(
+                    mTranslationTransformation.getOriginalTransformationMethod());
+        } else {
+            if (DEBUG) {
+                // TODO(b/182433547): remove before S release
+                Log.w(TAG, "onHideTranslation(): no translated text.");
+            }
+        }
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean onClearTranslation(@NonNull View view) {
+        // Restore to original text content and clear TranslationTransformation
+        if (mTranslationTransformation != null) {
+            ((TextView) view).setTransformationMethod(
+                    mTranslationTransformation.getOriginalTransformationMethod());
+            clearTranslationTransformation();
+        } else {
+            if (DEBUG) {
+                // TODO(b/182433547): remove before S release
+                Log.w(TAG, "onClearTranslation(): no translated text.");
+            }
+        }
+        return true;
+    }
+}
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index 922f96e..3b6a877 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -221,32 +221,19 @@
 
     @Override
     protected void showNoPersonalAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
-        if (mIsSendAction) {
-            showEmptyState(listAdapter,
-                    R.drawable.ic_no_apps,
-                    R.string.resolver_no_personal_apps_available_share,
-                    /* subtitleRes */ 0);
-        } else {
-            showEmptyState(listAdapter,
-                    R.drawable.ic_no_apps,
-                    R.string.resolver_no_personal_apps_available_resolve,
-                    /* subtitleRes */ 0);
-        }
+        showEmptyState(listAdapter,
+                R.drawable.ic_no_apps,
+                R.string.resolver_no_personal_apps_available,
+                /* subtitleRes */ 0);
+
     }
 
     @Override
     protected void showNoWorkAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
-        if (mIsSendAction) {
-            showEmptyState(listAdapter,
-                    R.drawable.ic_no_apps,
-                    R.string.resolver_no_work_apps_available_share,
-                    /* subtitleRes */ 0);
-        } else {
-            showEmptyState(listAdapter,
-                    R.drawable.ic_no_apps,
-                    R.string.resolver_no_work_apps_available_resolve,
-                    /* subtitleRes */ 0);
-        }
+        showEmptyState(listAdapter,
+                R.drawable.ic_no_apps,
+                R.string.resolver_no_work_apps_available,
+                /* subtitleRes */ 0);
     }
 
     void setEmptyStateBottomOffset(int bottomOffset) {
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 2a022e6..e273286 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -229,15 +229,14 @@
     /**
      * Set configuration and pass read-only data to hotword detection service.
      *
-     * @param options Application configuration data provided by the
-     * {@link VoiceInteractionService}. PersistableBundle does not allow any remotable objects or
+     * @param options Application configuration data to provide to the
+     * {@link HotwordDetectionService}. PersistableBundle does not allow any remotable objects or
      * other contents that can be used to communicate with other processes.
-     * @param sharedMemory The unrestricted data blob provided by the
-     * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+     * @param sharedMemory The unrestricted data blob to provide to the
+     * {@link HotwordDetectionService}. Use this to provide the hotword models data or other
      * such data to the trusted process.
      */
-    void setHotwordDetectionServiceConfig(
-            in PersistableBundle options, in SharedMemory sharedMemory);
+    void updateState(in PersistableBundle options, in SharedMemory sharedMemory);
 
     /**
      * Requests to shutdown hotword detection service.
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index a2f014c..622f166 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -221,7 +221,7 @@
     protected void showNoPersonalAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
         showEmptyState(listAdapter,
                 R.drawable.ic_no_apps,
-                R.string.resolver_no_personal_apps_available_resolve,
+                R.string.resolver_no_personal_apps_available,
                 /* subtitleRes */ 0);
     }
 
@@ -229,7 +229,7 @@
     protected void showNoWorkAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
         showEmptyState(listAdapter,
                 R.drawable.ic_no_apps,
-                R.string.resolver_no_work_apps_available_resolve,
+                R.string.resolver_no_work_apps_available,
                 /* subtitleRes */ 0);
     }
 
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index 762297d..86f29a8 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -106,13 +106,17 @@
     }
 
     private String resolveTitle() {
-        final int titleId = (mSuppliedDialogInfo != null) ? mSuppliedDialogInfo.getTitleResId()
-                : ID_NULL;
-        if (titleId != ID_NULL && mSuspendingAppResources != null) {
-            try {
-                return mSuspendingAppResources.getString(titleId);
-            } catch (Resources.NotFoundException nfe) {
-                Slog.e(TAG, "Could not resolve string resource id " + titleId);
+        if (mSuppliedDialogInfo != null) {
+            final int titleId = mSuppliedDialogInfo.getTitleResId();
+            final String title = mSuppliedDialogInfo.getTitle();
+            if (titleId != ID_NULL && mSuspendingAppResources != null) {
+                try {
+                    return mSuspendingAppResources.getString(titleId);
+                } catch (Resources.NotFoundException nfe) {
+                    Slog.e(TAG, "Could not resolve string resource id " + titleId);
+                }
+            } else if (title != null) {
+                return title;
             }
         }
         return getString(R.string.app_suspended_title);
@@ -159,13 +163,17 @@
                 Slog.w(TAG, "Unknown neutral button action: " + mNeutralButtonAction);
                 return null;
         }
-        final int buttonTextId = (mSuppliedDialogInfo != null)
-                ? mSuppliedDialogInfo.getNeutralButtonTextResId() : ID_NULL;
-        if (buttonTextId != ID_NULL && mSuspendingAppResources != null) {
-            try {
-                return mSuspendingAppResources.getString(buttonTextId);
-            } catch (Resources.NotFoundException nfe) {
-                Slog.e(TAG, "Could not resolve string resource id " + buttonTextId);
+        if (mSuppliedDialogInfo != null) {
+            final int buttonTextId = mSuppliedDialogInfo.getNeutralButtonTextResId();
+            final String buttonText = mSuppliedDialogInfo.getNeutralButtonText();
+            if (buttonTextId != ID_NULL && mSuspendingAppResources != null) {
+                try {
+                    return mSuspendingAppResources.getString(buttonTextId);
+                } catch (Resources.NotFoundException nfe) {
+                    Slog.e(TAG, "Could not resolve string resource id " + buttonTextId);
+                }
+            } else if (buttonText != null) {
+                return buttonText;
             }
         }
         return getString(defaultButtonTextId);
diff --git a/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
index 1c222a7..9a02b7b 100644
--- a/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
@@ -40,8 +40,7 @@
         overrides = new HashMap<>();
         for (int i = 0; i < keyCount; i++) {
             long key = in.readLong();
-            PackageOverride override = in.readParcelable(PackageOverride.class.getClassLoader());
-            overrides.put(key, override);
+            overrides.put(key, PackageOverride.createFromParcel(in));
         }
     }
 
@@ -55,7 +54,7 @@
         dest.writeInt(overrides.size());
         for (Long key : overrides.keySet()) {
             dest.writeLong(key);
-            dest.writeParcelable(overrides.get(key), 0);
+            overrides.get(key).writeToParcel(dest);
         }
     }
 
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 249c134..afcd0b0 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -151,15 +151,23 @@
     void setOverrides(in CompatibilityChangeConfig overrides, in String packageName);
 
     /**
-     * Adds overrides to compatibility changes.
+     * Adds overrides to compatibility changes on release builds.
      *
-     * <p>Kills the app to allow the changes to take effect.
+     * <p>The caller to this API needs to hold
+     * {@code android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD} and all change ids
+     * in {@code overrides} need to annotated with {@link android.compat.annotation.Overridable}.
+     *
+     * A release build in this definition means that {@link android.os.Build#IS_DEBUGGABLE} needs to
+     * be {@code false}.
+     *
+     * <p>Note that this does not kill the app, and therefore overrides read from the app process
+     * will not be updated. Overrides read from the system process do take effect.
      *
      * @param overrides   parcelable containing the compat change overrides to be applied
      * @param packageName the package name of the app whose changes will be overridden
      * @throws SecurityException if overriding changes is not permitted
      */
-    void setOverridesFromInstaller(in CompatibilityOverrideConfig overrides, in String packageName);
+    void setOverridesOnReleaseBuilds(in CompatibilityOverrideConfig overrides, in String packageName);
 
     /**
      * Adds overrides to compatibility changes.
diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java
index fae5862..6776c27 100644
--- a/core/java/com/android/internal/display/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java
@@ -20,6 +20,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Looper;
@@ -28,6 +29,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.MathUtils;
+import android.view.Display;
 
 import java.util.LinkedList;
 import java.util.Queue;
@@ -52,6 +54,7 @@
     // This value is approximately 1/3 of the smallest possible brightness value.
     public static final float EPSILON = 0.001f;
 
+    private DisplayManager mDisplayManager;
     private final Context mContext;
 
     private final Queue<Object> mWriteHistory = new LinkedList<>();
@@ -87,11 +90,15 @@
      * value, if float is invalid. If both are invalid, use default float value from config.
      */
     public void startSynchronizing() {
+        if (mDisplayManager == null) {
+            mDisplayManager = mContext.getSystemService(DisplayManager.class);
+        }
+
         final BrightnessSyncObserver brightnessSyncObserver;
         brightnessSyncObserver = new BrightnessSyncObserver(mHandler);
         brightnessSyncObserver.startObserving();
 
-        final float currentFloatBrightness = getScreenBrightnessFloat(mContext);
+        final float currentFloatBrightness = getScreenBrightnessFloat();
         final int currentIntBrightness = getScreenBrightnessInt(mContext);
 
         if (!Float.isNaN(currentFloatBrightness)) {
@@ -101,9 +108,7 @@
         } else {
             final float defaultBrightness = mContext.getResources().getFloat(
                     com.android.internal.R.dimen.config_screenBrightnessSettingDefaultFloat);
-            Settings.System.putFloatForUser(mContext.getContentResolver(),
-                    Settings.System.SCREEN_BRIGHTNESS_FLOAT, defaultBrightness,
-                    UserHandle.USER_CURRENT);
+            mDisplayManager.setBrightness(Display.DEFAULT_DISPLAY, defaultBrightness);
 
         }
     }
@@ -135,7 +140,7 @@
     /**
      * Translates specified value from the float brightness system to the int brightness system,
      * given the min/max of each range.  Accounts for special values such as OFF and invalid values.
-     * Value returned as a float privimite (to preserve precision), but is a value within the
+     * Value returned as a float primitive (to preserve precision), but is a value within the
      * int-system range.
      */
     public static float brightnessFloatToIntRange(float brightnessFloat) {
@@ -152,10 +157,8 @@
         }
     }
 
-    private static float getScreenBrightnessFloat(Context context) {
-        return Settings.System.getFloatForUser(context.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_FLOAT, PowerManager.BRIGHTNESS_INVALID_FLOAT,
-                UserHandle.USER_CURRENT);
+    private float getScreenBrightnessFloat() {
+        return mDisplayManager.getBrightness(Display.DEFAULT_DISPLAY);
     }
 
     private static int getScreenBrightnessInt(Context context) {
@@ -184,9 +187,7 @@
             float newBrightnessFloat = brightnessIntToFloat(value);
             mWriteHistory.offer(newBrightnessFloat);
             mPreferredSettingValue = newBrightnessFloat;
-            Settings.System.putFloatForUser(mContext.getContentResolver(),
-                    Settings.System.SCREEN_BRIGHTNESS_FLOAT, newBrightnessFloat,
-                    UserHandle.USER_CURRENT);
+            mDisplayManager.setBrightness(Display.DEFAULT_DISPLAY, newBrightnessFloat);
         }
     }
 
@@ -255,7 +256,7 @@
                 mHandler.removeMessages(MSG_UPDATE_FLOAT);
                 mHandler.obtainMessage(MSG_UPDATE_FLOAT, currentBrightness, 0).sendToTarget();
             } else if (BRIGHTNESS_FLOAT_URI.equals(uri)) {
-                float currentFloat = getScreenBrightnessFloat(mContext);
+                float currentFloat = getScreenBrightnessFloat();
                 int toSend = Float.floatToIntBits(currentFloat);
                 mHandler.removeMessages(MSG_UPDATE_INT);
                 mHandler.obtainMessage(MSG_UPDATE_INT, toSend, 0).sendToTarget();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index ec0a8d8..b0920b0 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -170,7 +170,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    static final int VERSION = 195;
+    static final int VERSION = 196;
 
     // The maximum number of names wakelocks we will keep track of
     // per uid; once the limit is reached, we batch the remaining wakelocks
@@ -1014,6 +1014,9 @@
     @Nullable BluetoothPowerCalculator mBluetoothPowerCalculator = null;
     /** Cpu Power calculator for attributing measured cpu charge consumption to uids */
     @Nullable CpuPowerCalculator mCpuPowerCalculator = null;
+    /** Mobile Radio Power calculator for attributing measured radio charge consumption to uids */
+    @Nullable
+    MobileRadioPowerCalculator mMobileRadioPowerCalculator = null;
     /** Wifi Power calculator for attributing measured wifi charge consumption to uids */
     @Nullable WifiPowerCalculator mWifiPowerCalculator = null;
 
@@ -6983,6 +6986,16 @@
     }
 
     @Override
+    public long getGnssMeasuredBatteryConsumptionUC() {
+        return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_GNSS);
+    }
+
+    @Override
+    public long getMobileRadioMeasuredBatteryConsumptionUC() {
+        return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO);
+    }
+
+    @Override
     public long getScreenOnMeasuredBatteryConsumptionUC() {
         return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
     }
@@ -7842,6 +7855,16 @@
         }
 
         @Override
+        public long getGnssMeasuredBatteryConsumptionUC() {
+            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_GNSS);
+        }
+
+        @Override
+        public long getMobileRadioMeasuredBatteryConsumptionUC() {
+            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO);
+        }
+
+        @Override
         public long getScreenOnMeasuredBatteryConsumptionUC() {
             return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
         }
@@ -7880,6 +7903,27 @@
             return (topTimeUs < fgTimeUs) ? topTimeUs : fgTimeUs;
         }
 
+
+        /**
+         * Gets the uid's time spent using the GNSS since last marked. Also sets the mark time for
+         * the GNSS timer.
+         */
+        private long markGnssTimeUs(long elapsedRealtimeMs) {
+            final Sensor sensor = mSensorStats.get(Sensor.GPS);
+            if (sensor == null) {
+                return 0;
+            }
+
+            final StopwatchTimer timer = sensor.mTimer;
+            if (timer == null) {
+                return 0;
+            }
+
+            final long gnssTimeUs = timer.getTimeSinceMarkLocked(elapsedRealtimeMs * 1000);
+            timer.setMark(elapsedRealtimeMs);
+            return gnssTimeUs;
+        }
+
         public StopwatchTimer createAudioTurnedOnTimerLocked() {
             if (mAudioTurnedOnTimer == null) {
                 mAudioTurnedOnTimer = new StopwatchTimer(mBsi.mClocks, Uid.this, AUDIO_TURNED_ON,
@@ -11823,7 +11867,7 @@
      * Distribute Cell radio energy info and network traffic to apps.
      */
     public void noteModemControllerActivity(@Nullable final ModemActivityInfo activityInfo,
-            long elapsedRealtimeMs, long uptimeMs) {
+            final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) {
         if (DEBUG_ENERGY) {
             Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
         }
@@ -11854,6 +11898,16 @@
                 return;
             }
 
+            final SparseDoubleArray uidEstimatedConsumptionMah;
+            if (consumedChargeUC > 0 && mMobileRadioPowerCalculator != null
+                    && mGlobalMeasuredEnergyStats != null) {
+                mGlobalMeasuredEnergyStats.updateStandardBucket(
+                        MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO, consumedChargeUC);
+                uidEstimatedConsumptionMah = new SparseDoubleArray();
+            } else {
+                uidEstimatedConsumptionMah = null;
+            }
+
             if (deltaInfo != null) {
                 mHasModemReporting = true;
                 mModemActivity.getIdleTimeCounter().addCountLocked(
@@ -11898,7 +11952,7 @@
                     mTmpRailStats.resetCellularTotalEnergyUsed();
                 }
             }
-            long radioTimeUs = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked(
+            long totalAppRadioTimeUs = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked(
                     elapsedRealtimeMs * 1000);
             mMobileRadioActivePerAppTimer.setMark(elapsedRealtimeMs);
 
@@ -11958,12 +12012,21 @@
 
                         // Distribute total radio active time in to this app.
                         final long appPackets = entry.rxPackets + entry.txPackets;
-                        final long appRadioTimeUs = (radioTimeUs * appPackets) / totalPackets;
+                        final long appRadioTimeUs =
+                                (totalAppRadioTimeUs * appPackets) / totalPackets;
                         u.noteMobileRadioActiveTimeLocked(appRadioTimeUs);
 
+                        // Distribute measured mobile radio charge consumption based on app radio
+                        // active time
+                        if (uidEstimatedConsumptionMah != null) {
+                            uidEstimatedConsumptionMah.add(u.getUid(),
+                                    mMobileRadioPowerCalculator.calcPowerFromRadioActiveDurationMah(
+                                            appRadioTimeUs / 1000));
+                        }
+
                         // Remove this app from the totals, so that we don't lose any time
                         // due to rounding.
-                        radioTimeUs -= appRadioTimeUs;
+                        totalAppRadioTimeUs -= appRadioTimeUs;
                         totalPackets -= appPackets;
 
                         if (deltaInfo != null) {
@@ -11988,12 +12051,51 @@
                     }
                 }
 
-                if (radioTimeUs > 0) {
+                if (totalAppRadioTimeUs > 0) {
                     // Whoops, there is some radio time we can't blame on an app!
-                    mMobileRadioActiveUnknownTime.addCountLocked(radioTimeUs);
+                    mMobileRadioActiveUnknownTime.addCountLocked(totalAppRadioTimeUs);
                     mMobileRadioActiveUnknownCount.addCountLocked(1);
                 }
 
+
+                // Update the MeasuredEnergyStats information.
+                if (uidEstimatedConsumptionMah != null) {
+                    double totalEstimatedConsumptionMah = 0.0;
+
+                    // Estimate total active radio power consumption since last mark.
+                    final long totalRadioTimeMs = mMobileRadioActiveTimer.getTimeSinceMarkLocked(
+                            elapsedRealtimeMs * 1000) / 1000;
+                    mMobileRadioActiveTimer.setMark(elapsedRealtimeMs);
+                    totalEstimatedConsumptionMah +=
+                            mMobileRadioPowerCalculator.calcPowerFromRadioActiveDurationMah(
+                                    totalRadioTimeMs);
+
+                    // Estimate idle power consumption at each signal strength level
+                    final int numSignalStrengthLevels = mPhoneSignalStrengthsTimer.length;
+                    for (int strengthLevel = 0; strengthLevel < numSignalStrengthLevels;
+                            strengthLevel++) {
+                        final long strengthLevelDurationMs =
+                                mPhoneSignalStrengthsTimer[strengthLevel].getTimeSinceMarkLocked(
+                                        elapsedRealtimeMs * 1000) / 1000;
+                        mPhoneSignalStrengthsTimer[strengthLevel].setMark(elapsedRealtimeMs);
+
+                        totalEstimatedConsumptionMah +=
+                                mMobileRadioPowerCalculator.calcIdlePowerAtSignalStrengthMah(
+                                        strengthLevelDurationMs, strengthLevel);
+                    }
+
+                    // Estimate total active radio power consumption since last mark.
+                    final long scanTimeMs = mPhoneSignalScanningTimer.getTimeSinceMarkLocked(
+                            elapsedRealtimeMs * 1000) / 1000;
+                    mPhoneSignalScanningTimer.setMark(elapsedRealtimeMs);
+                    totalEstimatedConsumptionMah +=
+                            mMobileRadioPowerCalculator.calcScanTimePowerMah(scanTimeMs);
+
+                    distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
+                            consumedChargeUC, uidEstimatedConsumptionMah,
+                            totalEstimatedConsumptionMah);
+                }
+
                 mNetworkStatsPool.release(delta);
                 delta = null;
             }
@@ -12453,7 +12555,7 @@
         // 'double counted' and will simply exceed the realtime that elapsed.
         // If multidisplay becomes a reality, this is probably more reasonable than pooling.
 
-        // On the first pass, collect total time since mark so that we can normalize power.
+        // Collect total time since mark so that we can normalize power.
         final SparseDoubleArray fgTimeUsArray = new SparseDoubleArray();
         final long elapsedRealtimeUs = elapsedRealtimeMs * 1000;
         // TODO(b/175726779): Update and optimize the algorithm (e.g. avoid iterating over ALL uids)
@@ -12468,6 +12570,50 @@
     }
 
     /**
+     * Accumulate GNSS charge consumption and distribute it to the correct state and the apps.
+     *
+     * @param chargeUC amount of charge (microcoulombs) used by GNSS since this was last called.
+     */
+    @GuardedBy("this")
+    public void updateGnssMeasuredEnergyStatsLocked(long chargeUC, long elapsedRealtimeMs) {
+        if (DEBUG_ENERGY) Slog.d(TAG, "Updating gnss stats: " + chargeUC);
+        if (mGlobalMeasuredEnergyStats == null) {
+            return;
+        }
+
+        if (!mOnBatteryInternal || chargeUC <= 0) {
+            // There's nothing further to update.
+            return;
+        }
+        if (mIgnoreNextExternalStats) {
+            // Although under ordinary resets we won't get here, and typically a new sync will
+            // happen right after the reset, strictly speaking we need to set all mark times to now.
+            final int uidStatsSize = mUidStats.size();
+            for (int i = 0; i < uidStatsSize; i++) {
+                final Uid uid = mUidStats.valueAt(i);
+                uid.markGnssTimeUs(elapsedRealtimeMs);
+            }
+            return;
+        }
+
+        mGlobalMeasuredEnergyStats.updateStandardBucket(MeasuredEnergyStats.POWER_BUCKET_GNSS,
+                chargeUC);
+
+        // Collect the per uid time since mark so that we can normalize power.
+        final SparseDoubleArray gnssTimeUsArray = new SparseDoubleArray();
+        // TODO(b/175726779): Update and optimize the algorithm (e.g. avoid iterating over ALL uids)
+        final int uidStatsSize = mUidStats.size();
+        for (int i = 0; i < uidStatsSize; i++) {
+            final Uid uid = mUidStats.valueAt(i);
+            final long gnssTimeUs = uid.markGnssTimeUs(elapsedRealtimeMs);
+            if (gnssTimeUs == 0) continue;
+            gnssTimeUsArray.put(uid.getUid(), (double) gnssTimeUs);
+        }
+        distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_GNSS, chargeUC,
+                gnssTimeUsArray, 0);
+    }
+
+    /**
      * Accumulate Custom power bucket charge, globally and for each app.
      *
      * @param totalChargeUC charge (microcoulombs) used for this bucket since this was last called.
@@ -14394,6 +14540,9 @@
             if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU]) {
                 mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
             }
+            if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO]) {
+                mMobileRadioPowerCalculator = new MobileRadioPowerCalculator(mPowerProfile);
+            }
             if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_WIFI]) {
                 mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile);
             }
@@ -16525,6 +16674,8 @@
         // Pull the clock time.  This may update the time and make a new history entry
         // if we had originally pulled a time before the RTC was set.
         getStartClockTime();
+
+        updateSystemServiceCallStats();
     }
 
     public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index 4f99c94..f8ae0c1 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -21,9 +21,7 @@
 import android.os.BatteryStats;
 import android.os.BatteryUsageStats;
 import android.os.BatteryUsageStatsQuery;
-import android.os.Bundle;
 import android.os.UidBatteryConsumer;
-import android.os.UserHandle;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -88,29 +86,31 @@
     }
 
     /**
+     * Returns true if the last update was too long ago for the tolerances specified
+     * by the supplied queries.
+     */
+    public boolean shouldUpdateStats(List<BatteryUsageStatsQuery> queries,
+            long lastUpdateTimeStampMs) {
+        long allowableStatsAge = Long.MAX_VALUE;
+        for (int i = queries.size() - 1; i >= 0; i--) {
+            BatteryUsageStatsQuery query = queries.get(i);
+            allowableStatsAge = Math.min(allowableStatsAge, query.getMaxStatsAge());
+        }
+
+        return mStats.mClocks.elapsedRealtime() - lastUpdateTimeStampMs > allowableStatsAge;
+    }
+
+    /**
      * Returns snapshots of battery attribution data, one per supplied query.
      */
     public List<BatteryUsageStats> getBatteryUsageStats(List<BatteryUsageStatsQuery> queries) {
-
-        // TODO(b/174186345): instead of BatteryStatsHelper, use PowerCalculators directly.
-        final BatteryStatsHelper batteryStatsHelper = new BatteryStatsHelper(mContext,
-                false /* collectBatteryBroadcast */);
-        batteryStatsHelper.create((Bundle) null);
-        final List<UserHandle> users = new ArrayList<>();
-        for (int i = 0; i < queries.size(); i++) {
-            BatteryUsageStatsQuery query = queries.get(i);
-            for (int userId : query.getUserIds()) {
-                UserHandle userHandle = UserHandle.of(userId);
-                if (!users.contains(userHandle)) {
-                    users.add(userHandle);
-                }
-            }
-        }
-        batteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, users);
-
         ArrayList<BatteryUsageStats> results = new ArrayList<>(queries.size());
-        for (int i = 0; i < queries.size(); i++) {
-            results.add(getBatteryUsageStats(queries.get(i)));
+        synchronized (mStats) {
+            mStats.prepareForDumpLocked();
+
+            for (int i = 0; i < queries.size(); i++) {
+                results.add(getBatteryUsageStats(queries.get(i)));
+            }
         }
         return results;
     }
diff --git a/core/java/com/android/internal/os/GnssPowerCalculator.java b/core/java/com/android/internal/os/GnssPowerCalculator.java
index df25cda..97c4fd8 100644
--- a/core/java/com/android/internal/os/GnssPowerCalculator.java
+++ b/core/java/com/android/internal/os/GnssPowerCalculator.java
@@ -61,7 +61,17 @@
             long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
             double averageGnssPowerMa) {
         final long durationMs = computeDuration(u, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
-        double powerMah = computePower(durationMs, averageGnssPowerMa);
+
+        final long measuredChargeUC = u.getGnssMeasuredBatteryConsumptionUC();
+        final boolean isMeasuredPowerAvailable = !query.shouldForceUsePowerProfileModel()
+                && measuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE;
+
+        final double powerMah;
+        if (isMeasuredPowerAvailable) {
+            powerMah = uCtoMah(measuredChargeUC);
+        } else {
+            powerMah = computePower(durationMs, averageGnssPowerMa);
+        }
         app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_GNSS, durationMs)
                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS, powerMah);
     }
@@ -73,15 +83,25 @@
         for (int i = sippers.size() - 1; i >= 0; i--) {
             final BatterySipper app = sippers.get(i);
             if (app.drainType == BatterySipper.DrainType.APP) {
-                calculateApp(app, app.uidObj, rawRealtimeUs, statsType, averageGnssPowerMa);
+                calculateApp(app, app.uidObj, rawRealtimeUs, statsType, averageGnssPowerMa, false);
             }
         }
     }
 
     protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
-            int statsType, double averageGnssPowerMa) {
+            int statsType, double averageGnssPowerMa, boolean shouldForceUsePowerProfileModel) {
         final long durationMs = computeDuration(u, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
-        double powerMah = computePower(durationMs, averageGnssPowerMa);
+
+        final long measuredChargeUC = u.getGnssMeasuredBatteryConsumptionUC();
+        final boolean isMeasuredPowerAvailable = shouldForceUsePowerProfileModel
+                && measuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE;
+
+        final double powerMah;
+        if (isMeasuredPowerAvailable) {
+            powerMah = uCtoMah(measuredChargeUC);
+        } else {
+            powerMah = computePower(durationMs, averageGnssPowerMa);
+        }
 
         app.gpsTimeMs = durationMs;
         app.gpsPowerMah = powerMah;
diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
index 22001d4..498e1f2 100644
--- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
+++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
@@ -96,10 +96,12 @@
         for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
             final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
             final BatteryStats.Uid uid = app.getBatteryStatsUid();
-            calculateApp(app, uid, powerPerPacketMah, total);
+            calculateApp(app, uid, powerPerPacketMah, total,
+                    query.shouldForceUsePowerProfileModel());
         }
 
-        calculateRemaining(total, batteryStats, rawRealtimeUs);
+        calculateRemaining(total, batteryStats, rawRealtimeUs,
+                query.shouldForceUsePowerProfileModel());
 
         if (total.powerMah != 0) {
             builder.getOrCreateSystemBatteryConsumerBuilder(
@@ -111,11 +113,13 @@
     }
 
     private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
-            double powerPerPacketMah, PowerAndDuration total) {
+            double powerPerPacketMah, PowerAndDuration total,
+            boolean shouldForceUsePowerProfileModel) {
         final long radioActiveDurationMs = calculateDuration(u, BatteryStats.STATS_SINCE_CHARGED);
         total.totalAppDurationMs += radioActiveDurationMs;
 
-        final double powerMah = calculatePower(u, powerPerPacketMah, radioActiveDurationMs);
+        final double powerMah = calculatePower(u, powerPerPacketMah, radioActiveDurationMs,
+                shouldForceUsePowerProfileModel);
 
         app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_MOBILE_RADIO,
                 radioActiveDurationMs)
@@ -132,12 +136,12 @@
             final BatterySipper app = sippers.get(i);
             if (app.drainType == BatterySipper.DrainType.APP) {
                 final BatteryStats.Uid u = app.uidObj;
-                calculateApp(app, u, statsType, mobilePowerPerPacket, total);
+                calculateApp(app, u, statsType, mobilePowerPerPacket, total, false);
             }
         }
 
         BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);
-        calculateRemaining(total, batteryStats, rawRealtimeUs);
+        calculateRemaining(total, batteryStats, rawRealtimeUs, false);
         if (total.powerMah != 0) {
             if (total.signalDurationMs != 0) {
                 radio.noCoveragePercent =
@@ -154,9 +158,12 @@
     }
 
     private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType,
-            double powerPerPacketMah, PowerAndDuration total) {
+            double powerPerPacketMah, PowerAndDuration total,
+            boolean shouldForceUsePowerProfileModel) {
         app.mobileActive = calculateDuration(u, statsType);
-        app.mobileRadioPowerMah = calculatePower(u, powerPerPacketMah, app.mobileActive);
+
+        app.mobileRadioPowerMah = calculatePower(u, powerPerPacketMah, app.mobileActive,
+                shouldForceUsePowerProfileModel);
         total.totalAppDurationMs += app.mobileActive;
 
         // Add cost of mobile traffic.
@@ -183,11 +190,19 @@
     }
 
     private double calculatePower(BatteryStats.Uid u, double powerPerPacketMah,
-            long radioActiveDurationMs) {
+            long radioActiveDurationMs, boolean shouldForceUsePowerProfileModel) {
+
+        final long measuredChargeUC = u.getMobileRadioMeasuredBatteryConsumptionUC();
+        final boolean isMeasuredPowerAvailable = !shouldForceUsePowerProfileModel
+                && measuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE;
+        if (isMeasuredPowerAvailable) {
+            return uCtoMah(measuredChargeUC);
+        }
+
         if (radioActiveDurationMs > 0) {
             // We are tracking when the radio is up, so can use the active time to
             // determine power use.
-            return mActivePowerEstimator.calculatePower(radioActiveDurationMs);
+            return calcPowerFromRadioActiveDurationMah(radioActiveDurationMs);
         } else {
             // We are not tracking when the radio is up, so must approximate power use
             // based on the number of packets.
@@ -202,18 +217,29 @@
     }
 
     private void calculateRemaining(MobileRadioPowerCalculator.PowerAndDuration total,
-            BatteryStats batteryStats, long rawRealtimeUs) {
+            BatteryStats batteryStats, long rawRealtimeUs,
+            boolean shouldForceUsePowerProfileModel) {
         long signalTimeMs = 0;
         double powerMah = 0;
+
+        final long measuredChargeUC = batteryStats.getMobileRadioMeasuredBatteryConsumptionUC();
+        final boolean isMeasuredPowerAvailable = !shouldForceUsePowerProfileModel
+                && measuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE;
+        if (isMeasuredPowerAvailable) {
+            powerMah = uCtoMah(measuredChargeUC);
+        }
+
         for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) {
             long strengthTimeMs = batteryStats.getPhoneSignalStrengthTime(i, rawRealtimeUs,
                     BatteryStats.STATS_SINCE_CHARGED) / 1000;
-            final double p = mIdlePowerEstimators[i].calculatePower(strengthTimeMs);
-            if (DEBUG && p != 0) {
-                Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power="
-                        + formatCharge(p));
+            if (!isMeasuredPowerAvailable) {
+                final double p = calcIdlePowerAtSignalStrengthMah(strengthTimeMs, i);
+                if (DEBUG && p != 0) {
+                    Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power="
+                            + formatCharge(p));
+                }
+                powerMah += p;
             }
-            powerMah += p;
             signalTimeMs += strengthTimeMs;
             if (i == 0) {
                 total.noCoverageDurationMs = strengthTimeMs;
@@ -222,16 +248,21 @@
 
         final long scanningTimeMs = batteryStats.getPhoneSignalScanningTime(rawRealtimeUs,
                 BatteryStats.STATS_SINCE_CHARGED) / 1000;
-        final double p = mScanPowerEstimator.calculatePower(scanningTimeMs);
-        if (DEBUG && p != 0) {
-            Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs + " power=" + formatCharge(p));
-        }
-        powerMah += p;
         long radioActiveTimeMs = batteryStats.getMobileRadioActiveTime(rawRealtimeUs,
                 BatteryStats.STATS_SINCE_CHARGED) / 1000;
         long remainingActiveTimeMs = radioActiveTimeMs - total.totalAppDurationMs;
-        if (remainingActiveTimeMs > 0) {
-            powerMah += mActivePowerEstimator.calculatePower(remainingActiveTimeMs);
+
+        if (!isMeasuredPowerAvailable) {
+            final double p = calcScanTimePowerMah(scanningTimeMs);
+            if (DEBUG && p != 0) {
+                Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs + " power=" + formatCharge(
+                        p));
+            }
+            powerMah += p;
+
+            if (remainingActiveTimeMs > 0) {
+                powerMah += calcPowerFromRadioActiveDurationMah(remainingActiveTimeMs);
+            }
         }
         total.durationMs = radioActiveTimeMs;
         total.powerMah = powerMah;
@@ -239,12 +270,35 @@
     }
 
     /**
+     * Calculates active radio power consumption (in milliamp-hours) from active radio duration.
+     */
+    public double calcPowerFromRadioActiveDurationMah(long radioActiveDurationMs) {
+        return mActivePowerEstimator.calculatePower(radioActiveDurationMs);
+    }
+
+    /**
+     * Calculates idle radio power consumption (in milliamp-hours) for time spent at a cell signal
+     * strength level.
+     * see {@link CellSignalStrength#getNumSignalStrengthLevels()}
+     */
+    public double calcIdlePowerAtSignalStrengthMah(long strengthTimeMs, int strengthLevel) {
+        return mIdlePowerEstimators[strengthLevel].calculatePower(strengthTimeMs);
+    }
+
+    /**
+     * Calculates radio scan power consumption (in milliamp-hours) from scan time.
+     */
+    public double calcScanTimePowerMah(long scanningTimeMs) {
+        return mScanPowerEstimator.calculatePower(scanningTimeMs);
+    }
+
+    /**
      * Return estimated power (in mAh) of sending or receiving a packet with the mobile radio.
      */
     private double getMobilePowerPerPacket(BatteryStats stats, long rawRealtimeUs, int statsType) {
         final long radioDataUptimeMs =
                 stats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
-        final double mobilePower = mActivePowerEstimator.calculatePower(radioDataUptimeMs);
+        final double mobilePower = calcPowerFromRadioActiveDurationMah(radioDataUptimeMs);
 
         final long mobileRx = stats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
                 statsType);
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 39bde74..611fe29 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -32,6 +32,7 @@
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -2508,6 +2509,7 @@
             if (mDecor.mForceWindowDrawsBarBackgrounds) {
                 params.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
             }
+            params.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
         }
         if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {
             decor.setSystemUiVisibility(
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index 845b3e5..00a0627 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -54,7 +54,9 @@
     public static final int POWER_BUCKET_CPU = 3;
     public static final int POWER_BUCKET_WIFI = 4;
     public static final int POWER_BUCKET_BLUETOOTH = 5;
-    public static final int NUMBER_STANDARD_POWER_BUCKETS = 6; // Buckets above this are custom.
+    public static final int POWER_BUCKET_GNSS = 6;
+    public static final int POWER_BUCKET_MOBILE_RADIO = 7;
+    public static final int NUMBER_STANDARD_POWER_BUCKETS = 8; // Buckets above this are custom.
 
     @IntDef(prefix = {"POWER_BUCKET_"}, value = {
             POWER_BUCKET_UNKNOWN,
@@ -64,6 +66,8 @@
             POWER_BUCKET_CPU,
             POWER_BUCKET_WIFI,
             POWER_BUCKET_BLUETOOTH,
+            POWER_BUCKET_GNSS,
+            POWER_BUCKET_MOBILE_RADIO,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface StandardPowerBucket {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index f1fa5db..20d257e 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -221,7 +221,6 @@
 
             static_libs: [
                 "libasync_safe",
-                "libconnectivityframeworkutils",
                 "libbinderthreadstateutils",
                 "libdmabufinfo",
                 "libgif",
@@ -238,7 +237,6 @@
                 "android.hardware.camera.device@3.2",
                 "media_permission-aidl-cpp",
                 "libandroidicu",
-                "libandroid_net",
                 "libbpf_android",
                 "libnetdbpf",
                 "libnetdutils",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 94ac183..0c3f265 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -156,7 +156,6 @@
 extern int register_android_service_DataLoaderService(JNIEnv* env);
 extern int register_android_os_incremental_IncrementalManager(JNIEnv* env);
 extern int register_android_net_LocalSocketImpl(JNIEnv* env);
-extern int register_android_net_NetworkUtils(JNIEnv* env);
 extern int register_android_text_AndroidCharacter(JNIEnv *env);
 extern int register_android_text_Hyphenator(JNIEnv *env);
 extern int register_android_opengl_classes(JNIEnv *env);
@@ -1548,7 +1547,6 @@
         REG_JNI(register_android_os_Trace),
         REG_JNI(register_android_os_UEventObserver),
         REG_JNI(register_android_net_LocalSocketImpl),
-        REG_JNI(register_android_net_NetworkUtils),
         REG_JNI(register_android_os_MemoryFile),
         REG_JNI(register_android_os_SharedMemory),
         REG_JNI(register_android_os_incremental_IncrementalManager),
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index f54ffc5..419dc6e 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1658,10 +1658,12 @@
             jobject jJankData = env->NewObject(gJankDataClassInfo.clazz,
                     gJankDataClassInfo.ctor, jankData[i].frameVsyncId, jankData[i].jankType);
             env->SetObjectArrayElement(jJankDataArray, i, jJankData);
+            env->DeleteLocalRef(jJankData);
         }
         env->CallVoidMethod(target,
                 gJankDataListenerClassInfo.onJankDataAvailable,
                 jJankDataArray);
+        env->DeleteLocalRef(jJankDataArray);
         env->DeleteLocalRef(target);
     }
 
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index dca6002..530cb44 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -123,6 +123,8 @@
         optional SettingProto gesture_silence_alerts_enabled = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto gesture_wake_enabled = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto gesture_setup_complete = 9 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto touch_gesture_enabled = 10 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto long_press_home_enabled = 11 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional Assist assist = 7;
 
@@ -296,6 +298,7 @@
         optional SettingProto subtype_history = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto selected_input_method_subtype = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto show_ime_with_hard_keyboard = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto default_voice_input_method = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional InputMethods input_methods = 26;
 
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index 0d23946..acb7429 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -58,6 +58,9 @@
         optional bool is_screen_bright = 1;
         optional bool is_screen_dim = 2;
         optional bool is_screen_dream = 3;
+        optional int64 last_user_activity_time_ms = 4;
+        optional int64 last_user_activity_time_no_change_lights_ms = 5;
+        optional int32 display_group_id = 6;
     }
     // A com.android.server.power.PowerManagerService.UidState object.
     message UidStateProto {
@@ -109,7 +112,7 @@
     // The time we decided to do next long check. (In milliseconds timestamp)
     optional int64 notify_long_next_check_ms = 19;
     // Summarizes the effect of the user activity timer.
-    optional UserActivityProto user_activity = 20;
+    repeated UserActivityProto user_activity = 20;
     // If true, instructs the display controller to wait for the proximity
     // sensor to go negative before turning the screen on.
     optional bool is_request_wait_for_negative_proximity = 21;
@@ -134,8 +137,8 @@
     // Timestamp of the last time the device was put to sleep.
     optional int64 last_sleep_time_ms = 30;
     // Timestamp of the last call to user activity.
-    optional int64 last_user_activity_time_ms = 31;
-    optional int64 last_user_activity_time_no_change_lights_ms = 32;
+    optional int64 last_user_activity_time_ms = 31 [deprecated = true];
+    optional int64 last_user_activity_time_no_change_lights_ms = 32 [deprecated = true];
     // Timestamp of last interactive power hint.
     optional int64 last_interactive_power_hint_time_ms = 33;
     // Timestamp of the last screen brightness boost.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1d462bc..90755b9 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2689,6 +2689,11 @@
     <permission android:name="android.permission.CREATE_USERS"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi @hide Allows an application to access data blobs across users.
+         This permission is not available to third party applications. -->
+    <permission android:name="android.permission.ACCESS_BLOBS_ACROSS_USERS"
+        android:protectionLevel="signature|privileged|development" />
+
     <!-- @hide Allows an application to set the profile owners and the device owner.
          This permission is not available to third party applications.-->
     <permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
@@ -3998,6 +4003,15 @@
     <permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
         android:protectionLevel="signature" />
 
+    <!-- Allows an app to schedule a prioritized alarm that can be used to perform
+         background work even when the device is in doze.
+         <p>Not for use by third-party applications.
+         @hide
+         @SystemApi
+     -->
+    <permission android:name="android.permission.SCHEDULE_PRIORITIZED_ALARM"
+                android:protectionLevel="signature|privileged"/>
+
     <!-- Allows an app to use exact alarm scheduling APIs to perform timing
          sensitive background work.
      -->
@@ -4227,6 +4241,11 @@
     <permission android:name="android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE"
                 android:protectionLevel="normal" />
 
+    <!-- Allows an application to create new companion device associations.
+         @hide -->
+    <permission android:name="android.permission.ASSOCIATE_COMPANION_DEVICES"
+        android:protectionLevel="internal|role" />
+
     <!-- @SystemApi Allows an application to use SurfaceFlinger's low level features.
          <p>Not for use by third-party applications.
          @hide
@@ -5424,12 +5443,6 @@
     <permission android:name="android.permission.WATCH_APPOPS"
         android:protectionLevel="signature|privileged" />
 
-    <!-- Allows an application to directly open the "Open by default" page inside a package's
-         Details screen.
-         @hide <p>Not for use by third-party applications. -->
-    <permission android:name="android.permission.OPEN_APP_OPEN_BY_DEFAULT_SETTINGS"
-                android:protectionLevel="signature" />
-
     <!-- Allows hidden API checks to be disabled when starting a process.
          @hide <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.DISABLE_HIDDEN_API_CHECKS"
@@ -5556,9 +5569,17 @@
     <permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"
                 android:protectionLevel="signature|privileged" />
     <!-- Allows an app to override compat change config.
+         This permission only allows to override config on debuggable builds or test-apks and is
+         therefore a less powerful version of OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD.
          @hide  <p>Not for use by third-party applications.</p> -->
     <permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"
                 android:protectionLevel="signature|privileged" />
+    <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"/>
+    <!-- @SystemApi Allows an app to override compat change config on release builds.
+        Only ChangeIds that are annotated as @Overridable can be overridden on release builds.
+        @hide -->
+    <permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"
+                android:protectionLevel="signature|privileged" />
 
     <!-- Allows input events to be monitored. Very dangerous!  @hide -->
     <permission android:name="android.permission.MONITOR_INPUT"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index dc4f52e..986bb82 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1595,6 +1595,20 @@
        <enum name="always" value="1" />
     </attr>
 
+    <!-- Enable hardware memory tagging (ARM MTE) in this process.
+         When enabled, heap memory bugs like use-after-free and buffer overlow
+         are detected and result in an immediate ("sync" mode) or delayed ("async"
+         mode) crash instead of a silent memory corruption. Sync mode, while slower,
+         provides enhanced bug reports including stack traces at the time of allocation
+         and deallocation of memory, similar to AddressSanitizer.
+
+         See the <a href="https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/enhancing-memory-safety">ARM announcement</a>
+         for more details.
+
+         <p>This attribute can be applied to a
+         {@link android.R.styleable#AndroidManifestProcess process} tag, or to an
+         {@link android.R.styleable#AndroidManifestApplication application} tag (to supply
+         a default setting for all application components). -->
     <attr name="memtagMode">
        <enum name="default" value="-1" />
        <enum name="off" value="0" />
@@ -1898,7 +1912,9 @@
 
         <attr name="memtagMode" />
 
-        <attr name="nativeHeapZeroInit" format="boolean" />
+        <!-- If {@code true} enables automatic zero initialization of all native heap
+             allocations. -->
+        <attr name="nativeHeapZeroInitialized" format="boolean" />
 
         <!-- @hide no longer used, kept to preserve padding -->
         <attr name="allowAutoRevokePermissionsExemption" format="boolean" />
@@ -2486,7 +2502,7 @@
         <attr name="process" />
         <attr name="gwpAsanMode" />
         <attr name="memtagMode" />
-        <attr name="nativeHeapZeroInit" />
+        <attr name="nativeHeapZeroInitialized" />
     </declare-styleable>
 
     <!-- The <code>deny-permission</code> tag specifies that a permission is to be denied
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8df3221..b6c22bb 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3732,6 +3732,13 @@
      -->
     <string name="config_defaultWellbeingPackage" translatable="false"></string>
 
+    <!-- The package name for the companion provider app.
+         This package must be trusted, as it has the permissions to associate apps with devices
+         without a UI prompt.
+         Example: "com.google.android.gms"
+     -->
+    <string name="config_companionProviderPackage" translatable="false"></string>
+
     <!-- The component name for the default system attention service.
          This service must be trusted, as it can be activated without explicit consent of the user.
          See android.attention.AttentionManagerService.
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 16feb4f..bc49818 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3059,7 +3059,7 @@
     <public name="fontProviderSystemFontFamily" />
     <public name="hand_second" />
     <public name="memtagMode" />
-    <public name="nativeHeapZeroInit" />
+    <public name="nativeHeapZeroInitialized" />
     <!-- @hide @SystemApi -->
     <public name="hotwordDetectionService" />
     <public name="previewLayout" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e2974bc..dd64750 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1656,6 +1656,14 @@
     <string name="face_recalibrate_notification_title">Re-enroll your face</string>
     <!-- Notification content shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
     <string name="face_recalibrate_notification_content">To improve recognition, please re-enroll your face</string>
+    <!-- Title of a notification that directs the user to set up face unlock by enrolling their face. [CHAR LIMIT=NONE] -->
+    <string name="face_setup_notification_title">Set up face unlock</string>
+    <!-- Contents of a notification that directs the user to set up face unlock by enrolling their face. [CHAR LIMIT=NONE] -->
+    <string name="face_setup_notification_content">Unlock your phone by looking at it</string>
+    <!-- Title of a notification that directs the user to enroll a fingerprint. [CHAR LIMIT=NONE] -->
+    <string name="fingerprint_setup_notification_title">Set up more ways to unlock</string>
+    <!-- Contents of a notification that directs the user to enroll a fingerprint. [CHAR LIMIT=NONE] -->
+    <string name="fingerprint_setup_notification_content">Tap to add a fingerprint</string>
 
     <!-- Message shown during face acquisition when the face cannot be recognized [CHAR LIMIT=50] -->
     <string name="face_acquired_insufficient">Couldn\u2019t capture accurate face data. Try again.</string>
@@ -5654,15 +5662,20 @@
     <!-- Button text. This button turns on a user's work profile so they can access their work apps and data. [CHAR LIMIT=NONE] -->
     <string name="resolver_switch_on_work">Tap to turn on</string>
 
-    <!-- Error message. This text lets the user know that their current work apps don't support the specific content that they're trying to share. [CHAR LIMIT=NONE] -->
-    <string name="resolver_no_work_apps_available_share">No work apps can support this content</string>
-    <!-- Error message. This text lets the user know that their current work apps can't open this specific content. [CHAR LIMIT=NONE] -->
-    <string name="resolver_no_work_apps_available_resolve">No work apps can open this content</string>
+    <!-- Error message. This text lets the user know that their current work apps don't support the specific content. [CHAR LIMIT=NONE] -->
+    <string name="resolver_no_work_apps_available">No work apps</string>
 
-    <!-- Error message. This text lets the user know that their current personal apps don't support the specific content that they're trying to share. [CHAR LIMIT=NONE] -->
-    <string name="resolver_no_personal_apps_available_share">No personal apps can support this content</string>
-    <!-- Error message. This text lets the user know that their current personal apps can't open this specific content. [CHAR LIMIT=NONE] -->
-    <string name="resolver_no_personal_apps_available_resolve">No personal apps can open this content</string>
+    <!-- Error message. This text lets the user know that their current personal apps don't support the specific content. [CHAR LIMIT=NONE] -->
+    <string name="resolver_no_personal_apps_available">No personal apps</string>
+
+    <!-- Dialog title. User must choose between opening content in a cross-profile app or same-profile browser. [CHAR LIMIT=NONE] -->
+    <string name="miniresolver_open_in_personal">Open in <xliff:g id="app" example="YouTube">%s</xliff:g> in personal profile?</string>
+    <!-- Dialog title. User must choose between opening content in a cross-profile app or same-profile browser. [CHAR LIMIT=NONE] -->
+    <string name="miniresolver_open_in_work">Open in <xliff:g id="app" example="YouTube">%s</xliff:g> in work profile?</string>
+    <!-- Button option. Open the link in the personal browser. [CHAR LIMIT=NONE] -->
+    <string name="miniresolver_use_personal_browser">Use personal browser</string>
+    <!-- Button option. Open the link in the work browser. [CHAR LIMIT=NONE] -->
+    <string name="miniresolver_use_work_browser">Use work browser</string>
 
     <!-- Icc depersonalization related strings -->
     <!-- Label text for PIN entry widget on SIM Network Depersonalization panel [CHAR LIMIT=none] -->
@@ -5909,9 +5922,9 @@
     <!-- Window magnification prompt related string. -->
 
     <!-- Notification title to prompt the user that new magnification feature is available. [CHAR LIMIT=50] -->
-    <string name="window_magnification_prompt_title">Magnify part of your screen</string>
+    <string name="window_magnification_prompt_title">New magnification settings</string>
     <!-- Notification content to prompt the user that new magnification feature is available. [CHAR LIMIT=NONE] -->
-    <string name="window_magnification_prompt_content">You can now magnify your full screen, a specific area, or switch between both options.</string>
+    <string name="window_magnification_prompt_content">You can now magnify part of your screen.</string>
     <!-- Notification action to bring the user to magnification settings page. [CHAR LIMIT=50] -->
     <string name="turn_on_magnification_settings_action">Turn on in Settings</string>
     <!-- Notification action to dismiss. [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 01374b1..b924ecd 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3536,6 +3536,7 @@
   <java-symbol type="string" name="notification_channel_do_not_disturb" />
   <java-symbol type="string" name="notification_channel_accessibility_magnification" />
   <java-symbol type="string" name="notification_channel_accessibility_security_policy" />
+  <java-symbol type="string" name="config_companionProviderPackage" />
   <java-symbol type="string" name="config_defaultAutofillService" />
   <java-symbol type="string" name="config_defaultOnDeviceSpeechRecognitionService" />
   <java-symbol type="string" name="config_defaultTextClassifierPackage" />
@@ -4101,10 +4102,8 @@
   <java-symbol type="string" name="resolver_cant_access_work_apps_explanation" />
   <java-symbol type="string" name="resolver_cant_access_personal_apps_explanation" />
   <java-symbol type="string" name="resolver_turn_on_work_apps" />
-  <java-symbol type="string" name="resolver_no_work_apps_available_share" />
-  <java-symbol type="string" name="resolver_no_work_apps_available_resolve" />
-  <java-symbol type="string" name="resolver_no_personal_apps_available_share" />
-  <java-symbol type="string" name="resolver_no_personal_apps_available_resolve" />
+  <java-symbol type="string" name="resolver_no_work_apps_available" />
+  <java-symbol type="string" name="resolver_no_personal_apps_available" />
   <java-symbol type="string" name="resolver_switch_on_work" />
   <java-symbol type="drawable" name="ic_work_apps_off" />
   <java-symbol type="drawable" name="ic_sharing_disabled" />
diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java b/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java
index 6d9e2ea5..56c685a 100644
--- a/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java
@@ -42,8 +42,8 @@
     public void setUp() throws Exception {
         // Remove all documents from any instances that may have been created in the tests.
         Objects.requireNonNull(mAppSearch);
-        AppSearchManager.SearchContext searchContext = new AppSearchManager.SearchContext.Builder()
-                .setDatabaseName("testDb").build();
+        AppSearchManager.SearchContext searchContext =
+                new AppSearchManager.SearchContext.Builder("testDb").build();
         CompletableFuture<AppSearchResult<AppSearchSession>> future = new CompletableFuture<>();
         mAppSearch.createSearchSession(searchContext, mExecutor, future::complete);
         mSearchSession = future.get().getResultValue();
diff --git a/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java
index ffe93bc..21eb44a 100644
--- a/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java
+++ b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java
@@ -45,7 +45,7 @@
         final Set<String> categorySet = new ArraySet<>();
         categorySet.add(category);
         final Intent shortcutIntent = new Intent(Intent.ACTION_VIEW);
-        final ShortcutInfo shortcut = new AppSearchShortcutInfo.Builder(id)
+        final ShortcutInfo shortcut = new AppSearchShortcutInfo.Builder(/*packageName=*/"", id)
                 .setActivity(activity)
                 .setLongLabel(id)
                 .setIconResName(shortcutIconResName)
diff --git a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
index f3a6f9e..22c71b52 100644
--- a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
+++ b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
@@ -32,7 +32,9 @@
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.ICancellationSignal;
+import android.platform.test.annotations.Presubmit;
 
+import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
@@ -45,6 +47,8 @@
  * Tests of {@link ScrollCaptureConnection}.
  */
 @SuppressWarnings("UnnecessaryLocalVariable")
+@Presubmit
+@SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ScrollCaptureConnectionTest {
 
diff --git a/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java b/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java
index cc229e1..dc43204 100644
--- a/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java
+++ b/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java
@@ -32,8 +32,10 @@
 import android.graphics.Rect;
 import android.os.CancellationSignal;
 import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
 
 import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
@@ -47,8 +49,10 @@
 import java.util.function.Consumer;
 
 /**
- * Tests of {@link ScrollCaptureTargetSelector}.
+ * Tests of {@link ScrollCaptureSearchResults}.
  */
+@Presubmit
+@SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ScrollCaptureSearchResultsTest {
 
diff --git a/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java b/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java
index 41cd4c5..2833ea3 100644
--- a/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java
@@ -49,7 +49,6 @@
  */
 @Presubmit
 @SmallTest
-@FlakyTest(detail = "promote once confirmed flake-free")
 @RunWith(MockitoJUnitRunner.class)
 public class ViewGroupScrollCaptureTest {
 
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
index 67614bb..e6a25d0 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
@@ -236,12 +236,13 @@
     @Test
     public void testMergeEvent_typeViewTextChanged() {
         final ContentCaptureEvent event = new ContentCaptureEvent(42, TYPE_VIEW_TEXT_CHANGED)
-                .setText("test");
+                .setText("test", false);
         final ContentCaptureEvent event2 = new ContentCaptureEvent(43, TYPE_VIEW_TEXT_CHANGED)
-                .setText("empty");
+                .setText("empty", true);
 
         event.mergeEvent(event2);
         assertThat(event.getText()).isEqualTo(event2.getText());
+        assertThat(event.getTextHasComposingSpan()).isEqualTo(event2.getTextHasComposingSpan());
     }
 
     @Test
@@ -282,16 +283,18 @@
     @Test
     public void testMergeEvent_differentEventTypes() {
         final ContentCaptureEvent event = new ContentCaptureEvent(42, TYPE_VIEW_DISAPPEARED)
-                .setText("test").setAutofillId(new AutofillId(1));
+                .setText("test", false).setAutofillId(new AutofillId(1));
         final ContentCaptureEvent event2 = new ContentCaptureEvent(17, TYPE_VIEW_TEXT_CHANGED)
-                .setText("empty").setAutofillId(new AutofillId(2));
+                .setText("empty", true).setAutofillId(new AutofillId(2));
 
         event.mergeEvent(event2);
         assertThat(event.getText()).isEqualTo("test");
+        assertThat(event.getTextHasComposingSpan()).isFalse();
         assertThat(event.getId()).isEqualTo(new AutofillId(1));
 
         event2.mergeEvent(event);
         assertThat(event2.getText()).isEqualTo("empty");
+        assertThat(event2.getTextHasComposingSpan()).isTrue();
         assertThat(event2.getId()).isEqualTo(new AutofillId(2));
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 80d47a9..1633d28 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -1660,7 +1660,7 @@
         onView(withText(R.string.resolver_work_tab)).perform(click());
         waitForIdle();
 
-        onView(withText(R.string.resolver_no_work_apps_available_share))
+        onView(withText(R.string.resolver_no_work_apps_available))
                 .check(matches(isDisplayed()));
     }
 
@@ -1711,7 +1711,7 @@
         onView(withText(R.string.resolver_work_tab)).perform(click());
         waitForIdle();
 
-        onView(withText(R.string.resolver_no_work_apps_available_share))
+        onView(withText(R.string.resolver_no_work_apps_available))
                 .check(matches(isDisplayed()));
     }
 
@@ -2146,7 +2146,7 @@
         onView(withText(R.string.resolver_work_tab)).perform(click());
         waitForIdle();
 
-        onView(withText(R.string.resolver_no_work_apps_available_resolve))
+        onView(withText(R.string.resolver_no_work_apps_available))
                 .check(matches(isDisplayed()));
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 68287ca..97652a9 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -700,7 +700,7 @@
         onView(withText(R.string.resolver_work_tab)).perform(click());
         waitForIdle();
 
-        onView(withText(R.string.resolver_no_work_apps_available_resolve))
+        onView(withText(R.string.resolver_no_work_apps_available))
                 .check(matches(isDisplayed()));
     }
 
@@ -751,7 +751,7 @@
         onView(withText(R.string.resolver_work_tab)).perform(click());
         waitForIdle();
 
-        onView(withText(R.string.resolver_no_work_apps_available_resolve))
+        onView(withText(R.string.resolver_no_work_apps_available))
                 .check(matches(isDisplayed()));
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
index 0f59143..d36f06a 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
@@ -158,4 +158,22 @@
 
         assertThat(item.time).isEqualTo(elapsedTimeMs);
     }
+
+    @Test
+    public void shouldUpdateStats() {
+        Context context = InstrumentationRegistry.getContext();
+        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context,
+                mStatsRule.getBatteryStats());
+
+        final List<BatteryUsageStatsQuery> queries = List.of(
+                new BatteryUsageStatsQuery.Builder().setMaxStatsAgeMs(1000).build(),
+                new BatteryUsageStatsQuery.Builder().setMaxStatsAgeMs(2000).build()
+        );
+
+        mStatsRule.setTime(10500, 0);
+        assertThat(provider.shouldUpdateStats(queries, 10000)).isFalse();
+
+        mStatsRule.setTime(11500, 0);
+        assertThat(provider.shouldUpdateStats(queries, 10000)).isTrue();
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java
index bd7f1e2..eed61cb 100644
--- a/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.os.BatteryConsumer;
+import android.os.BatteryUsageStatsQuery;
 import android.os.Process;
 import android.os.UidBatteryConsumer;
 
@@ -35,12 +36,14 @@
     private static final double PRECISION = 0.00001;
 
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
+    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 222;
 
     @Rule
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_GPS_ON, 360.0)
             .setAveragePower(PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED,
-                    new double[] {720.0, 1440.0, 1800.0});
+                    new double[] {720.0, 1440.0, 1800.0})
+            .initMeasuredEnergyStatsLocked(0);
 
     @Test
     public void testTimerBasedModel() {
@@ -51,7 +54,8 @@
         GnssPowerCalculator calculator =
                 new GnssPowerCalculator(mStatsRule.getPowerProfile());
 
-        mStatsRule.apply(calculator);
+        mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(),
+                calculator);
 
         UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID);
         assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_GNSS))
@@ -59,4 +63,35 @@
         assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
                 .isWithin(PRECISION).of(0.1);
     }
+
+    @Test
+    public void testMeasuredEnergyBasedModel() {
+        BatteryStatsImpl.Uid uidStats = mStatsRule.getUidStats(APP_UID);
+        uidStats.noteStartGps(1000);
+        uidStats.noteStopGps(2000);
+
+        BatteryStatsImpl.Uid uidStats2 = mStatsRule.getUidStats(APP_UID2);
+        uidStats2.noteStartGps(3000);
+        uidStats2.noteStopGps(5000);
+
+        BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+        stats.updateGnssMeasuredEnergyStatsLocked(30_000_000, 6000);
+
+        GnssPowerCalculator calculator =
+                new GnssPowerCalculator(mStatsRule.getPowerProfile());
+
+        mStatsRule.apply(calculator);
+
+        UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+        assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_GNSS))
+                .isEqualTo(1000);
+        assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
+                .isWithin(PRECISION).of(2.77777);
+
+        UidBatteryConsumer consumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
+        assertThat(consumer2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_GNSS))
+                .isEqualTo(2000);
+        assertThat(consumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
+                .isWithin(PRECISION).of(5.55555);
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
index 66a8379..ae59a54 100644
--- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.os;
 
+import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.mock;
@@ -24,6 +26,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkStats;
 import android.os.BatteryConsumer;
+import android.os.BatteryUsageStatsQuery;
 import android.os.Process;
 import android.os.SystemBatteryConsumer;
 import android.os.UidBatteryConsumer;
@@ -54,7 +57,8 @@
             .setAveragePower(PowerProfile.POWER_RADIO_SCANNING, 720.0)
             .setAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX, 1440.0)
             .setAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX,
-                    new double[] {720.0, 1080.0, 1440.0, 1800.0, 2160.0});
+                    new double[] {720.0, 1080.0, 1440.0, 1800.0, 2160.0})
+            .initMeasuredEnergyStatsLocked(0);
 
     @Test
     public void testCounterBasedModel() {
@@ -88,7 +92,59 @@
 
         ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
                 new int[] {100, 200, 300, 400, 500}, 600);
-        stats.noteModemControllerActivity(mai, 10000, 10000);
+        stats.noteModemControllerActivity(mai, POWER_DATA_UNAVAILABLE, 10000, 10000);
+
+        mStatsRule.setTime(12_000_000, 12_000_000);
+
+        MobileRadioPowerCalculator calculator =
+                new MobileRadioPowerCalculator(mStatsRule.getPowerProfile());
+
+        mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(),
+                calculator);
+
+        SystemBatteryConsumer consumer =
+                mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO);
+        assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
+                .isWithin(PRECISION).of(1.44440);
+
+        UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+        assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
+                .isWithin(PRECISION).of(0.8);
+    }
+
+    @Test
+    public void testMeasuredEnergyBasedModel() {
+        BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+
+        // Scan for a cell
+        stats.notePhoneStateLocked(ServiceState.STATE_OUT_OF_SERVICE,
+                TelephonyManager.SIM_STATE_READY,
+                2000, 2000);
+
+        // Found a cell
+        stats.notePhoneStateLocked(ServiceState.STATE_IN_SERVICE, TelephonyManager.SIM_STATE_READY,
+                5000, 5000);
+
+        // Note cell signal strength
+        SignalStrength signalStrength = mock(SignalStrength.class);
+        when(signalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_MODERATE);
+        stats.notePhoneSignalStrengthLocked(signalStrength, 5000, 5000);
+
+        stats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
+                8_000_000_000L, APP_UID, 8000, 8000);
+
+        // Note established network
+        stats.noteNetworkInterfaceForTransports("cellular",
+                new int[] { NetworkCapabilities.TRANSPORT_CELLULAR });
+
+        // Note application network activity
+        NetworkStats networkStats = new NetworkStats(10000, 1)
+                .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100);
+        mStatsRule.setNetworkStats(networkStats);
+
+        ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
+                new int[] {100, 200, 300, 400, 500}, 600);
+        stats.noteModemControllerActivity(mai, 10_000_000, 10000, 10000);
 
         mStatsRule.setTime(12_000_000, 12_000_000);
 
@@ -99,11 +155,13 @@
 
         SystemBatteryConsumer consumer =
                 mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO);
+
+        // 100000000 uAs * (1 mA / 1000 uA) * (1 h / 3600 s) = 2.77777 mAh
         assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
-                .isWithin(PRECISION).of(1.44440);
+                .isWithin(PRECISION).of(2.77777);
 
         UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
         assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
-                .isWithin(PRECISION).of(0.8);
+                .isWithin(PRECISION).of(1.53934);
     }
 }
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index cafda09..c48fd8b 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -34,9 +34,11 @@
 import android.graphics.fonts.FontStyle;
 import android.graphics.fonts.FontVariationAxis;
 import android.graphics.fonts.SystemFonts;
+import android.icu.util.ULocale;
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.os.SharedMemory;
+import android.os.SystemProperties;
 import android.os.Trace;
 import android.provider.FontRequest;
 import android.provider.FontsContract;
@@ -45,6 +47,7 @@
 import android.text.FontConfig;
 import android.util.ArrayMap;
 import android.util.Base64;
+import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.LruCache;
 import android.util.SparseArray;
@@ -1375,13 +1378,35 @@
 
     static {
         // Preload Roboto-Regular.ttf in Zygote for improving app launch performance.
-        // TODO: add new attribute to fonts.xml to preload fonts in Zygote.
         preloadFontFile("/system/fonts/Roboto-Regular.ttf");
+
+        String locale = SystemProperties.get("persist.sys.locale", "en-US");
+        String script = ULocale.addLikelySubtags(ULocale.forLanguageTag(locale)).getScript();
+
+        FontConfig config = SystemFonts.getSystemPreinstalledFontConfig();
+        for (int i = 0; i < config.getFontFamilies().size(); ++i) {
+            FontConfig.FontFamily family = config.getFontFamilies().get(i);
+            boolean loadFamily = false;
+            for (int j = 0; j < family.getLocaleList().size(); ++j) {
+                String fontScript = ULocale.addLikelySubtags(
+                        ULocale.forLocale(family.getLocaleList().get(j))).getScript();
+                loadFamily = fontScript.equals(script);
+                if (loadFamily) {
+                    break;
+                }
+            }
+            if (loadFamily) {
+                for (int j = 0; j < family.getFontList().size(); ++j) {
+                    preloadFontFile(family.getFontList().get(j).getFile().getAbsolutePath());
+                }
+            }
+        }
     }
 
     private static void preloadFontFile(String filePath) {
         File file = new File(filePath);
         if (file.exists()) {
+            Log.i(TAG, "Preloading " + file.getAbsolutePath());
             nativeWarmUpCache(filePath);
         }
     }
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index 35b1c16..72cea0c 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -139,4 +139,18 @@
             return SYSTEM_ERROR;
         }
     }
+
+    /**
+     * Informs Keystore 2.0 that an off body event was detected.
+     */
+    public static void onDeviceOffBody() {
+        if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return;
+        try {
+            getService().onDeviceOffBody();
+        } catch (Exception e) {
+            // TODO This fails open. This is not a regression with respect to keystore1 but it
+            //      should get fixed.
+            Log.e(TAG, "Error while reporting device off body event.", e);
+        }
+    }
 }
diff --git a/keystore/java/android/security/GenerateRkpKey.java b/keystore/java/android/security/GenerateRkpKey.java
new file mode 100644
index 0000000..a1a7aa8
--- /dev/null
+++ b/keystore/java/android/security/GenerateRkpKey.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+/**
+ * GenerateKey is a helper class to handle interactions between Keystore and the RemoteProvisioner
+ * app. There are two cases where Keystore should use this class.
+ *
+ * (1) : An app generates a new attested key pair, so Keystore calls notifyKeyGenerated to let the
+ *       RemoteProvisioner app check if the state of the attestation key pool is getting low enough
+ *       to warrant provisioning more attestation certificates early.
+ *
+ * (2) : An app attempts to generate a new key pair, but the keystore service discovers it is out of
+ *       attestation key pairs and cannot provide one for the given application. Keystore can then
+ *       make a blocking call on notifyEmpty to allow the RemoteProvisioner app to get another
+ *       attestation certificate chain provisioned.
+ *
+ * In most cases, the proper usage of (1) should preclude the need for (2).
+ *
+ * @hide
+ */
+public class GenerateRkpKey {
+
+    private IGenerateRkpKeyService mBinder;
+    private Context mContext;
+
+    private ServiceConnection mConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            mBinder = IGenerateRkpKeyService.Stub.asInterface(service);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName className) {
+            mBinder = null;
+        }
+    };
+
+    /**
+     * Constructor which takes a Context object.
+     */
+    public GenerateRkpKey(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Fulfills the use case of (2) described in the class documentation. Blocks until the
+     * RemoteProvisioner application can get new attestation keys signed by the server.
+     */
+    public void notifyEmpty(int securityLevel) throws RemoteException {
+        Intent intent = new Intent(IGenerateRkpKeyService.class.getName());
+        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        intent.setComponent(comp);
+        if (comp == null || !mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
+            throw new RemoteException("Failed to bind to GenerateKeyService");
+        }
+        if (mBinder != null) {
+            mBinder.generateKey(securityLevel);
+        }
+        mContext.unbindService(mConnection);
+    }
+
+    /**
+     * FUlfills the use case of (1) described in the class documentation. Non blocking call.
+     */
+    public void notifyKeyGenerated(int securityLevel) throws RemoteException {
+        Intent intent = new Intent(IGenerateRkpKeyService.class.getName());
+        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        intent.setComponent(comp);
+        if (comp == null || !mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
+            throw new RemoteException("Failed to bind to GenerateKeyService");
+        }
+        if (mBinder != null) {
+            mBinder.notifyKeyGenerated(securityLevel);
+        }
+        mContext.unbindService(mConnection);
+    }
+}
diff --git a/keystore/java/android/security/GenerateRkpKeyException.java b/keystore/java/android/security/GenerateRkpKeyException.java
new file mode 100644
index 0000000..a2d65e4
--- /dev/null
+++ b/keystore/java/android/security/GenerateRkpKeyException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+/**
+ * Thrown on problems in attempting to attest to a key using a remotely provisioned key.
+ *
+ * @hide
+ */
+public class GenerateRkpKeyException extends Exception {
+
+    /**
+     * Constructs a new {@code GenerateRkpKeyException}.
+     */
+    public GenerateRkpKeyException() {
+    }
+}
diff --git a/keystore/java/android/security/IGenerateRkpKeyService.aidl b/keystore/java/android/security/IGenerateRkpKeyService.aidl
new file mode 100644
index 0000000..5f1d669
--- /dev/null
+++ b/keystore/java/android/security/IGenerateRkpKeyService.aidl
@@ -0,0 +1,36 @@
+/**
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+/**
+ * Interface to allow the framework to notify the RemoteProvisioner app when keys are empty. This
+ * will be used if Keystore replies with an error code NO_KEYS_AVAILABLE in response to an
+ * attestation request. The framework can then synchronously call generateKey() to get more
+ * attestation keys generated and signed. Upon return, the caller can be certain an attestation key
+ * is available.
+ *
+ * @hide
+ */
+interface IGenerateRkpKeyService {
+    /**
+     * Ping the provisioner service to let it know an app generated a key. This may or may not have
+     * consumed a remotely provisioned attestation key, so the RemoteProvisioner app should check.
+     */
+    oneway void notifyKeyGenerated(in int securityLevel);
+    /** Ping the provisioner service to indicate there are no remaining attestation keys left. */
+    void generateKey(in int securityLevel);
+}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index a08f390..b05149e 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -1204,6 +1204,7 @@
      * Notify keystore that the device went off-body.
      */
     public void onDeviceOffBody() {
+        AndroidKeyStoreMaintenance.onDeviceOffBody();
         try {
             mBinder.onDeviceOffBody();
         } catch (RemoteException e) {
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index 6ac3821..75e248e 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -18,8 +18,7 @@
 
 import android.annotation.NonNull;
 import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
-import android.os.Build;
+import android.compat.annotation.Disabled;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
@@ -86,7 +85,7 @@
      * successfully conclude an operation.
      */
     @ChangeId
-    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+    @Disabled // See b/180133780
     static final long KEYSTORE_OPERATION_CREATION_MAY_FAIL = 169897160L;
 
     // Never use mBinder directly, use KeyStore2.getService() instead or better yet
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index e401add..2d8901a 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -24,6 +24,9 @@
 import android.hardware.security.keymint.SecurityLevel;
 import android.hardware.security.keymint.Tag;
 import android.os.Build;
+import android.os.RemoteException;
+import android.security.GenerateRkpKey;
+import android.security.GenerateRkpKeyException;
 import android.security.KeyPairGeneratorSpec;
 import android.security.KeyStore;
 import android.security.KeyStore2;
@@ -520,6 +523,18 @@
 
     @Override
     public KeyPair generateKeyPair() {
+        try {
+            return generateKeyPairHelper();
+        } catch (GenerateRkpKeyException e) {
+            try {
+                return generateKeyPairHelper();
+            } catch (GenerateRkpKeyException f) {
+                throw new ProviderException("Failed to provision new attestation keys.");
+            }
+        }
+    }
+
+    private KeyPair generateKeyPairHelper() throws GenerateRkpKeyException {
         if (mKeyStore == null || mSpec == null) {
             throw new IllegalStateException("Not initialized");
         }
@@ -557,13 +572,30 @@
             AndroidKeyStorePublicKey publicKey =
                     AndroidKeyStoreProvider.makeAndroidKeyStorePublicKeyFromKeyEntryResponse(
                             descriptor, metadata, iSecurityLevel, mKeymasterAlgorithm);
-
+            GenerateRkpKey keyGen = new GenerateRkpKey(KeyStore.getApplicationContext());
+            try {
+                if (mSpec.getAttestationChallenge() != null) {
+                    keyGen.notifyKeyGenerated(securityLevel);
+                }
+            } catch (RemoteException e) {
+                // This is not really an error state, and necessarily does not apply to non RKP
+                // systems or hybrid systems where RKP is not currently turned on.
+                Log.d(TAG, "Couldn't connect to the RemoteProvisioner backend.");
+            }
             success = true;
             return new KeyPair(publicKey, publicKey.getPrivateKey());
         } catch (android.security.KeyStoreException e) {
             switch(e.getErrorCode()) {
                 case KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE:
                     throw new StrongBoxUnavailableException("Failed to generated key pair.", e);
+                case ResponseCode.OUT_OF_KEYS:
+                    GenerateRkpKey keyGen = new GenerateRkpKey(KeyStore.getApplicationContext());
+                    try {
+                        keyGen.notifyEmpty(securityLevel);
+                    } catch (RemoteException f) {
+                        throw new ProviderException("Failed to talk to RemoteProvisioner", f);
+                    }
+                    throw new GenerateRkpKeyException();
                 default:
                     ProviderException p = new ProviderException("Failed to generate key pair.", e);
                     if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) {
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index 5e2e559..5f8d795 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -1246,20 +1246,42 @@
      * Returns true if the Location came from a mock provider.
      *
      * @return true if this Location came from a mock provider, false otherwise
+     * @deprecated Prefer {@link #isMock()} instead.
      */
+    @Deprecated
     public boolean isFromMockProvider() {
-        return (mFieldsMask & HAS_MOCK_PROVIDER_MASK) != 0;
+        return isMock();
     }
 
     /**
      * Flag this Location as having come from a mock provider or not.
      *
      * @param isFromMockProvider true if this Location came from a mock provider, false otherwise
+     * @deprecated Prefer {@link #setMock(boolean)} instead.
      * @hide
      */
+    @Deprecated
     @SystemApi
     public void setIsFromMockProvider(boolean isFromMockProvider) {
-        if (isFromMockProvider) {
+        setMock(isFromMockProvider);
+    }
+
+    /**
+     * Returns true if this location is marked as a mock location. If this location comes from the
+     * Android framework, this indicates that the location was provided by a test location provider,
+     * and thus may not be related to the actual location of the device.
+     *
+     * @see LocationManager#addTestProvider
+     */
+    public boolean isMock() {
+        return (mFieldsMask & HAS_MOCK_PROVIDER_MASK) != 0;
+    }
+
+    /**
+     * Sets whether this location is marked as a mock location.
+     */
+    public void setMock(boolean mock) {
+        if (mock) {
             mFieldsMask |= HAS_MOCK_PROVIDER_MASK;
         } else {
             mFieldsMask &= ~HAS_MOCK_PROVIDER_MASK;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 9aeef07..343d04f 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3221,6 +3221,23 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public static final int NUM_SOUND_EFFECTS = 16;
 
+    /** @hide */
+    @IntDef(prefix = { "FX_" }, value = {
+            FX_KEY_CLICK,
+            FX_FOCUS_NAVIGATION_UP,
+            FX_FOCUS_NAVIGATION_DOWN,
+            FX_FOCUS_NAVIGATION_LEFT,
+            FX_FOCUS_NAVIGATION_RIGHT,
+            FX_KEYPRESS_STANDARD,
+            FX_KEYPRESS_SPACEBAR,
+            FX_KEYPRESS_DELETE,
+            FX_KEYPRESS_RETURN,
+            FX_KEYPRESS_INVALID,
+            FX_BACK
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SystemSoundEffect {}
+
     /**
      * @hide Number of FX_FOCUS_NAVIGATION_REPEAT_* sound effects
      */
@@ -3296,22 +3313,11 @@
 
     /**
      * Plays a sound effect (Key clicks, lid open/close...)
-     * @param effectType The type of sound effect. One of
-     *            {@link #FX_KEY_CLICK},
-     *            {@link #FX_FOCUS_NAVIGATION_UP},
-     *            {@link #FX_FOCUS_NAVIGATION_DOWN},
-     *            {@link #FX_FOCUS_NAVIGATION_LEFT},
-     *            {@link #FX_FOCUS_NAVIGATION_RIGHT},
-     *            {@link #FX_KEYPRESS_STANDARD},
-     *            {@link #FX_KEYPRESS_SPACEBAR},
-     *            {@link #FX_KEYPRESS_DELETE},
-     *            {@link #FX_KEYPRESS_RETURN},
-     *            {@link #FX_KEYPRESS_INVALID},
-     *            {@link #FX_BACK},
+     * @param effectType The type of sound effect.
      * NOTE: This version uses the UI settings to determine
      * whether sounds are heard or not.
      */
-    public void playSoundEffect(int effectType) {
+    public void playSoundEffect(@SystemSoundEffect int effectType) {
         if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) {
             return;
         }
@@ -3330,24 +3336,13 @@
 
     /**
      * Plays a sound effect (Key clicks, lid open/close...)
-     * @param effectType The type of sound effect. One of
-     *            {@link #FX_KEY_CLICK},
-     *            {@link #FX_FOCUS_NAVIGATION_UP},
-     *            {@link #FX_FOCUS_NAVIGATION_DOWN},
-     *            {@link #FX_FOCUS_NAVIGATION_LEFT},
-     *            {@link #FX_FOCUS_NAVIGATION_RIGHT},
-     *            {@link #FX_KEYPRESS_STANDARD},
-     *            {@link #FX_KEYPRESS_SPACEBAR},
-     *            {@link #FX_KEYPRESS_DELETE},
-     *            {@link #FX_KEYPRESS_RETURN},
-     *            {@link #FX_KEYPRESS_INVALID},
-     *            {@link #FX_BACK},
+     * @param effectType The type of sound effect.
      * @param userId The current user to pull sound settings from
      * NOTE: This version uses the UI settings to determine
      * whether sounds are heard or not.
      * @hide
      */
-    public void  playSoundEffect(int effectType, int userId) {
+    public void  playSoundEffect(@SystemSoundEffect int effectType, int userId) {
         if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) {
             return;
         }
@@ -3366,25 +3361,14 @@
 
     /**
      * Plays a sound effect (Key clicks, lid open/close...)
-     * @param effectType The type of sound effect. One of
-     *            {@link #FX_KEY_CLICK},
-     *            {@link #FX_FOCUS_NAVIGATION_UP},
-     *            {@link #FX_FOCUS_NAVIGATION_DOWN},
-     *            {@link #FX_FOCUS_NAVIGATION_LEFT},
-     *            {@link #FX_FOCUS_NAVIGATION_RIGHT},
-     *            {@link #FX_KEYPRESS_STANDARD},
-     *            {@link #FX_KEYPRESS_SPACEBAR},
-     *            {@link #FX_KEYPRESS_DELETE},
-     *            {@link #FX_KEYPRESS_RETURN},
-     *            {@link #FX_KEYPRESS_INVALID},
-     *            {@link #FX_BACK},
+     * @param effectType The type of sound effect.
      * @param volume Sound effect volume.
      * The volume value is a raw scalar so UI controls should be scaled logarithmically.
      * If a volume of -1 is specified, the AudioManager.STREAM_MUSIC stream volume minus 3dB will be used.
      * NOTE: This version is for applications that have their own
      * settings panel for enabling and controlling volume.
      */
-    public void  playSoundEffect(int effectType, float volume) {
+    public void  playSoundEffect(@SystemSoundEffect int effectType, float volume) {
         if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) {
             return;
         }
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 8db75d6..b51777c 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -45,6 +45,7 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -4690,6 +4691,128 @@
 
     private native void native_enableOnFrameRenderedListener(boolean enable);
 
+    /**
+     * Returns a list of vendor parameter names.
+     * <p>
+     * This method can be called in any codec state except for released state.
+     *
+     * @return a list containing supported vendor parameters; an empty
+     *         list if no vendor parameters are supported. The order of the
+     *         parameters is arbitrary.
+     * @throws IllegalStateException if in the Released state.
+     */
+    @NonNull
+    public List<String> getSupportedVendorParameters() {
+        return native_getSupportedVendorParameters();
+    }
+
+    @NonNull
+    private native List<String> native_getSupportedVendorParameters();
+
+    /**
+     * Contains description of a parameter.
+     */
+    public static class ParameterDescriptor {
+        private ParameterDescriptor() {}
+
+        /**
+         * Returns the name of the parameter.
+         */
+        @NonNull
+        public String getName() {
+            return mName;
+        }
+
+        /**
+         * Returns the type of the parameter.
+         * {@link MediaFormat#TYPE_NULL} is never returned.
+         */
+        @MediaFormat.Type
+        public int getType() {
+            return mType;
+        }
+
+        private String mName;
+        private @MediaFormat.Type int mType;
+    }
+
+    /**
+     * Describe a parameter with the name.
+     * <p>
+     * This method can be called in any codec state except for released state.
+     *
+     * @param name name of the parameter to describe, typically one from
+     *             {@link #getSupportedVendorParameters}.
+     * @return {@link ParameterDescriptor} object that describes the parameter.
+     *         {@code null} if unrecognized / not able to describe.
+     * @throws IllegalStateException if in the Released state.
+     */
+    @Nullable
+    public ParameterDescriptor getParameterDescriptor(@NonNull String name) {
+        return native_getParameterDescriptor(name);
+    }
+
+    @Nullable
+    private native ParameterDescriptor native_getParameterDescriptor(@NonNull String name);
+
+    /**
+     * Subscribe to vendor parameters, so that changes to these parameters generate
+     * output format change event.
+     * <p>
+     * Unrecognized parameter names or standard (non-vendor) parameter names will be ignored.
+     * {@link #reset} also resets the list of subscribed parameters.
+     * If a parameter in {@code names} is already subscribed, it will remain subscribed.
+     * <p>
+     * This method can be called in any codec state except for released state. When called in
+     * running state with newly subscribed parameters, it takes effect no later than the
+     * processing of the subsequently queued buffer. For the new parameters, the codec will generate
+     * output format change event.
+     * <p>
+     * Note that any vendor parameters set in a {@link #configure} or
+     * {@link #setParameters} call are automatically subscribed.
+     * <p>
+     * See also {@link #INFO_OUTPUT_FORMAT_CHANGED} or {@link Callback#onOutputFormatChanged}
+     * for output format change events.
+     *
+     * @param names names of the vendor parameters to subscribe. This may be an empty list,
+     *              and in that case this method will not change the list of subscribed parameters.
+     * @throws IllegalStateException if in the Released state.
+     */
+    public void subscribeToVendorParameters(@NonNull List<String> names) {
+        native_subscribeToVendorParameters(names);
+    }
+
+    private native void native_subscribeToVendorParameters(@NonNull List<String> names);
+
+    /**
+     * Unsubscribe from vendor parameters, so that changes to these parameters
+     * no longer generate output format change event.
+     * <p>
+     * Unrecognized parameter names, standard (non-vendor) parameter names will be ignored.
+     * {@link #reset} also resets the list of subscribed parameters.
+     * If a parameter in {@code names} is already unsubscribed, it will remain unsubscribed.
+     * <p>
+     * This method can be called in any codec state except for released state. When called in
+     * running state with newly unsubscribed parameters, it takes effect no later than the
+     * processing of the subsequently queued buffer.
+     * <p>
+     * Note that any vendor parameters set in a {@link #configure} or
+     * {@link #setParameters} call are automatically subscribed, and with this method
+     * they can be unsubscribed.
+     * <p>
+     * See also {@link #INFO_OUTPUT_FORMAT_CHANGED} or {@link Callback#onOutputFormatChanged}
+     * for output format change events.
+     *
+     * @param names names of the vendor parameters to unsubscribe. This may be an empty list,
+     *              and in that case this method will not change the list of subscribed parameters.
+     * @throws IllegalStateException if in the Released state.
+     */
+    public void unsubscribeFromVendorParameters(@NonNull List<String> names) {
+        native_unsubscribeFromVendorParameters(names);
+    }
+
+    private native void native_unsubscribeFromVendorParameters(@NonNull List<String> names);
+
     private EventHandler getEventHandlerOn(
             @Nullable Handler handler, @NonNull EventHandler lastHandler) {
         if (handler == null) {
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 06d0eb0..a9e8c26 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -3434,11 +3434,14 @@
         public static final int BITRATE_MODE_VBR = 1;
         /** Constant bitrate mode */
         public static final int BITRATE_MODE_CBR = 2;
+        /** Constant bitrate mode with frame drops */
+        public static final int BITRATE_MODE_CBR_FD =  3;
 
         private static final Feature[] bitrates = new Feature[] {
             new Feature("VBR", BITRATE_MODE_VBR, true),
             new Feature("CBR", BITRATE_MODE_CBR, false),
-            new Feature("CQ",  BITRATE_MODE_CQ,  false)
+            new Feature("CQ",  BITRATE_MODE_CQ,  false),
+            new Feature("CBR-FD", BITRATE_MODE_CBR_FD, false)
         };
 
         private static int parseBitrateMode(String mode) {
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 8daa303..1f6855a 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -194,6 +194,14 @@
 
     /**
      * Starts scanning remote routes.
+     * <p>
+     * Route discovery can happen even when the {@link #startScan()} is not called.
+     * This is because the scanning could be started before by other apps.
+     * Therefore, calling this method after calling {@link #stopScan()} does not necessarily mean
+     * that the routes found before are removed and added again.
+     * <p>
+     * Use {@link RouteCallback} to get the route related events.
+     * <p>
      * Note that calling start/stopScan is applied to all system routers in the same process.
      *
      * @see #stopScan()
@@ -208,6 +216,15 @@
 
     /**
      * Stops scanning remote routes to reduce resource consumption.
+     * <p>
+     * Route discovery can be continued even after this method is called.
+     * This is because the scanning is only turned off when all the apps stop scanning.
+     * Therefore, calling this method does not necessarily mean the routes are removed.
+     * Also, for the same reason it does not mean that {@link RouteCallback#onRoutesAdded(List)}
+     * is not called afterwards.
+     * <p>
+     * Use {@link RouteCallback} to get the route related events.
+     * <p>
      * Note that calling start/stopScan is applied to all system routers in the same process.
      *
      * @see #startScan()
@@ -299,24 +316,6 @@
     }
 
     /**
-     * Registers a callback to receive route related events when they change.
-     * <p>
-     * If the specified callback is already registered, its registration will be updated for the
-     * given {@link Executor executor}.
-     * <p>
-     * This will be no-op for non-system routers.
-     * @hide
-     */
-    @SystemApi
-    public void registerRouteCallback(@NonNull @CallbackExecutor Executor executor,
-            @NonNull RouteCallback routeCallback) {
-        if (!isSystemRouter()) {
-            return;
-        }
-        registerRouteCallback(executor, routeCallback, RouteDiscoveryPreference.EMPTY);
-    }
-
-    /**
      * Registers a callback to discover routes and to receive events when they change.
      * <p>
      * If the specified callback is already registered, its registration will be updated for the
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 6fefbe1..758a813 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -148,6 +148,14 @@
 
     /**
      * Starts scanning remote routes.
+     * <p>
+     * Route discovery can happen even when the {@link #startScan()} is not called.
+     * This is because the scanning could be started before by other apps.
+     * Therefore, calling this method after calling {@link #stopScan()} does not necessarily mean
+     * that the routes found before are removed and added again.
+     * <p>
+     * Use {@link Callback} to get the route related events.
+     * <p>
      * @see #stopScan()
      */
     public void startScan() {
@@ -163,6 +171,15 @@
 
     /**
      * Stops scanning remote routes to reduce resource consumption.
+     * <p>
+     * Route discovery can be continued even after this method is called.
+     * This is because the scanning is only turned off when all the apps stop scanning.
+     * Therefore, calling this method does not necessarily mean the routes are removed.
+     * Also, for the same reason it does not mean that {@link Callback#onRoutesAdded(List)}
+     * is not called afterwards.
+     * <p>
+     * Use {@link Callback} to get the route related events.
+     *
      * @see #startScan()
      */
     public void stopScan() {
diff --git a/media/java/android/media/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java
index 2f95247..37fee84 100644
--- a/media/java/android/media/RouteDiscoveryPreference.java
+++ b/media/java/android/media/RouteDiscoveryPreference.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -68,9 +69,10 @@
     private final Bundle mExtras;
 
     /**
-     * An empty discovery preference
+     * An empty discovery preference.
      * @hide
      */
+    @SystemApi
     public static final RouteDiscoveryPreference EMPTY =
             new Builder(Collections.emptyList(), false).build();
 
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index ded2e1b..f694482 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -53,6 +53,7 @@
 
 #include <media/MediaCodecBuffer.h>
 #include <media/hardware/VideoAPI.h>
+#include <media/stagefright/CodecBase.h>
 #include <media/stagefright/MediaCodec.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -84,6 +85,16 @@
     EVENT_FIRST_TUNNEL_FRAME_READY = 4,
 };
 
+// From MediaFormat.java
+enum {
+    TYPE_NULL           = 0,
+    TYPE_INTEGER        = 1,
+    TYPE_LONG           = 2,
+    TYPE_FLOAT          = 3,
+    TYPE_STRING         = 4,
+    TYPE_BYTE_BUFFER    = 5,
+};
+
 static struct CryptoErrorCodes {
     jint cryptoErrorNoKey;
     jint cryptoErrorKeyExpired;
@@ -140,6 +151,8 @@
 } gByteBufferInfo;
 
 static struct {
+    jclass clazz;
+    jmethodID ctorId;
     jmethodID sizeId;
     jmethodID getId;
     jmethodID addId;
@@ -154,6 +167,13 @@
     jfieldID lockId;
 } gLinearBlockInfo;
 
+static struct {
+    jclass clazz;
+    jmethodID ctorId;
+    jfieldID nameId;
+    jfieldID typeId;
+} gDescriptorInfo;
+
 struct fields_t {
     jmethodID postEventFromNativeID;
     jmethodID lockAndGetContextID;
@@ -937,6 +957,74 @@
     (void)mCodec->setParameters(msg);
 }
 
+status_t JMediaCodec::querySupportedVendorParameters(JNIEnv *env, jobject *namesObj) {
+    std::vector<std::string> names;
+    status_t status = mCodec->querySupportedVendorParameters(&names);
+    if (status != OK) {
+        return status;
+    }
+    *namesObj = env->NewObject(gArrayListInfo.clazz, gArrayListInfo.ctorId);
+    for (const std::string &name : names) {
+        ScopedLocalRef<jstring> nameStr{env, env->NewStringUTF(name.c_str())};
+        (void)env->CallBooleanMethod(*namesObj, gArrayListInfo.addId, nameStr.get());
+    }
+    return OK;
+}
+
+status_t JMediaCodec::describeParameter(JNIEnv *env, jstring name, jobject *descObj) {
+    const char *tmp = env->GetStringUTFChars(name, nullptr);
+    CodecParameterDescriptor desc;
+    status_t status = mCodec->describeParameter(tmp, &desc);
+    env->ReleaseStringUTFChars(name, tmp);
+    if (status != OK) {
+        return status;
+    }
+    jint type = TYPE_NULL;
+    switch (desc.type) {
+        case AMessage::kTypeInt32:  type = TYPE_INTEGER;     break;
+        case AMessage::kTypeSize:
+        case AMessage::kTypeInt64:  type = TYPE_LONG;        break;
+        case AMessage::kTypeFloat:  type = TYPE_FLOAT;       break;
+        case AMessage::kTypeString: type = TYPE_STRING;      break;
+        case AMessage::kTypeBuffer: type = TYPE_BYTE_BUFFER; break;
+        default:                    type = TYPE_NULL;        break;
+    }
+    if (type == TYPE_NULL) {
+        return BAD_VALUE;
+    }
+    *descObj = env->NewObject(gDescriptorInfo.clazz, gDescriptorInfo.ctorId);
+    env->SetObjectField(*descObj, gDescriptorInfo.nameId, name);
+    env->SetIntField(*descObj, gDescriptorInfo.typeId, type);
+    return OK;
+}
+
+static void BuildVectorFromList(JNIEnv *env, jobject list, std::vector<std::string> *vec) {
+    ScopedLocalRef<jclass> listClazz{env, env->FindClass("java/util/List")};
+    ScopedLocalRef<jclass> iterClazz{env, env->FindClass("java/util/Iterator")};
+    jmethodID hasNextID = env->GetMethodID(iterClazz.get(), "hasNext", "()Z");
+    jmethodID nextID = env->GetMethodID(iterClazz.get(), "next", "()Ljava/lang/Object;");
+    jobject it = env->CallObjectMethod(
+            list, env->GetMethodID(listClazz.get(), "iterator", "()Ljava/util/Iterator;"));
+    while (env->CallBooleanMethod(it, hasNextID)) {
+        jstring name = (jstring)env->CallObjectMethod(it, nextID);
+        const char *tmp = env->GetStringUTFChars(name, nullptr);
+        vec->push_back(tmp);
+        env->ReleaseStringUTFChars(name, tmp);
+    }
+}
+
+status_t JMediaCodec::subscribeToVendorParameters(JNIEnv *env, jobject namesObj) {
+    std::vector<std::string> names;
+    BuildVectorFromList(env, namesObj, &names);
+    return mCodec->subscribeToVendorParameters(names);
+}
+
+status_t JMediaCodec::unsubscribeFromVendorParameters(JNIEnv *env, jobject namesObj) {
+    std::vector<std::string> names;
+    BuildVectorFromList(env, namesObj, &names);
+    return mCodec->unsubscribeFromVendorParameters(names);
+}
+
 static jthrowable createCodecException(
         JNIEnv *env, status_t err, int32_t actionCode, const char *msg = NULL) {
     ScopedLocalRef<jclass> clazz(
@@ -2671,6 +2759,73 @@
     codec->selectAudioPresentation((int32_t)presentationId, (int32_t)programId);
 }
 
+static jobject android_media_MediaCodec_getSupportedVendorParameters(
+        JNIEnv *env, jobject thiz) {
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL || codec->initCheck() != OK) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return NULL;
+    }
+
+    jobject ret = NULL;
+    status_t status = codec->querySupportedVendorParameters(env, &ret);
+    if (status != OK) {
+        throwExceptionAsNecessary(env, status);
+    }
+
+    return ret;
+}
+
+static jobject android_media_MediaCodec_getParameterDescriptor(
+        JNIEnv *env, jobject thiz, jstring name) {
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL || codec->initCheck() != OK) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return NULL;
+    }
+
+    jobject ret = NULL;
+    status_t status = codec->describeParameter(env, name, &ret);
+    if (status != OK) {
+        ret = NULL;
+    }
+    return ret;
+}
+
+static void android_media_MediaCodec_subscribeToVendorParameters(
+        JNIEnv *env, jobject thiz, jobject names) {
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL || codec->initCheck() != OK) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return;
+    }
+
+    status_t status = codec->subscribeToVendorParameters(env, names);
+    if (status != OK) {
+        throwExceptionAsNecessary(env, status);
+    }
+    return;
+}
+
+static void android_media_MediaCodec_unsubscribeFromVendorParameters(
+        JNIEnv *env, jobject thiz, jobject names) {
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL || codec->initCheck() != OK) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return;
+    }
+
+    status_t status = codec->unsubscribeFromVendorParameters(env, names);
+    if (status != OK) {
+        throwExceptionAsNecessary(env, status);
+    }
+    return;
+}
+
 static void android_media_MediaCodec_native_init(JNIEnv *env, jclass) {
     ScopedLocalRef<jclass> clazz(
             env, env->FindClass("android/media/MediaCodec"));
@@ -2930,6 +3085,10 @@
 
     clazz.reset(env->FindClass("java/util/ArrayList"));
     CHECK(clazz.get() != NULL);
+    gArrayListInfo.clazz = (jclass)env->NewGlobalRef(clazz.get());
+
+    gArrayListInfo.ctorId = env->GetMethodID(clazz.get(), "<init>", "()V");
+    CHECK(gArrayListInfo.ctorId != NULL);
 
     gArrayListInfo.sizeId = env->GetMethodID(clazz.get(), "size", "()I");
     CHECK(gArrayListInfo.sizeId != NULL);
@@ -2960,6 +3119,19 @@
 
     gLinearBlockInfo.lockId = env->GetFieldID(clazz.get(), "mLock", "Ljava/lang/Object;");
     CHECK(gLinearBlockInfo.lockId != NULL);
+
+    clazz.reset(env->FindClass("android/media/MediaCodec$ParameterDescriptor"));
+    CHECK(clazz.get() != NULL);
+    gDescriptorInfo.clazz = (jclass)env->NewGlobalRef(clazz.get());
+
+    gDescriptorInfo.ctorId = env->GetMethodID(clazz.get(), "<init>", "()V");
+    CHECK(gDescriptorInfo.ctorId != NULL);
+
+    gDescriptorInfo.nameId = env->GetFieldID(clazz.get(), "mName", "Ljava/lang/String;");
+    CHECK(gDescriptorInfo.nameId != NULL);
+
+    gDescriptorInfo.typeId = env->GetFieldID(clazz.get(), "mType", "I");
+    CHECK(gDescriptorInfo.typeId != NULL);
 }
 
 static void android_media_MediaCodec_native_setup(
@@ -3289,6 +3461,21 @@
     { "native_setAudioPresentation", "(II)V",
       (void *)android_media_MediaCodec_setAudioPresentation },
 
+    { "native_getSupportedVendorParameters", "()Ljava/util/List;",
+      (void *)android_media_MediaCodec_getSupportedVendorParameters },
+
+    { "native_getParameterDescriptor",
+      "(Ljava/lang/String;)Landroid/media/MediaCodec$ParameterDescriptor;",
+      (void *)android_media_MediaCodec_getParameterDescriptor },
+
+    { "native_subscribeToVendorParameters",
+      "(Ljava/util/List;)V",
+      (void *)android_media_MediaCodec_subscribeToVendorParameters},
+
+    { "native_unsubscribeFromVendorParameters",
+      "(Ljava/util/List;)V",
+      (void *)android_media_MediaCodec_unsubscribeFromVendorParameters},
+
     { "native_init", "()V", (void *)android_media_MediaCodec_native_init },
 
     { "native_setup", "(Ljava/lang/String;ZZ)V",
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 5fd6bfd..ee456c9 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -164,6 +164,14 @@
 
     void selectAudioPresentation(const int32_t presentationId, const int32_t programId);
 
+    status_t querySupportedVendorParameters(JNIEnv *env, jobject *names);
+
+    status_t describeParameter(JNIEnv *env, jstring name, jobject *desc);
+
+    status_t subscribeToVendorParameters(JNIEnv *env, jobject names);
+
+    status_t unsubscribeFromVendorParameters(JNIEnv *env, jobject names);
+
     bool hasCryptoOrDescrambler() { return mHasCryptoOrDescrambler; }
 
     const sp<ICrypto> &getCrypto() { return mCrypto; }
diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp
index 80c68f2..74cecdd 100644
--- a/packages/Connectivity/framework/Android.bp
+++ b/packages/Connectivity/framework/Android.bp
@@ -35,7 +35,6 @@
         ":framework-javastream-protos",
     ],
     apex_available: [
-        "//apex_available:platform",
         "com.android.tethering",
     ],
     jarjar_rules: "jarjar-rules-proto.txt",
@@ -82,11 +81,13 @@
 
 java_sdk_library {
     name: "framework-connectivity",
-    api_only: true,
+    sdk_version: "module_current",
+    min_sdk_version: "30",
     defaults: ["framework-module-defaults"],
     installable: true,
     srcs: [
         ":framework-connectivity-sources",
+        ":net-utils-framework-common-srcs",
     ],
     aidl: {
         include_dirs: [
@@ -98,10 +99,33 @@
             "frameworks/native/aidl/binder", // For PersistableBundle.aidl
         ],
     },
+    impl_only_libs: [
+        // TODO (b/183097033) remove once module_current includes core_platform
+        "stable.core.platform.api.stubs",
+        "framework-tethering.stubs.module_lib",
+        "framework-wifi.stubs.module_lib",
+        "net-utils-device-common",
+    ],
     libs: [
         "unsupportedappusage",
     ],
+    static_libs: [
+        "framework-connectivity-protos",
+    ],
+    jarjar_rules: "jarjar-rules.txt",
     permitted_packages: ["android.net"],
+    impl_library_visibility: [
+        "//packages/modules/Connectivity/Tethering/apex",
+        // In preparation for future move
+        "//packages/modules/Connectivity/apex",
+        "//packages/modules/Connectivity/service",
+        "//frameworks/base/packages/Connectivity/service",
+        "//frameworks/base",
+        "//packages/modules/Connectivity/Tethering/tests/unit",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
 }
 
 cc_defaults {
@@ -149,37 +173,6 @@
     shared_libs: ["libandroid"],
     stl: "libc++_static",
     apex_available: [
-        "//apex_available:platform",
         "com.android.tethering",
     ],
 }
-
-java_library {
-    name: "framework-connectivity.impl",
-    sdk_version: "module_current",
-    min_sdk_version: "30",
-    srcs: [
-        ":framework-connectivity-sources",
-    ],
-    aidl: {
-        include_dirs: [
-            "frameworks/base/core/java", // For framework parcelables
-            "frameworks/native/aidl/binder", // For PersistableBundle.aidl
-        ],
-    },
-    libs: [
-        // TODO (b/183097033) remove once module_current includes core_current
-        "stable.core.platform.api.stubs",
-        "framework-tethering",
-        "framework-wifi",
-        "unsupportedappusage",
-    ],
-    static_libs: [
-        "framework-connectivity-protos",
-        "net-utils-device-common",
-    ],
-    jarjar_rules: "jarjar-rules.txt",
-    apex_available: ["com.android.tethering"],
-    installable: true,
-    permitted_packages: ["android.net"],
-}
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index 2bf807c4..0cc8c23 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -20,6 +20,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAvoidUnvalidated(@NonNull android.net.Network);
     method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setGlobalProxy(@Nullable android.net.ProxyInfo);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setLegacyLockdownVpnEnabled(boolean);
+    method public static void setPrivateDnsMode(@NonNull android.content.Context, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setProfileNetworkPreference(@NonNull android.os.UserHandle, int, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setRequireVpnForUids(boolean, @NonNull java.util.Collection<android.util.Range<java.lang.Integer>>);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
@@ -43,6 +44,52 @@
     field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1; // 0x1
   }
 
+  public class ConnectivitySettingsManager {
+    method public static void clearGlobalProxy(@NonNull android.content.Context);
+    method @Nullable public static String getCaptivePortalHttpUrl(@NonNull android.content.Context);
+    method public static int getCaptivePortalMode(@NonNull android.content.Context, int);
+    method @NonNull public static java.time.Duration getConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+    method @NonNull public static android.util.Range<java.lang.Integer> getDnsResolverSampleRanges(@NonNull android.content.Context);
+    method @NonNull public static java.time.Duration getDnsResolverSampleValidityDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+    method public static int getDnsResolverSuccessThresholdPercent(@NonNull android.content.Context, int);
+    method @Nullable public static android.net.ProxyInfo getGlobalProxy(@NonNull android.content.Context);
+    method @NonNull public static java.time.Duration getMobileDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
+    method public static boolean getMobileDataAlwaysOn(@NonNull android.content.Context, boolean);
+    method @Nullable public static String getMobileDataPreferredApps(@NonNull android.content.Context);
+    method public static int getNetworkAvoidBadWifi(@NonNull android.content.Context);
+    method @Nullable public static String getNetworkMeteredMultipathPreference(@NonNull android.content.Context);
+    method public static int getNetworkSwitchNotificationMaximumDailyCount(@NonNull android.content.Context, int);
+    method @NonNull public static java.time.Duration getNetworkSwitchNotificationRateDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+    method @NonNull public static String getPrivateDnsDefaultMode(@NonNull android.content.Context);
+    method @Nullable public static String getPrivateDnsHostname(@NonNull android.content.Context);
+    method public static boolean getWifiAlwaysRequested(@NonNull android.content.Context, boolean);
+    method @NonNull public static java.time.Duration getWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
+    method public static void setCaptivePortalHttpUrl(@NonNull android.content.Context, @Nullable String);
+    method public static void setCaptivePortalMode(@NonNull android.content.Context, int);
+    method public static void setConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+    method public static void setDnsResolverSampleRanges(@NonNull android.content.Context, @NonNull android.util.Range<java.lang.Integer>);
+    method public static void setDnsResolverSampleValidityDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+    method public static void setDnsResolverSuccessThresholdPercent(@NonNull android.content.Context, @IntRange(from=0, to=100) int);
+    method public static void setGlobalProxy(@NonNull android.content.Context, @NonNull android.net.ProxyInfo);
+    method public static void setMobileDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
+    method public static void setMobileDataAlwaysOn(@NonNull android.content.Context, boolean);
+    method public static void setMobileDataPreferredApps(@NonNull android.content.Context, @Nullable String);
+    method public static void setNetworkAvoidBadWifi(@NonNull android.content.Context, int);
+    method public static void setNetworkMeteredMultipathPreference(@NonNull android.content.Context, @NonNull String);
+    method public static void setNetworkSwitchNotificationMaximumDailyCount(@NonNull android.content.Context, @IntRange(from=0) int);
+    method public static void setNetworkSwitchNotificationRateDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+    method public static void setPrivateDnsDefaultMode(@NonNull android.content.Context, @NonNull String);
+    method public static void setPrivateDnsHostname(@NonNull android.content.Context, @Nullable String);
+    method public static void setWifiAlwaysRequested(@NonNull android.content.Context, boolean);
+    method public static void setWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
+    field public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; // 0x2
+    field public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; // 0x0
+    field public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; // 0x1
+    field public static final int NETWORK_AVOID_BAD_WIFI_AVOID = 2; // 0x2
+    field public static final int NETWORK_AVOID_BAD_WIFI_IGNORE = 0; // 0x0
+    field public static final int NETWORK_AVOID_BAD_WIFI_PROMPT = 1; // 0x1
+  }
+
   public final class NetworkAgentConfig implements android.os.Parcelable {
     method @Nullable public String getSubscriberId();
     method public boolean isBypassableVpn();
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index 9dcc391..593698e 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -218,6 +218,8 @@
     method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
     method public void onAutomaticReconnectDisabled();
     method public void onBandwidthUpdateRequested();
+    method public void onNetworkCreated();
+    method public void onNetworkDisconnected();
     method public void onNetworkUnwanted();
     method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter);
     method public void onQosCallbackUnregistered(int);
@@ -232,8 +234,8 @@
     method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
     method public final void sendNetworkScore(@IntRange(from=0, to=99) int);
     method public final void sendQosCallbackError(int, int);
-    method public final void sendQosSessionAvailable(int, int, @NonNull android.telephony.data.EpsBearerQosSessionAttributes);
-    method public final void sendQosSessionLost(int, int);
+    method public final void sendQosSessionAvailable(int, int, @NonNull android.net.QosSessionAttributes);
+    method public final void sendQosSessionLost(int, int, int);
     method public final void sendSocketKeepaliveEvent(int, int);
     method @Deprecated public void setLegacySubtype(int, @NonNull String);
     method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
@@ -385,6 +387,7 @@
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSession> CREATOR;
     field public static final int TYPE_EPS_BEARER = 1; // 0x1
+    field public static final int TYPE_NR_BEARER = 2; // 0x2
   }
 
   public interface QosSessionAttributes {
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index a73d76e..a9f8b8d 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -3326,9 +3326,10 @@
      * Register or update a network offer with ConnectivityService.
      *
      * ConnectivityService keeps track of offers made by the various providers and matches
-     * them to networking requests made by apps or the system. The provider supplies a score
-     * and the capabilities of the network it might be able to bring up ; these act as filters
-     * used by ConnectivityService to only send those requests that can be fulfilled by the
+     * them to networking requests made by apps or the system. A callback identifies an offer
+     * uniquely, and later calls with the same callback update the offer. The provider supplies a
+     * score and the capabilities of the network it might be able to bring up ; these act as
+     * filters used by ConnectivityService to only send those requests that can be fulfilled by the
      * provider.
      *
      * The provider is under no obligation to be able to bring up the network it offers at any
@@ -5426,4 +5427,23 @@
         if (TextUtils.isEmpty(mode)) mode = PRIVATE_DNS_MODE_OPPORTUNISTIC;
         return mode;
     }
+
+    /**
+     * Set private DNS mode to settings.
+     *
+     * @param context The {@link Context} to set the private DNS mode.
+     * @param mode The private dns mode. This should be one of the PRIVATE_DNS_MODE_* constants.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void setPrivateDnsMode(@NonNull Context context,
+            @NonNull @PrivateDnsMode String mode) {
+        if (!(mode == PRIVATE_DNS_MODE_OFF
+                || mode == PRIVATE_DNS_MODE_OPPORTUNISTIC
+                || mode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) {
+            throw new IllegalArgumentException("Invalid private dns mode");
+        }
+        Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_MODE, mode);
+    }
 }
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
index bbd8393..9a00055 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
@@ -16,16 +16,38 @@
 
 package android.net;
 
+import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER;
+import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE;
+import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+
 import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.net.ConnectivityManager.MultipathPreference;
+import android.net.ConnectivityManager.PrivateDnsMode;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Range;
+
+import com.android.net.module.util.ProxyUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.time.Duration;
+import java.util.List;
 
 /**
  * A manager class for connectivity module settings.
  *
  * @hide
  */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public class ConnectivitySettingsManager {
 
     private ConnectivitySettingsManager() {}
@@ -45,12 +67,16 @@
      * Network activity refers to transmitting or receiving data on the network interfaces.
      *
      * Tracking is disabled if set to zero or negative value.
+     *
+     * @hide
      */
     public static final String DATA_ACTIVITY_TIMEOUT_MOBILE = "data_activity_timeout_mobile";
 
     /**
      * Timeout to tracking Wifi data activity. Same as {@code DATA_ACTIVITY_TIMEOUT_MOBILE}
      * but for Wifi network.
+     *
+     * @hide
      */
     public static final String DATA_ACTIVITY_TIMEOUT_WIFI = "data_activity_timeout_wifi";
 
@@ -58,12 +84,16 @@
 
     /**
      * Sample validity in seconds to configure for the system DNS resolver.
+     *
+     * @hide
      */
     public static final String DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS =
             "dns_resolver_sample_validity_seconds";
 
     /**
      * Success threshold in percent for use with the system DNS resolver.
+     *
+     * @hide
      */
     public static final String DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT =
             "dns_resolver_success_threshold_percent";
@@ -71,24 +101,35 @@
     /**
      * Minimum number of samples needed for statistics to be considered meaningful in the
      * system DNS resolver.
+     *
+     * @hide
      */
     public static final String DNS_RESOLVER_MIN_SAMPLES = "dns_resolver_min_samples";
 
     /**
      * Maximum number taken into account for statistics purposes in the system DNS resolver.
+     *
+     * @hide
      */
     public static final String DNS_RESOLVER_MAX_SAMPLES = "dns_resolver_max_samples";
 
+    private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
+    private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
+
     /** Network switch notification settings */
 
     /**
      * The maximum number of notifications shown in 24 hours when switching networks.
+     *
+     * @hide
      */
     public static final String NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT =
             "network_switch_notification_daily_limit";
 
     /**
      * The minimum time in milliseconds between notifications when switching networks.
+     *
+     * @hide
      */
     public static final String NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS =
             "network_switch_notification_rate_limit_millis";
@@ -98,14 +139,18 @@
     /**
      * The URL used for HTTP captive portal detection upon a new connection.
      * A 204 response code from the server is used for validation.
+     *
+     * @hide
      */
     public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url";
 
     /**
      * What to do when connecting a network that presents a captive portal.
-     * Must be one of the CAPTIVE_PORTAL_MODE_* constants above.
+     * Must be one of the CAPTIVE_PORTAL_MODE_* constants below.
      *
      * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT.
+     *
+     * @hide
      */
     public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode";
 
@@ -139,11 +184,15 @@
 
     /**
      * Host name for global http proxy. Set via ConnectivityManager.
+     *
+     * @hide
      */
     public static final String GLOBAL_HTTP_PROXY_HOST = "global_http_proxy_host";
 
     /**
      * Integer host port for global http proxy. Set via ConnectivityManager.
+     *
+     * @hide
      */
     public static final String GLOBAL_HTTP_PROXY_PORT = "global_http_proxy_port";
 
@@ -153,12 +202,16 @@
      * Domains should be listed in a comma- separated list. Example of
      * acceptable formats: ".domain1.com,my.domain2.com" Use
      * ConnectivityManager to set/get.
+     *
+     * @hide
      */
     public static final String GLOBAL_HTTP_PROXY_EXCLUSION_LIST =
             "global_http_proxy_exclusion_list";
 
     /**
      * The location PAC File for the proxy.
+     *
+     * @hide
      */
     public static final String GLOBAL_HTTP_PROXY_PAC = "global_proxy_pac_url";
 
@@ -171,11 +224,15 @@
      * a specific provider. It may be used to store the provider name even when the
      * mode changes so that temporarily disabling and re-enabling the specific
      * provider mode does not necessitate retyping the provider hostname.
+     *
+     * @hide
      */
     public static final String PRIVATE_DNS_MODE = "private_dns_mode";
 
     /**
      * The specific Private DNS provider name.
+     *
+     * @hide
      */
     public static final String PRIVATE_DNS_SPECIFIER = "private_dns_specifier";
 
@@ -185,6 +242,8 @@
      * all of which require explicit user action to enable/configure. See also b/79719289.
      *
      * Value is a string, suitable for assignment to PRIVATE_DNS_MODE above.
+     *
+     * @hide
      */
     public static final String PRIVATE_DNS_DEFAULT_MODE = "private_dns_default_mode";
 
@@ -194,6 +253,8 @@
      * The number of milliseconds to hold on to a PendingIntent based request. This delay gives
      * the receivers of the PendingIntent an opportunity to make a new network request before
      * the Network satisfying the request is potentially removed.
+     *
+     * @hide
      */
     public static final String CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS =
             "connectivity_release_pending_intent_delay_ms";
@@ -205,6 +266,8 @@
      * See ConnectivityService for more info.
      *
      * (0 = disabled, 1 = enabled)
+     *
+     * @hide
      */
     public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on";
 
@@ -217,6 +280,8 @@
      * See ConnectivityService for more info.
      *
      * (0 = disabled, 1 = enabled)
+     *
+     * @hide
      */
     public static final String WIFI_ALWAYS_REQUESTED = "wifi_always_requested";
 
@@ -228,14 +293,637 @@
      * 0: Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013.
      * null: Ask the user whether to switch away from bad wifi.
      * 1: Avoid bad wifi.
+     *
+     * @hide
      */
     public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi";
 
     /**
+     * Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013.
+     */
+    public static final int NETWORK_AVOID_BAD_WIFI_IGNORE = 0;
+
+    /**
+     * Ask the user whether to switch away from bad wifi.
+     */
+    public static final int NETWORK_AVOID_BAD_WIFI_PROMPT = 1;
+
+    /**
+     * Avoid bad wifi.
+     */
+    public static final int NETWORK_AVOID_BAD_WIFI_AVOID = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            NETWORK_AVOID_BAD_WIFI_IGNORE,
+            NETWORK_AVOID_BAD_WIFI_PROMPT,
+            NETWORK_AVOID_BAD_WIFI_AVOID,
+    })
+    public @interface NetworkAvoidBadWifi {}
+
+    /**
      * User setting for ConnectivityManager.getMeteredMultipathPreference(). This value may be
      * overridden by the system based on device or application state. If null, the value
      * specified by config_networkMeteredMultipathPreference is used.
+     *
+     * @hide
      */
     public static final String NETWORK_METERED_MULTIPATH_PREFERENCE =
             "network_metered_multipath_preference";
+
+    /**
+     * A list of apps that should go on cellular networks in preference even when higher-priority
+     * networks are connected.
+     *
+     * @hide
+     */
+    public static final String MOBILE_DATA_PREFERRED_APPS = "mobile_data_preferred_apps";
+
+    /**
+     * Get mobile data activity timeout from {@link Settings}.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default timeout if no setting value.
+     * @return The {@link Duration} of timeout to track mobile data activity.
+     */
+    @NonNull
+    public static Duration getMobileDataActivityTimeout(@NonNull Context context,
+            @NonNull Duration def) {
+        final int timeout = Settings.Global.getInt(
+                context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_MOBILE, (int) def.getSeconds());
+        return Duration.ofSeconds(timeout);
+    }
+
+    /**
+     * Set mobile data activity timeout to {@link Settings}.
+     * Tracking is disabled if set to zero or negative value.
+     *
+     * Note: Only use the number of seconds in this duration, lower second(nanoseconds) will be
+     * ignored.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param timeout The mobile data activity timeout.
+     */
+    public static void setMobileDataActivityTimeout(@NonNull Context context,
+            @NonNull Duration timeout) {
+        Settings.Global.putInt(context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_MOBILE,
+                (int) timeout.getSeconds());
+    }
+
+    /**
+     * Get wifi data activity timeout from {@link Settings}.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default timeout if no setting value.
+     * @return The {@link Duration} of timeout to track wifi data activity.
+     */
+    @NonNull
+    public static Duration getWifiDataActivityTimeout(@NonNull Context context,
+            @NonNull Duration def) {
+        final int timeout = Settings.Global.getInt(
+                context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_WIFI, (int) def.getSeconds());
+        return Duration.ofSeconds(timeout);
+    }
+
+    /**
+     * Set wifi data activity timeout to {@link Settings}.
+     * Tracking is disabled if set to zero or negative value.
+     *
+     * Note: Only use the number of seconds in this duration, lower second(nanoseconds) will be
+     * ignored.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param timeout The wifi data activity timeout.
+     */
+    public static void setWifiDataActivityTimeout(@NonNull Context context,
+            @NonNull Duration timeout) {
+        Settings.Global.putInt(context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_WIFI,
+                (int) timeout.getSeconds());
+    }
+
+    /**
+     * Get dns resolver sample validity duration from {@link Settings}.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default duration if no setting value.
+     * @return The {@link Duration} of sample validity duration to configure for the system DNS
+     *         resolver.
+     */
+    @NonNull
+    public static Duration getDnsResolverSampleValidityDuration(@NonNull Context context,
+            @NonNull Duration def) {
+        final int duration = Settings.Global.getInt(context.getContentResolver(),
+                DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, (int) def.getSeconds());
+        return Duration.ofSeconds(duration);
+    }
+
+    /**
+     * Set dns resolver sample validity duration to {@link Settings}. The duration must be a
+     * positive number of seconds.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param duration The sample validity duration.
+     */
+    public static void setDnsResolverSampleValidityDuration(@NonNull Context context,
+            @NonNull Duration duration) {
+        final int time = (int) duration.getSeconds();
+        if (time <= 0) {
+            throw new IllegalArgumentException("Invalid duration");
+        }
+        Settings.Global.putInt(
+                context.getContentResolver(), DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, time);
+    }
+
+    /**
+     * Get dns resolver success threshold percent from {@link Settings}.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default value if no setting value.
+     * @return The success threshold in percent for use with the system DNS resolver.
+     */
+    public static int getDnsResolverSuccessThresholdPercent(@NonNull Context context, int def) {
+        return Settings.Global.getInt(
+                context.getContentResolver(), DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT, def);
+    }
+
+    /**
+     * Set dns resolver success threshold percent to {@link Settings}. The threshold percent must
+     * be 0~100.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param percent The success threshold percent.
+     */
+    public static void setDnsResolverSuccessThresholdPercent(@NonNull Context context,
+            @IntRange(from = 0, to = 100) int percent) {
+        if (percent < 0 || percent > 100) {
+            throw new IllegalArgumentException("Percent must be 0~100");
+        }
+        Settings.Global.putInt(
+                context.getContentResolver(), DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT, percent);
+    }
+
+    /**
+     * Get dns resolver samples range from {@link Settings}.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @return The {@link Range<Integer>} of samples needed for statistics to be considered
+     *         meaningful in the system DNS resolver.
+     */
+    @NonNull
+    public static Range<Integer> getDnsResolverSampleRanges(@NonNull Context context) {
+        final int minSamples = Settings.Global.getInt(context.getContentResolver(),
+                DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES);
+        final int maxSamples = Settings.Global.getInt(context.getContentResolver(),
+                DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES);
+        return new Range<>(minSamples, maxSamples);
+    }
+
+    /**
+     * Set dns resolver samples range to {@link Settings}.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param range The samples range. The minimum number should be more than 0 and the maximum
+     *              number should be less that 64.
+     */
+    public static void setDnsResolverSampleRanges(@NonNull Context context,
+            @NonNull Range<Integer> range) {
+        if (range.getLower() < 0 || range.getUpper() > 64) {
+            throw new IllegalArgumentException("Argument must be 0~64");
+        }
+        Settings.Global.putInt(
+                context.getContentResolver(), DNS_RESOLVER_MIN_SAMPLES, range.getLower());
+        Settings.Global.putInt(
+                context.getContentResolver(), DNS_RESOLVER_MAX_SAMPLES, range.getUpper());
+    }
+
+    /**
+     * Get maximum count (from {@link Settings}) of switching network notifications shown in 24
+     * hours.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default value if no setting value.
+     * @return The maximum count of notifications shown in 24 hours when switching networks.
+     */
+    public static int getNetworkSwitchNotificationMaximumDailyCount(@NonNull Context context,
+            int def) {
+        return Settings.Global.getInt(
+                context.getContentResolver(), NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, def);
+    }
+
+    /**
+     * Set maximum count (to {@link Settings}) of switching network notifications shown in 24 hours.
+     * The count must be at least 0.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param count The maximum count of switching network notifications shown in 24 hours.
+     */
+    public static void setNetworkSwitchNotificationMaximumDailyCount(@NonNull Context context,
+            @IntRange(from = 0) int count) {
+        if (count < 0) {
+            throw new IllegalArgumentException("Count must be 0~10.");
+        }
+        Settings.Global.putInt(
+                context.getContentResolver(), NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, count);
+    }
+
+    /**
+     * Get minimum duration (from {@link Settings}) between each switching network notifications.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default time if no setting value.
+     * @return The minimum duration between notifications when switching networks.
+     */
+    @NonNull
+    public static Duration getNetworkSwitchNotificationRateDuration(@NonNull Context context,
+            @NonNull Duration def) {
+        final int duration = Settings.Global.getInt(context.getContentResolver(),
+                NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS, (int) def.toMillis());
+        return Duration.ofMillis(duration);
+    }
+
+    /**
+     * Set minimum duration (to {@link Settings}) between each switching network notifications.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param duration The minimum duration between notifications when switching networks.
+     */
+    public static void setNetworkSwitchNotificationRateDuration(@NonNull Context context,
+            @NonNull Duration duration) {
+        final int time = (int) duration.toMillis();
+        if (time < 0) {
+            throw new IllegalArgumentException("Invalid duration.");
+        }
+        Settings.Global.putInt(context.getContentResolver(),
+                NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS, time);
+    }
+
+    /**
+     * Get URL (from {@link Settings}) used for HTTP captive portal detection upon a new connection.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @return The URL used for HTTP captive portal detection upon a new connection.
+     */
+    @Nullable
+    public static String getCaptivePortalHttpUrl(@NonNull Context context) {
+        return Settings.Global.getString(context.getContentResolver(), CAPTIVE_PORTAL_HTTP_URL);
+    }
+
+    /**
+     * Set URL (to {@link Settings}) used for HTTP captive portal detection upon a new connection.
+     * This URL should respond with a 204 response to a GET request to indicate no captive portal is
+     * present. And this URL must be HTTP as redirect responses are used to find captive portal
+     * sign-in pages. If the URL set to null or be incorrect, it will result in captive portal
+     * detection failed and lost the connection.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param url The URL used for HTTP captive portal detection upon a new connection.
+     */
+    public static void setCaptivePortalHttpUrl(@NonNull Context context, @Nullable String url) {
+        Settings.Global.putString(context.getContentResolver(), CAPTIVE_PORTAL_HTTP_URL, url);
+    }
+
+    /**
+     * Get mode (from {@link Settings}) when connecting a network that presents a captive portal.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default mode if no setting value.
+     * @return The mode when connecting a network that presents a captive portal.
+     */
+    @CaptivePortalMode
+    public static int getCaptivePortalMode(@NonNull Context context,
+            @CaptivePortalMode int def) {
+        return Settings.Global.getInt(context.getContentResolver(), CAPTIVE_PORTAL_MODE, def);
+    }
+
+    /**
+     * Set mode (to {@link Settings}) when connecting a network that presents a captive portal.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param mode The mode when connecting a network that presents a captive portal.
+     */
+    public static void setCaptivePortalMode(@NonNull Context context, @CaptivePortalMode int mode) {
+        if (!(mode == CAPTIVE_PORTAL_MODE_IGNORE
+                || mode == CAPTIVE_PORTAL_MODE_PROMPT
+                || mode == CAPTIVE_PORTAL_MODE_AVOID)) {
+            throw new IllegalArgumentException("Invalid captive portal mode");
+        }
+        Settings.Global.putInt(context.getContentResolver(), CAPTIVE_PORTAL_MODE, mode);
+    }
+
+    /**
+     * Get the global HTTP proxy applied to the device, or null if none.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @return The {@link ProxyInfo} which build from global http proxy settings.
+     */
+    @Nullable
+    public static ProxyInfo getGlobalProxy(@NonNull Context context) {
+        final String host = Settings.Global.getString(
+                context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST);
+        final int port = Settings.Global.getInt(
+                context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* def */);
+        final String exclusionList = Settings.Global.getString(
+                context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
+        final String pacFileUrl = Settings.Global.getString(
+                context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC);
+
+        if (TextUtils.isEmpty(host) && TextUtils.isEmpty(pacFileUrl)) {
+            return null; // No global proxy.
+        }
+
+        if (TextUtils.isEmpty(pacFileUrl)) {
+            return ProxyInfo.buildDirectProxy(
+                    host, port, ProxyUtils.exclusionStringAsList(exclusionList));
+        } else {
+            return ProxyInfo.buildPacProxy(Uri.parse(pacFileUrl));
+        }
+    }
+
+    /**
+     * Set global http proxy settings from given {@link ProxyInfo}.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param proxyInfo The {@link ProxyInfo} for global http proxy settings which build from
+     *                    {@link ProxyInfo#buildPacProxy(Uri)} or
+     *                    {@link ProxyInfo#buildDirectProxy(String, int, List)}
+     */
+    public static void setGlobalProxy(@NonNull Context context, @NonNull ProxyInfo proxyInfo) {
+        final String host = proxyInfo.getHost();
+        final int port = proxyInfo.getPort();
+        final String exclusionList = proxyInfo.getExclusionListAsString();
+        final String pacFileUrl = proxyInfo.getPacFileUrl().toString();
+
+        if (TextUtils.isEmpty(pacFileUrl)) {
+            Settings.Global.putString(context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, host);
+            Settings.Global.putInt(context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, port);
+            Settings.Global.putString(
+                    context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, exclusionList);
+            Settings.Global.putString(
+                    context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, "" /* value */);
+        } else {
+            Settings.Global.putString(
+                    context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, pacFileUrl);
+            Settings.Global.putString(
+                    context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, "" /* value */);
+            Settings.Global.putInt(
+                    context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* value */);
+            Settings.Global.putString(
+                    context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, "" /* value */);
+        }
+    }
+
+    /**
+     * Clear all global http proxy settings.
+     *
+     * @param context The {@link Context} to set the setting.
+     */
+    public static void clearGlobalProxy(@NonNull Context context) {
+        Settings.Global.putString(
+                context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, "" /* value */);
+        Settings.Global.putInt(
+                context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* value */);
+        Settings.Global.putString(
+                context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, "" /* value */);
+        Settings.Global.putString(
+                context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, "" /* value */);
+    }
+
+    /**
+     * Get specific private dns provider name from {@link Settings}.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @return The specific private dns provider name, or null if no setting value.
+     */
+    @Nullable
+    public static String getPrivateDnsHostname(@NonNull Context context) {
+        return Settings.Global.getString(context.getContentResolver(), PRIVATE_DNS_SPECIFIER);
+    }
+
+    /**
+     * Set specific private dns provider name to {@link Settings}.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param specifier The specific private dns provider name.
+     */
+    public static void setPrivateDnsHostname(@NonNull Context context,
+            @Nullable String specifier) {
+        Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_SPECIFIER, specifier);
+    }
+
+    /**
+     * Get default private dns mode from {@link Settings}.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @return The default private dns mode.
+     */
+    @PrivateDnsMode
+    @NonNull
+    public static String getPrivateDnsDefaultMode(@NonNull Context context) {
+        return Settings.Global.getString(context.getContentResolver(), PRIVATE_DNS_DEFAULT_MODE);
+    }
+
+    /**
+     * Set default private dns mode to {@link Settings}.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param mode The default private dns mode. This should be one of the PRIVATE_DNS_MODE_*
+     *             constants.
+     */
+    public static void setPrivateDnsDefaultMode(@NonNull Context context,
+            @NonNull @PrivateDnsMode String mode) {
+        if (!(mode == PRIVATE_DNS_MODE_OFF
+                || mode == PRIVATE_DNS_MODE_OPPORTUNISTIC
+                || mode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) {
+            throw new IllegalArgumentException("Invalid private dns mode");
+        }
+        Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_DEFAULT_MODE, mode);
+    }
+
+    /**
+     * Get duration (from {@link Settings}) to keep a PendingIntent-based request.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default duration if no setting value.
+     * @return The duration to keep a PendingIntent-based request.
+     */
+    @NonNull
+    public static Duration getConnectivityKeepPendingIntentDuration(@NonNull Context context,
+            @NonNull Duration def) {
+        final int duration = Settings.Secure.getInt(context.getContentResolver(),
+                CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, (int) def.toMillis());
+        return Duration.ofMillis(duration);
+    }
+
+    /**
+     * Set duration (to {@link Settings}) to keep a PendingIntent-based request.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param duration The duration to keep a PendingIntent-based request.
+     */
+    public static void setConnectivityKeepPendingIntentDuration(@NonNull Context context,
+            @NonNull Duration duration) {
+        final int time = (int) duration.toMillis();
+        if (time < 0) {
+            throw new IllegalArgumentException("Invalid duration.");
+        }
+        Settings.Secure.putInt(
+                context.getContentResolver(), CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, time);
+    }
+
+    /**
+     * Read from {@link Settings} whether the mobile data connection should remain active
+     * even when higher priority networks are active.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default value if no setting value.
+     * @return Whether the mobile data connection should remain active even when higher
+     *         priority networks are active.
+     */
+    public static boolean getMobileDataAlwaysOn(@NonNull Context context, boolean def) {
+        final int enable = Settings.Global.getInt(
+                context.getContentResolver(), MOBILE_DATA_ALWAYS_ON, (def ? 1 : 0));
+        return (enable != 0) ? true : false;
+    }
+
+    /**
+     * Write into {@link Settings} whether the mobile data connection should remain active
+     * even when higher priority networks are active.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param enable Whether the mobile data connection should remain active even when higher
+     *               priority networks are active.
+     */
+    public static void setMobileDataAlwaysOn(@NonNull Context context, boolean enable) {
+        Settings.Global.putInt(
+                context.getContentResolver(), MOBILE_DATA_ALWAYS_ON, (enable ? 1 : 0));
+    }
+
+    /**
+     * Read from {@link Settings} whether the wifi data connection should remain active
+     * even when higher priority networks are active.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default value if no setting value.
+     * @return Whether the wifi data connection should remain active even when higher
+     *         priority networks are active.
+     */
+    public static boolean getWifiAlwaysRequested(@NonNull Context context, boolean def) {
+        final int enable = Settings.Global.getInt(
+                context.getContentResolver(), WIFI_ALWAYS_REQUESTED, (def ? 1 : 0));
+        return (enable != 0) ? true : false;
+    }
+
+    /**
+     * Write into {@link Settings} whether the wifi data connection should remain active
+     * even when higher priority networks are active.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param enable Whether the wifi data connection should remain active even when higher
+     *               priority networks are active
+     */
+    public static void setWifiAlwaysRequested(@NonNull Context context, boolean enable) {
+        Settings.Global.putInt(
+                context.getContentResolver(), WIFI_ALWAYS_REQUESTED, (enable ? 1 : 0));
+    }
+
+    /**
+     * Get avoid bad wifi setting from {@link Settings}.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @return The setting whether to automatically switch away from wifi networks that lose
+     *         internet access.
+     */
+    @NetworkAvoidBadWifi
+    public static int getNetworkAvoidBadWifi(@NonNull Context context) {
+        final String setting =
+                Settings.Global.getString(context.getContentResolver(), NETWORK_AVOID_BAD_WIFI);
+        if ("0".equals(setting)) {
+            return NETWORK_AVOID_BAD_WIFI_IGNORE;
+        } else if ("1".equals(setting)) {
+            return NETWORK_AVOID_BAD_WIFI_AVOID;
+        } else {
+            return NETWORK_AVOID_BAD_WIFI_PROMPT;
+        }
+    }
+
+    /**
+     * Set avoid bad wifi setting to {@link Settings}.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param value Whether to automatically switch away from wifi networks that lose internet
+     *              access.
+     */
+    public static void setNetworkAvoidBadWifi(@NonNull Context context,
+            @NetworkAvoidBadWifi int value) {
+        final String setting;
+        if (value == NETWORK_AVOID_BAD_WIFI_IGNORE) {
+            setting = "0";
+        } else if (value == NETWORK_AVOID_BAD_WIFI_AVOID) {
+            setting = "1";
+        } else if (value == NETWORK_AVOID_BAD_WIFI_PROMPT) {
+            setting = null;
+        } else {
+            throw new IllegalArgumentException("Invalid avoid bad wifi setting");
+        }
+        Settings.Global.putString(context.getContentResolver(), NETWORK_AVOID_BAD_WIFI, setting);
+    }
+
+    /**
+     * Get network metered multipath preference from {@link Settings}.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @return The network metered multipath preference which should be one of
+     *         ConnectivityManager#MULTIPATH_PREFERENCE_* value or null if the value specified
+     *         by config_networkMeteredMultipathPreference is used.
+     */
+    @Nullable
+    public static String getNetworkMeteredMultipathPreference(@NonNull Context context) {
+        return Settings.Global.getString(
+                context.getContentResolver(), NETWORK_METERED_MULTIPATH_PREFERENCE);
+    }
+
+    /**
+     * Set network metered multipath preference to {@link Settings}.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param preference The network metered multipath preference which should be one of
+     *                   ConnectivityManager#MULTIPATH_PREFERENCE_* value or null if the value
+     *                   specified by config_networkMeteredMultipathPreference is used.
+     */
+    public static void setNetworkMeteredMultipathPreference(@NonNull Context context,
+            @NonNull @MultipathPreference String preference) {
+        if (!(Integer.valueOf(preference) == MULTIPATH_PREFERENCE_HANDOVER
+                || Integer.valueOf(preference) == MULTIPATH_PREFERENCE_RELIABILITY
+                || Integer.valueOf(preference) == MULTIPATH_PREFERENCE_PERFORMANCE)) {
+            throw new IllegalArgumentException("Invalid private dns mode");
+        }
+        Settings.Global.putString(
+                context.getContentResolver(), NETWORK_METERED_MULTIPATH_PREFERENCE, preference);
+    }
+
+    /**
+     * Get the list of apps(from {@link Settings}) that should go on cellular networks in preference
+     * even when higher-priority networks are connected.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @return A list of apps that should go on cellular networks in preference even when
+     *         higher-priority networks are connected or null if no setting value.
+     */
+    @Nullable
+    public static String getMobileDataPreferredApps(@NonNull Context context) {
+        return Settings.Secure.getString(context.getContentResolver(), MOBILE_DATA_PREFERRED_APPS);
+    }
+
+    /**
+     * Set the list of apps(to {@link Settings}) that should go on cellular networks in preference
+     * even when higher-priority networks are connected.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param list A list of apps that should go on cellular networks in preference even when
+     *             higher-priority networks are connected.
+     */
+    public static void setMobileDataPreferredApps(@NonNull Context context, @Nullable String list) {
+        Settings.Secure.putString(context.getContentResolver(), MOBILE_DATA_PREFERRED_APPS, list);
+    }
 }
diff --git a/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl b/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl
index 1f66e18..f9d3994 100644
--- a/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl
+++ b/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl
@@ -46,4 +46,6 @@
     void onRemoveKeepalivePacketFilter(int slot);
     void onQosFilterCallbackRegistered(int qosCallbackId, in QosFilterParcelable filterParcel);
     void onQosCallbackUnregistered(int qosCallbackId);
+    void onNetworkCreated();
+    void onNetworkDisconnected();
 }
diff --git a/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl b/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl
index c5464d3..cbd6193 100644
--- a/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl
+++ b/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl
@@ -22,6 +22,7 @@
 import android.net.NetworkScore;
 import android.net.QosSession;
 import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.telephony.data.NrQosSessionAttributes;
 
 /**
  * Interface for NetworkAgents to send network properties.
@@ -37,6 +38,7 @@
     void sendSocketKeepaliveEvent(int slot, int reason);
     void sendUnderlyingNetworks(in @nullable List<Network> networks);
     void sendEpsQosSessionAvailable(int callbackId, in QosSession session, in EpsBearerQosSessionAttributes attributes);
+    void sendNrQosSessionAvailable(int callbackId, in QosSession session, in NrQosSessionAttributes attributes);
     void sendQosSessionLost(int qosCallbackId, in QosSession session);
     void sendQosCallbackError(int qosCallbackId, int exceptionType);
 }
diff --git a/packages/Connectivity/framework/src/android/net/INetworkOfferCallback.aidl b/packages/Connectivity/framework/src/android/net/INetworkOfferCallback.aidl
index 67d2d405..a6de173 100644
--- a/packages/Connectivity/framework/src/android/net/INetworkOfferCallback.aidl
+++ b/packages/Connectivity/framework/src/android/net/INetworkOfferCallback.aidl
@@ -22,15 +22,16 @@
  * A callback registered with connectivity by network providers together with
  * a NetworkOffer.
  *
- * When the offer is needed to satisfy some application or system component,
- * connectivity will call onOfferNeeded on this callback. When this happens,
- * the provider should try and bring up the network.
+ * When the network for this offer is needed to satisfy some application or
+ * system component, connectivity will call onNetworkNeeded on this callback.
+ * When this happens, the provider should try and bring up the network.
  *
- * When the offer is no longer needed, for example because the application has
- * withdrawn the request or if the request is being satisfied by a network
- * that this offer will never be able to beat, connectivity calls
- * onOfferUnneeded. When this happens, the provider should stop trying to
- * bring up the network, or tear it down if it has already been brought up.
+ * When the network for this offer is no longer needed, for example because
+ * the application has withdrawn the request or if the request is being
+ * satisfied by a network that this offer will never be able to beat,
+ * connectivity calls onNetworkUnneeded. When this happens, the provider
+ * should stop trying to bring up the network, or tear it down if it has
+ * already been brought up.
  *
  * When NetworkProvider#offerNetwork is called, the provider can expect to
  * immediately receive all requests that can be fulfilled by that offer and
@@ -38,25 +39,25 @@
  * request is currently outstanding, because no requests have been made that
  * can be satisfied by this offer, or because all such requests are already
  * satisfied by a better network.
- * onOfferNeeded can be called at any time after registration and until the
+ * onNetworkNeeded can be called at any time after registration and until the
  * offer is withdrawn with NetworkProvider#unofferNetwork is called. This
  * typically happens when a new network request is filed by an application,
  * or when the network satisfying a request disconnects and this offer now
- * stands a chance to be the best network for it.
+ * stands a chance to supply the best network for it.
  *
  * @hide
  */
 oneway interface INetworkOfferCallback {
     /**
-     * Informs the registrant that the offer is needed to fulfill this request.
+     * Called when a network for this offer is needed to fulfill this request.
      * @param networkRequest the request to satisfy
      * @param providerId the ID of the provider currently satisfying
      *          this request, or NetworkProvider.ID_NONE if none.
      */
-    void onOfferNeeded(in NetworkRequest networkRequest, int providerId);
+    void onNetworkNeeded(in NetworkRequest networkRequest, int providerId);
 
     /**
-     * Informs the registrant that the offer is no longer needed to fulfill this request.
+     * Informs the registrant that the offer is no longer valuable to fulfill this request.
      */
-    void onOfferUnneeded(in NetworkRequest networkRequest);
+    void onNetworkUnneeded(in NetworkRequest networkRequest);
 }
diff --git a/packages/Connectivity/framework/src/android/net/IQosCallback.aidl b/packages/Connectivity/framework/src/android/net/IQosCallback.aidl
index 91c7575..c973541 100644
--- a/packages/Connectivity/framework/src/android/net/IQosCallback.aidl
+++ b/packages/Connectivity/framework/src/android/net/IQosCallback.aidl
@@ -19,6 +19,7 @@
 import android.os.Bundle;
 import android.net.QosSession;
 import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.telephony.data.NrQosSessionAttributes;
 
 /**
  * AIDL interface for QosCallback
@@ -29,6 +30,8 @@
 {
      void onQosEpsBearerSessionAvailable(in QosSession session,
         in EpsBearerQosSessionAttributes attributes);
+     void onNrQosSessionAvailable(in QosSession session,
+        in NrQosSessionAttributes attributes);
      void onQosSessionLost(in QosSession session);
      void onError(in int type);
 }
diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
index b3d9616..6b55bb7 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
@@ -32,6 +32,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.telephony.data.NrQosSessionAttributes;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -361,6 +362,22 @@
      */
     public static final int CMD_UNREGISTER_QOS_CALLBACK = BASE + 21;
 
+    /**
+     * Sent by ConnectivityService to {@link NetworkAgent} to inform the agent that its native
+     * network was created and the Network object is now valid.
+     *
+     * @hide
+     */
+    public static final int CMD_NETWORK_CREATED = BASE + 22;
+
+    /**
+     * Sent by ConnectivityService to {@link NetworkAgent} to inform the agent that its native
+     * network was destroyed.
+     *
+     * @hide
+     */
+    public static final int CMD_NETWORK_DISCONNECTED = BASE + 23;
+
     private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
         final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType,
                 config.legacyTypeName, config.legacySubTypeName);
@@ -560,6 +577,14 @@
                             msg.arg1 /* QoS callback id */);
                     break;
                 }
+                case CMD_NETWORK_CREATED: {
+                    onNetworkCreated();
+                    break;
+                }
+                case CMD_NETWORK_DISCONNECTED: {
+                    onNetworkDisconnected();
+                    break;
+                }
             }
         }
     }
@@ -700,6 +725,16 @@
             mHandler.sendMessage(mHandler.obtainMessage(
                     CMD_UNREGISTER_QOS_CALLBACK, qosCallbackId, 0, null));
         }
+
+        @Override
+        public void onNetworkCreated() {
+            mHandler.sendMessage(mHandler.obtainMessage(CMD_NETWORK_CREATED));
+        }
+
+        @Override
+        public void onNetworkDisconnected() {
+            mHandler.sendMessage(mHandler.obtainMessage(CMD_NETWORK_DISCONNECTED));
+        }
     }
 
     /**
@@ -1010,6 +1045,17 @@
     }
 
     /**
+     * Called when ConnectivityService has successfully created this NetworkAgent's native network.
+     */
+    public void onNetworkCreated() {}
+
+
+    /**
+     * Called when ConnectivityService has successfully destroy this NetworkAgent's native network.
+     */
+    public void onNetworkDisconnected() {}
+
+    /**
      * Requests that the network hardware send the specified packet at the specified interval.
      *
      * @param slot the hardware slot on which to start the keepalive.
@@ -1160,29 +1206,37 @@
 
 
     /**
-     * Sends the attributes of Eps Bearer Qos Session back to the Application
+     * Sends the attributes of Qos Session back to the Application
      *
      * @param qosCallbackId the callback id that the session belongs to
-     * @param sessionId the unique session id across all Eps Bearer Qos Sessions
-     * @param attributes the attributes of the Eps Qos Session
+     * @param sessionId the unique session id across all Qos Sessions
+     * @param attributes the attributes of the Qos Session
      */
     public final void sendQosSessionAvailable(final int qosCallbackId, final int sessionId,
-            @NonNull final EpsBearerQosSessionAttributes attributes) {
+            @NonNull final QosSessionAttributes attributes) {
         Objects.requireNonNull(attributes, "The attributes must be non-null");
-        queueOrSendMessage(ra -> ra.sendEpsQosSessionAvailable(qosCallbackId,
-                new QosSession(sessionId, QosSession.TYPE_EPS_BEARER),
-                attributes));
+        if (attributes instanceof EpsBearerQosSessionAttributes) {
+            queueOrSendMessage(ra -> ra.sendEpsQosSessionAvailable(qosCallbackId,
+                    new QosSession(sessionId, QosSession.TYPE_EPS_BEARER),
+                    (EpsBearerQosSessionAttributes)attributes));
+        } else if (attributes instanceof NrQosSessionAttributes) {
+            queueOrSendMessage(ra -> ra.sendNrQosSessionAvailable(qosCallbackId,
+                    new QosSession(sessionId, QosSession.TYPE_NR_BEARER),
+                    (NrQosSessionAttributes)attributes));
+        }
     }
 
     /**
-     * Sends event that the Eps Qos Session was lost.
+     * Sends event that the Qos Session was lost.
      *
      * @param qosCallbackId the callback id that the session belongs to
-     * @param sessionId the unique session id across all Eps Bearer Qos Sessions
+     * @param sessionId the unique session id across all Qos Sessions
+     * @param qosSessionType the session type {@code QosSesson#QosSessionType}
      */
-    public final void sendQosSessionLost(final int qosCallbackId, final int sessionId) {
+    public final void sendQosSessionLost(final int qosCallbackId,
+            final int sessionId, final int qosSessionType) {
         queueOrSendMessage(ra -> ra.sendQosSessionLost(qosCallbackId,
-                new QosSession(sessionId, QosSession.TYPE_EPS_BEARER)));
+                new QosSession(sessionId, qosSessionType)));
     }
 
     /**
diff --git a/packages/Connectivity/framework/src/android/net/NetworkProvider.java b/packages/Connectivity/framework/src/android/net/NetworkProvider.java
index d5b5c9b..d859022 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkProvider.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkProvider.java
@@ -170,10 +170,11 @@
     /** @hide */
     // TODO : make @SystemApi when the impl is complete
     public interface NetworkOfferCallback {
-        /** Called by the system when this offer is needed to satisfy some networking request. */
-        void onOfferNeeded(@NonNull NetworkRequest request, int providerId);
-        /** Called by the system when this offer is no longer needed. */
-        void onOfferUnneeded(@NonNull NetworkRequest request);
+        /** Called by the system when a network for this offer is needed to satisfy some
+         *  networking request. */
+        void onNetworkNeeded(@NonNull NetworkRequest request, int providerId);
+        /** Called by the system when this offer is no longer valuable for this request. */
+        void onNetworkUnneeded(@NonNull NetworkRequest request);
     }
 
     private class NetworkOfferCallbackProxy extends INetworkOfferCallback.Stub {
@@ -187,14 +188,14 @@
         }
 
         @Override
-        public void onOfferNeeded(final @NonNull NetworkRequest request,
+        public void onNetworkNeeded(final @NonNull NetworkRequest request,
                 final int providerId) {
-            mExecutor.execute(() -> callback.onOfferNeeded(request, providerId));
+            mExecutor.execute(() -> callback.onNetworkNeeded(request, providerId));
         }
 
         @Override
-        public void onOfferUnneeded(final @NonNull NetworkRequest request) {
-            mExecutor.execute(() -> callback.onOfferUnneeded(request));
+        public void onNetworkUnneeded(final @NonNull NetworkRequest request) {
+            mExecutor.execute(() -> callback.onNetworkUnneeded(request));
         }
     }
 
@@ -213,41 +214,41 @@
     }
 
     /**
-     * Register or update an offer for network with the passed caps and score.
+     * Register or update an offer for network with the passed capabilities and score.
      *
-     * A NetworkProvider's job is to provide networks. This function is how a provider tells the
+     * A NetworkProvider's role is to provide networks. This method is how a provider tells the
      * connectivity stack what kind of network it may provide. The score and caps arguments act
-     * as filters that the connectivity stack uses to tell when the offer is necessary. When an
-     * offer might be advantageous over existing networks, the provider will receive a call to
-     * the associated callback's {@link NetworkOfferCallback#onOfferNeeded} method. The provider
-     * should then try to bring up this network. When an offer is no longer needed, the stack
-     * will inform the provider by calling {@link NetworkOfferCallback#onOfferUnneeded}. The
+     * as filters that the connectivity stack uses to tell when the offer is valuable. When an
+     * offer might be preferred over existing networks, the provider will receive a call to
+     * the associated callback's {@link NetworkOfferCallback#onNetworkNeeded} method. The provider
+     * should then try to bring up this network. When an offer is no longer useful, the stack
+     * will inform the provider by calling {@link NetworkOfferCallback#onNetworkUnneeded}. The
      * provider should stop trying to bring up such a network, or disconnect it if it already has
      * one.
      *
-     * The stack determines what offers are needed according to what networks are currently
+     * The stack determines what offers are valuable according to what networks are currently
      * available to the system, and what networking requests are made by applications. If an
-     * offer looks like it could be a better choice than any existing network for any particular
-     * request, that's when the stack decides the offer is needed. If the current networking
-     * requests are all satisfied by networks that this offer can't possibly be a better match
-     * for, that's when the offer is unneeded. An offer starts off as unneeded ; the provider
-     * should not try to bring up the network until {@link NetworkOfferCallback#onOfferNeeded}
-     * is called.
+     * offer looks like it could connect a better network than any existing network for any
+     * particular request, that's when the stack decides the network is needed. If the current
+     * networking requests are all satisfied by networks that this offer couldn't possibly be a
+     * better match for, that's when the offer is no longer valuable. An offer starts out as
+     * unneeded ; the provider should not try to bring up the network until
+     * {@link NetworkOfferCallback#onNetworkNeeded} is called.
      *
      * Note that the offers are non-binding to the providers, in particular because providers
      * often don't know if they will be able to bring up such a network at any given time. For
-     * example, no wireless network may be in range when the offer is needed. This is fine and
-     * expected ; the provider should simply continue to try to bring up the network and do so
+     * example, no wireless network may be in range when the offer would be valuable. This is fine
+     * and expected ; the provider should simply continue to try to bring up the network and do so
      * if/when it becomes possible. In the mean time, the stack will continue to satisfy requests
      * with the best network currently available, or if none, keep the apps informed that no
      * network can currently satisfy this request. When/if the provider can bring up the network,
      * the connectivity stack will match it against requests, and inform interested apps of the
      * availability of this network. This may, in turn, render the offer of some other provider
-     * unneeded if all requests it used to satisfy are now better served by this network.
+     * low-value if all requests it used to satisfy are now better served by this network.
      *
      * A network can become unneeded for a reason like the above : whether the provider managed
      * to bring up the offered network after it became needed or not, some other provider may
-     * bring up a better network than this one, making this offer unneeded. A network may also
+     * bring up a better network than this one, making this network unneeded. A network may also
      * become unneeded if the application making the request withdrew it (for example, after it
      * is done transferring data, or if the user canceled an operation).
      *
diff --git a/packages/Connectivity/framework/src/android/net/NetworkUtils.java b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
index 16a49bc..2679b62 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkUtils.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
@@ -42,6 +42,9 @@
  * {@hide}
  */
 public class NetworkUtils {
+    static {
+        System.loadLibrary("framework-connectivity-jni");
+    }
 
     private static final String TAG = "NetworkUtils";
 
diff --git a/packages/Connectivity/framework/src/android/net/QosCallbackConnection.java b/packages/Connectivity/framework/src/android/net/QosCallbackConnection.java
index bdb4ad6..de0fc24 100644
--- a/packages/Connectivity/framework/src/android/net/QosCallbackConnection.java
+++ b/packages/Connectivity/framework/src/android/net/QosCallbackConnection.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.telephony.data.NrQosSessionAttributes;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -84,6 +85,25 @@
     }
 
     /**
+     * Called when either the {@link NrQosSessionAttributes} has changed or on the first time
+     * the attributes have become available.
+     *
+     * @param session the session that is now available
+     * @param attributes the corresponding attributes of session
+     */
+    @Override
+    public void onNrQosSessionAvailable(@NonNull final QosSession session,
+            @NonNull final NrQosSessionAttributes attributes) {
+
+        mExecutor.execute(() -> {
+            final QosCallback callback = mCallback;
+            if (callback != null) {
+                callback.onQosSessionAvailable(session, attributes);
+            }
+        });
+    }
+
+    /**
      * Called when the session is lost.
      *
      * @param session the session that was lost
diff --git a/packages/Connectivity/framework/src/android/net/QosSession.java b/packages/Connectivity/framework/src/android/net/QosSession.java
index 4f3bb77..93f2ff2 100644
--- a/packages/Connectivity/framework/src/android/net/QosSession.java
+++ b/packages/Connectivity/framework/src/android/net/QosSession.java
@@ -36,6 +36,11 @@
      */
     public static final int TYPE_EPS_BEARER = 1;
 
+    /**
+     * The {@link QosSession} is a NR Session.
+     */
+    public static final int TYPE_NR_BEARER = 2;
+
     private final int mSessionId;
 
     private final int mSessionType;
@@ -100,6 +105,7 @@
      */
     @IntDef(value = {
             TYPE_EPS_BEARER,
+            TYPE_NR_BEARER,
     })
     @interface QosSessionType {}
 
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index 9d1bb0f..b44128b 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -83,7 +83,6 @@
         "service-connectivity-protos",
     ],
     apex_available: [
-        "//apex_available:platform",
         "com.android.tethering",
     ],
 }
@@ -100,7 +99,6 @@
     ],
     libs: ["libprotobuf-java-nano"],
     apex_available: [
-        "//apex_available:platform",
         "com.android.tethering",
     ],
 }
@@ -115,7 +113,7 @@
     ],
     jarjar_rules: "jarjar-rules.txt",
     apex_available: [
-        "//apex_available:platform",
+        "//apex_available:platform", // For arc-services
         "com.android.tethering",
     ],
 }
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
index 231babe..dd9fc2c 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
@@ -23,5 +23,6 @@
     apex_available: [
         "//apex_available:platform",
         "com.android.cellbroadcast",
+        "com.android.permission",
     ],
 }
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
new file mode 100644
index 0000000..24d53ab
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<androidx.coordinatorlayout.widget.CoordinatorLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/content_parent"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:transitionGroup="true">
+
+    <com.google.android.material.appbar.AppBarLayout
+        android:id="@+id/app_bar"
+        android:layout_width="match_parent"
+        android:layout_height="180dp"
+        android:theme="@style/Theme.CollapsingToolbar.Settings">
+
+        <com.android.settingslib.collapsingtoolbar.AdjustableToolbarLayout
+            android:id="@+id/collapsing_toolbar"
+            android:background="?android:attr/colorPrimary"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            app:maxLines="3"
+            app:contentScrim="?android:attr/colorPrimary"
+            app:collapsedTitleTextAppearance="@style/CollapsingToolbarTitle.Collapsed"
+            app:statusBarScrim="?android:attr/colorPrimary"
+            app:layout_scrollFlags="scroll|exitUntilCollapsed"
+            app:expandedTitleMarginStart="18dp"
+            app:expandedTitleMarginEnd="18dp"
+            app:toolbarId="@id/action_bar">
+
+            <Toolbar
+                android:id="@+id/action_bar"
+                android:layout_width="match_parent"
+                android:layout_height="?attr/actionBarSize"
+                android:theme="?android:attr/actionBarTheme"
+                app:layout_collapseMode="pin"/>
+
+        </com.android.settingslib.collapsingtoolbar.AdjustableToolbarLayout>
+    </com.google.android.material.appbar.AppBarLayout>
+
+    <FrameLayout
+        android:id="@+id/content_frame"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/collapsing_toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/collapsing_toolbar_base_layout.xml
index e376930..c799b99 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/collapsing_toolbar_base_layout.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/collapsing_toolbar_base_layout.xml
@@ -14,47 +14,23 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
-<androidx.coordinatorlayout.widget.CoordinatorLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
+<!-- The main content view -->
+<LinearLayout
     android:id="@+id/content_parent"
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:transitionGroup="true">
-
-    <com.google.android.material.appbar.AppBarLayout
-        android:id="@+id/app_bar"
+    android:fitsSystemWindows="true"
+    android:transitionGroup="true"
+    android:orientation="vertical">
+    <Toolbar
+        android:id="@+id/action_bar"
+        style="?android:attr/actionBarStyle"
         android:layout_width="match_parent"
-        android:layout_height="180dp"
-        android:theme="@style/Theme.CollapsingToolbar.Settings">
-
-        <com.android.settingslib.collapsingtoolbar.AdjustableToolbarLayout
-            android:id="@+id/collapsing_toolbar"
-            android:background="?android:attr/colorPrimary"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            app:maxLines="3"
-            app:contentScrim="?android:attr/colorPrimary"
-            app:collapsedTitleTextAppearance="@style/CollapsingToolbarTitle.Collapsed"
-            app:statusBarScrim="?android:attr/colorPrimary"
-            app:layout_scrollFlags="scroll|exitUntilCollapsed"
-            app:expandedTitleMarginStart="18dp"
-            app:expandedTitleMarginEnd="18dp"
-            app:toolbarId="@id/action_bar">
-
-            <Toolbar
-                android:id="@+id/action_bar"
-                android:layout_width="match_parent"
-                android:layout_height="?attr/actionBarSize"
-                android:theme="?android:attr/actionBarTheme"
-                app:layout_collapseMode="pin"/>
-
-        </com.android.settingslib.collapsingtoolbar.AdjustableToolbarLayout>
-    </com.google.android.material.appbar.AppBarLayout>
-
+        android:layout_height="wrap_content"
+        android:theme="?android:attr/actionBarTheme" />
     <FrameLayout
         android:id="@+id/content_frame"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
-</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
+        android:layout_height="match_parent"/>
+</LinearLayout>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/toolbar_base_layout.xml
deleted file mode 100644
index c799b99..0000000
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/toolbar_base_layout.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
--->
-<!-- The main content view -->
-<LinearLayout
-    android:id="@+id/content_parent"
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:fitsSystemWindows="true"
-    android:transitionGroup="true"
-    android:orientation="vertical">
-    <Toolbar
-        android:id="@+id/action_bar"
-        style="?android:attr/actionBarStyle"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:theme="?android:attr/actionBarTheme" />
-    <FrameLayout
-        android:id="@+id/content_frame"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"/>
-</LinearLayout>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index ad94cd03..957bac7 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -24,7 +24,6 @@
 import android.widget.Toolbar;
 
 import androidx.annotation.Nullable;
-import androidx.core.os.BuildCompat;
 import androidx.fragment.app.FragmentActivity;
 
 import com.google.android.material.appbar.CollapsingToolbarLayout;
@@ -41,15 +40,8 @@
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        // TODO(b/181723278): Update the version check after SDK for S is finalized
-        // The collapsing toolbar is only supported if the android platform version is S or higher.
-        // Otherwise the regular action bar will be shown.
-        if (BuildCompat.isAtLeastS()) {
-            super.setContentView(R.layout.collapsing_toolbar_base_layout);
-            mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar);
-        } else {
-            super.setContentView(R.layout.toolbar_base_layout);
-        }
+        super.setContentView(R.layout.collapsing_toolbar_base_layout);
+        mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar);
 
         final Toolbar toolbar = findViewById(R.id.action_bar);
         setActionBar(toolbar);
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseFragment.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseFragment.java
new file mode 100644
index 0000000..c4c74ff
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseFragment.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.collapsingtoolbar;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.Toolbar;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import com.google.android.material.appbar.CollapsingToolbarLayout;
+
+/**
+ * A base fragment that has a collapsing toolbar layout for enabling the collapsing toolbar design.
+ */
+public abstract class CollapsingToolbarBaseFragment extends Fragment {
+
+    @Nullable
+    private CollapsingToolbarLayout mCollapsingToolbarLayout;
+    @NonNull
+    private Toolbar mToolbar;
+    @NonNull
+    private FrameLayout mContentFrameLayout;
+
+    @Nullable
+    @Override
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+            @Nullable Bundle savedInstanceState) {
+        final View view = inflater.inflate(R.layout.collapsing_toolbar_base_layout, container,
+                false);
+        mCollapsingToolbarLayout = view.findViewById(R.id.collapsing_toolbar);
+        mToolbar = view.findViewById(R.id.action_bar);
+        mContentFrameLayout = view.findViewById(R.id.content_frame);
+        return view;
+    }
+
+    @Override
+    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        requireActivity().setActionBar(mToolbar);
+    }
+
+    /**
+     * Return the collapsing toolbar layout.
+     */
+    @Nullable
+    public CollapsingToolbarLayout getCollapsingToolbarLayout() {
+        return mCollapsingToolbarLayout;
+    }
+
+    /**
+     * Return the content frame layout.
+     */
+    @NonNull
+    public FrameLayout getContentFrameLayout() {
+        return mContentFrameLayout;
+    }
+}
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 8d9a562..ae9261c 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -63,7 +63,8 @@
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
-        LayoutInflater.from(context).inflate(R.layout.main_switch_bar, this);
+        LayoutInflater.from(context).inflate(resourceId(context, "layout", "main_switch_bar"),
+                this);
 
         setFocusable(true);
         setClickable(true);
@@ -255,4 +256,8 @@
 
         requestLayout();
     }
+
+    private int resourceId(Context context, String type, String name) {
+        return context.getResources().getIdentifier(name, type, context.getPackageName());
+    }
 }
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 7186ec5..927e9db 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1272,6 +1272,22 @@
     <!-- Button label for generic OK action [CHAR LIMIT=20] -->
     <string name="okay">OK</string>
 
+    <!-- Label for the settings activity for controlling apps that can schedule alarms [CHAR LIMIT=30] -->
+    <string name="alarms_and_reminders_label">Alarms and reminders</string>
+    <!-- Label for the switch to toggler the permission for scheduling alarms [CHAR LIMIT=50] -->
+    <string name="alarms_and_reminders_switch_title">Allow to set alarms or reminders</string>
+    <!-- Title for the setting screen for controlling apps that can schedule alarms [CHAR LIMIT=30] -->
+    <string name="alarms_and_reminders_title">Alarms and reminders</string>
+    <!-- Description that appears below the alarms_and_reminders switch [CHAR LIMIT=NONE] -->
+    <string name="alarms_and_reminders_footer_title">
+        Allow this app to schedule alarms or other timing based events.
+        This will allow the app to wake up and run even when you are not using the device.
+        Note that revoking this permission may cause the app to malfunction, specifically any alarms
+        that the app has scheduled will no longer work.
+    </string>
+    <!-- Keywords for setting screen for controlling apps that can schedule alarms [CHAR LIMIT=100] -->
+    <string name="keywords_alarms_and_reminders">schedule, alarm, reminder, event</string>
+
     <!--  Do not disturb: Label for button in enable zen dialog that will turn on zen mode. [CHAR LIMIT=30] -->
     <string name="zen_mode_enable_dialog_turn_on">Turn on</string>
     <!-- Do not disturb: Title for the Do not Disturb dialog to turn on Do not disturb. [CHAR LIMIT=50]-->
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
index 8987968..a5da8b6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -31,6 +31,7 @@
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.settingslib.R;
@@ -198,4 +199,17 @@
         }
         return false;
     }
+
+    /**
+     * Returns a boolean indicating whether a given package is a default browser.
+     *
+     * @param packageName a given package.
+     * @return true if the given package is default browser.
+     */
+    public static boolean isDefaultBrowser(Context context, String packageName) {
+        final String defaultBrowserPackage =
+                context.getPackageManager().getDefaultBrowserPackageNameAsUser(
+                        UserHandle.myUserId());
+        return TextUtils.equals(packageName, defaultBrowserPackage);
+    }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index db9b83e..53920f0 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -165,6 +165,8 @@
         VALIDATORS.put(Secure.ASSIST_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ASSIST_GESTURE_WAKE_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.ASSIST_TOUCH_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.ASSIST_LONG_PRESS_HOME_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.VR_DISPLAY_MODE, new DiscreteValueValidator(new String[] {"0", "1"}));
         VALIDATORS.put(Secure.NOTIFICATION_BADGING, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.NOTIFICATION_DISMISS_RTL, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 7288371..4119dc9f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1881,6 +1881,12 @@
         dumpSetting(s, p,
                 Settings.Secure.ASSIST_GESTURE_SETUP_COMPLETE,
                 SecureSettingsProto.Assist.GESTURE_SETUP_COMPLETE);
+        dumpSetting(s, p,
+                Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED,
+                SecureSettingsProto.Assist.TOUCH_GESTURE_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED,
+                SecureSettingsProto.Assist.LONG_PRESS_HOME_ENABLED);
         p.end(assistToken);
 
         final long assistHandlesToken = p.start(SecureSettingsProto.ASSIST_HANDLES);
@@ -2150,6 +2156,9 @@
         dumpSetting(s, p,
                 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
                 SecureSettingsProto.InputMethods.SHOW_IME_WITH_HARD_KEYBOARD);
+        dumpSetting(s, p,
+                Settings.Secure.DEFAULT_VOICE_INPUT_METHOD,
+                SecureSettingsProto.InputMethods.DEFAULT_VOICE_INPUT_METHOD);
         p.end(inputMethodsToken);
 
         dumpSetting(s, p,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java
index caa9b4f..95c2d2e 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java
@@ -51,6 +51,15 @@
         return Resources.ID_NULL;
     }
 
+    /**
+     * @return resource id of the string to use for closing the detail panel. If
+     * {@code Resources.ID_NULL}, then use the default string:
+     * {@code com.android.systemui.R.string.quick_settings_done}
+     */
+    default int getDoneText() {
+        return Resources.ID_NULL;
+    }
+
     void setToggleState(boolean state);
     int getMetricsCategory();
 
diff --git a/packages/SystemUI/res/layout/wireless_charging_layout.xml b/packages/SystemUI/res/layout/wireless_charging_layout.xml
index 730f24f..d82151d 100644
--- a/packages/SystemUI/res/layout/wireless_charging_layout.xml
+++ b/packages/SystemUI/res/layout/wireless_charging_layout.xml
@@ -22,6 +22,11 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
+    <com.android.systemui.statusbar.charging.ChargingRippleView
+        android:id="@+id/wireless_charging_ripple"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
     <!-- Circle animation -->
     <ImageView
         android:id="@+id/wireless_charging_view"
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index be49e1f..886f98e 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -124,6 +124,7 @@
     <attr name="darkIconTheme" format="reference" />
     <attr name="wallpaperTextColor" format="reference|color" />
     <attr name="wallpaperTextColorSecondary" format="reference|color" />
+    <attr name="wallpaperTextColorAccent" format="reference|color" />
     <attr name="backgroundProtectedStyle" format="reference" />
 
     <declare-styleable name="SmartReplyView">
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index fbe7175..6e61148 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -99,7 +99,7 @@
 
     <!-- The default tiles to display in QuickSettings -->
     <string name="quick_settings_tiles_default" translatable="false">
-        wifi,bt,dnd,flashlight,rotation,battery,cell,airplane,cast,screenrecord
+        internet,wifi,bt,dnd,flashlight,rotation,battery,cell,airplane,cast,screenrecord
     </string>
 
     <!-- The minimum number of tiles to display in QuickSettings -->
@@ -107,7 +107,7 @@
 
     <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
     <string name="quick_settings_tiles_stock" translatable="false">
-        wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle,mictoggle,controls,alarm,wallet
+        internet,wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle,mictoggle,controls,alarm,wallet
     </string>
 
     <!-- The tiles to display in QuickSettings -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 935f025..3ca885a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -239,10 +239,8 @@
     <string name="screenshot_edit_label">Edit</string>
     <!-- Content description indicating that tapping the element will allow editing the screenshot [CHAR LIMIT=NONE] -->
     <string name="screenshot_edit_description">Edit screenshot</string>
-    <!-- Label for UI element which allows scrolling and extending the screenshot to be taller [CHAR LIMIT=30] -->
-    <string name="screenshot_scroll_label">Scroll</string>
-    <!-- Content description UI element which allows scrolling and extending the screenshot to be taller [CHAR LIMIT=NONE] -->
-    <string name="screenshot_scroll_description">Scroll screenshot</string>
+    <!-- Label for UI element which allows the user to capture additional off-screen content in a screenshot. [CHAR LIMIT=30] -->
+    <string name="screenshot_scroll_label">Capture more</string>
     <!-- Content description indicating that tapping a button will dismiss the screenshots UI [CHAR LIMIT=NONE] -->
     <string name="screenshot_dismiss_description">Dismiss screenshot</string>
     <!-- Content description indicating that the view is a preview of the screenshot that was just taken [CHAR LIMIT=NONE] -->
@@ -893,6 +891,8 @@
     <string name="quick_settings_more_user_settings">User settings</string>
     <!-- QuickSettings: Control panel: Label for button that dismisses control panel. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_done">Done</string>
+    <!-- QuickSettings: Control panel: Label for button that dismisses user switcher control panel. [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_close_user_panel">Close</string>
     <!-- QuickSettings: Control panel: Label for connected device. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_connected">Connected</string>
     <!-- QuickSettings: Control panel: Label for connected device, showing remote device battery level. [CHAR LIMIT=NONE] -->
@@ -1284,6 +1284,9 @@
     <!-- Disclosure at the bottom of Quick Settings that indicates that the device has a managed profile which can be monitored by the profile owner [CHAR LIMIT=100] -->
     <string name="quick_settings_disclosure_named_managed_profile_monitoring"><xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> may monitor network traffic in your work profile</string>
 
+    <!-- Disclosure at the bottom of Quick Settings that indicates that the user's device has a managed profile where network activity is visible to their IT admin [CHAR LIMIT=100] -->
+    <string name="quick_settings_disclosure_managed_profile_network_activity">Work profile network activity is visible to your IT admin</string>
+
     <!-- Disclosure at the bottom of Quick Settings that indicates that a certificate authorithy is installed on this device and the traffic might be monitored [CHAR LIMIT=100] -->
     <string name="quick_settings_disclosure_monitoring">Network may be monitored</string>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 4a661dc..ecc1a5c 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -321,6 +321,7 @@
         <item name="darkIconTheme">@style/DualToneDarkTheme</item>
         <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
         <item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_dark</item>
+        <item name="wallpaperTextColorAccent">@*android:color/system_accent1_100</item>
         <item name="android:colorError">@*android:color/error_color_material_dark</item>
         <item name="android:colorControlHighlight">@*android:color/primary_text_material_dark</item>
         <item name="*android:lockPatternStyle">@style/LockPatternStyle</item>
@@ -337,6 +338,7 @@
     <style name="Theme.SystemUI.Light">
         <item name="wallpaperTextColor">@*android:color/primary_text_material_light</item>
         <item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_light</item>
+        <item name="wallpaperTextColorAccent">@*android:color/system_accent2_600</item>
         <item name="android:colorError">@*android:color/error_color_material_light</item>
         <item name="android:colorControlHighlight">#40000000</item>
         <item name="shadowRadius">0</item>
diff --git a/packages/SystemUI/res/xml/people_space_widget_info.xml b/packages/SystemUI/res/xml/people_space_widget_info.xml
index d2bff18..b2bf6da 100644
--- a/packages/SystemUI/res/xml/people_space_widget_info.xml
+++ b/packages/SystemUI/res/xml/people_space_widget_info.xml
@@ -16,9 +16,9 @@
 
 <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
     android:minWidth="120dp"
-    android:minHeight="54dp"
+    android:minHeight="50dp"
     android:minResizeWidth="60dp"
-    android:minResizeHeight="54dp"
+    android:minResizeHeight="50dp"
     android:maxResizeHeight="207dp"
     android:updatePeriodMillis="60000"
     android:description="@string/people_tile_description"
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 3f0e3eb..ab219f3 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -131,7 +131,7 @@
 
     private void initColors() {
         mLockScreenColor = Utils.getColorAttrDefaultColor(getContext(),
-                com.android.systemui.R.attr.wallpaperTextColor);
+                com.android.systemui.R.attr.wallpaperTextColorAccent);
         mView.setColors(mDozingColor, mLockScreenColor);
         mView.animateDoze(mIsDozing, false);
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
index bb1d972..cfef6cb 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
@@ -160,11 +160,18 @@
      *
      * @param separator Separator between different parts of the text
      */
-    private CarrierTextManager(Context context, CharSequence separator, boolean showAirplaneMode,
-            boolean showMissingSim, @Nullable WifiManager wifiManager,
-            TelephonyManager telephonyManager, TelephonyListenerManager telephonyListenerManager,
-            WakefulnessLifecycle wakefulnessLifecycle, @Main Executor mainExecutor,
-            @Background Executor bgExecutor, KeyguardUpdateMonitor keyguardUpdateMonitor) {
+    private CarrierTextManager(
+            Context context,
+            CharSequence separator,
+            boolean showAirplaneMode,
+            boolean showMissingSim,
+            @Nullable WifiManager wifiManager,
+            TelephonyManager telephonyManager,
+            TelephonyListenerManager telephonyListenerManager,
+            WakefulnessLifecycle wakefulnessLifecycle,
+            @Main Executor mainExecutor,
+            @Background Executor bgExecutor,
+            KeyguardUpdateMonitor keyguardUpdateMonitor) {
         mContext = context;
         mIsEmergencyCallCapable = telephonyManager.isVoiceCapable();
 
@@ -633,12 +640,15 @@
         private boolean mShowMissingSim;
 
         @Inject
-        public Builder(Context context, @Main Resources resources,
+        public Builder(
+                Context context,
+                @Main Resources resources,
                 @Nullable WifiManager wifiManager,
                 TelephonyManager telephonyManager,
                 TelephonyListenerManager telephonyListenerManager,
                 WakefulnessLifecycle wakefulnessLifecycle,
-                @Main Executor mainExecutor, @Background Executor bgExecutor,
+                @Main Executor mainExecutor,
+                @Background Executor bgExecutor,
                 KeyguardUpdateMonitor keyguardUpdateMonitor) {
             mContext = context;
             mSeparator = resources.getString(
@@ -668,8 +678,8 @@
         public CarrierTextManager build() {
             return new CarrierTextManager(
                     mContext, mSeparator, mShowAirplaneMode, mShowMissingSim, mWifiManager,
-                    mTelephonyManager, mTelephonyListenerManager,
-                    mWakefulnessLifecycle, mMainExecutor, mBgExecutor, mKeyguardUpdateMonitor);
+                    mTelephonyManager, mTelephonyListenerManager, mWakefulnessLifecycle,
+                    mMainExecutor, mBgExecutor, mKeyguardUpdateMonitor);
         }
     }
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index 561ea40..568bea0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -70,7 +70,7 @@
 
     void onThemeChanged() {
         TypedArray array = mContext.obtainStyledAttributes(new int[] {
-                android.R.attr.textColor
+                android.R.attr.textColorPrimary
         });
         ColorStateList newTextColors = ColorStateList.valueOf(array.getColor(0, Color.RED));
         array.recycle();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index 378907c..e274843 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -62,7 +62,8 @@
 
         mBgProtection = findViewById(R.id.udfps_keyguard_fp_bg);
 
-        mWallpaperTexColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor);
+        mWallpaperTexColor = Utils.getColorAttrDefaultColor(mContext,
+                R.attr.wallpaperTextColorAccent);
         mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext,
                 android.R.attr.textColorPrimary);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index 2569f7c..f7beaf1 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -26,7 +26,6 @@
 import android.util.Log;
 import android.util.Slog;
 import android.view.Gravity;
-import android.view.View;
 import android.view.WindowManager;
 
 /**
@@ -98,8 +97,8 @@
         private final Handler mHandler;
 
         private int mGravity;
-        private View mView;
-        private View mNextView;
+        private WirelessChargingLayout mView;
+        private WirelessChargingLayout mNextView;
         private WindowManager mWM;
         private Callback mCallback;
 
@@ -112,7 +111,7 @@
             mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;
 
             final WindowManager.LayoutParams params = mParams;
-            params.height = WindowManager.LayoutParams.WRAP_CONTENT;
+            params.height = WindowManager.LayoutParams.MATCH_PARENT;
             params.width = WindowManager.LayoutParams.MATCH_PARENT;
             params.format = PixelFormat.TRANSLUCENT;
 
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index e8407f0..ce0b514 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -20,6 +20,7 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.graphics.PointF;
 import android.graphics.drawable.Animatable;
 import android.util.AttributeSet;
 import android.util.TypedValue;
@@ -29,8 +30,10 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.android.settingslib.Utils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.charging.ChargingRippleView;
 
 import java.text.NumberFormat;
 
@@ -38,7 +41,9 @@
  * @hide
  */
 public class WirelessChargingLayout extends FrameLayout {
-    public final static int UNKNOWN_BATTERY_LEVEL = -1;
+    public static final int UNKNOWN_BATTERY_LEVEL = -1;
+    private static final long RIPPLE_ANIMATION_DURATION = 2000;
+    private ChargingRippleView mRippleView;
 
     public WirelessChargingLayout(Context context) {
         super(context);
@@ -120,6 +125,8 @@
         AnimatorSet animatorSet = new AnimatorSet();
         animatorSet.playTogether(textSizeAnimator, textOpacityAnimator, textFadeAnimator);
 
+        mRippleView = findViewById(R.id.wireless_charging_ripple);
+
         if (!showTransmittingBatteryLevel) {
             chargingAnimation.start();
             animatorSet.start();
@@ -195,4 +202,21 @@
         animatorSetTransmitting.start();
         animatorSetIcon.start();
     }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        if (mRippleView != null) {
+            int width = getMeasuredWidth();
+            int height = getMeasuredHeight();
+            mRippleView.setColor(
+                    Utils.getColorAttr(mRippleView.getContext(),
+                            android.R.attr.colorAccent).getDefaultColor());
+            mRippleView.setOrigin(new PointF(width / 2, height / 2));
+            mRippleView.setRadius(Math.max(width, height) * 0.5f);
+            mRippleView.setDuration(RIPPLE_ANIMATION_DURATION);
+            mRippleView.startRipple();
+        }
+
+        super.onLayout(changed, left, top, right, bottom);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 52c9f16..b7f6e2b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -186,7 +186,7 @@
                 new TriggerSensor(
                         findSensorWithType(config.quickPickupSensorType()),
                         Settings.Secure.DOZE_QUICK_PICKUP_GESTURE,
-                        false /* setting default */,
+                        true /* setting default */,
                         config.quickPickupSensorEnabled(KeyguardUpdateMonitor.getCurrentUser())
                                 && udfpsEnrolled,
                         DozeLog.REASON_SENSOR_QUICK_PICKUP,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 9d43e0c..c8dfde1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -209,6 +209,8 @@
     private @TransitionMode int mNavigationBarMode;
     private ContentResolver mContentResolver;
     private boolean mAssistantAvailable;
+    private boolean mLongPressHomeEnabled;
+    private boolean mAssistantTouchGestureEnabled;
 
     private int mDisabledFlags1;
     private int mDisabledFlags2;
@@ -309,7 +311,7 @@
 
             // Send the assistant availability upon connection
             if (isConnected) {
-                sendAssistantAvailability(mAssistantAvailable);
+                updateAssistantEntrypoints();
             }
         }
 
@@ -404,12 +406,7 @@
             new Handler(Looper.getMainLooper())) {
         @Override
         public void onChange(boolean selfChange, Uri uri) {
-            boolean available = mAssistManagerLazy.get()
-                    .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
-            if (mAssistantAvailable != available) {
-                sendAssistantAvailability(available);
-                mAssistantAvailable = available;
-            }
+            updateAssistantEntrypoints();
         }
     };
 
@@ -531,6 +528,13 @@
         mContentResolver.registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
                 false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
+        mContentResolver.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
+                false, mAssistContentObserver, UserHandle.USER_ALL);
+        mContentResolver.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
+                false, mAssistContentObserver, UserHandle.USER_ALL);
+        updateAssistantEntrypoints();
 
         if (savedState != null) {
             mDisabledFlags1 = savedState.getInt(EXTRA_DISABLE_STATE, 0);
@@ -823,7 +827,7 @@
                 || mNavigationBarView.getHomeButton().getCurrentView() == null) {
             return;
         }
-        if (mHomeButtonLongPressDurationMs.isPresent()) {
+        if (mHomeButtonLongPressDurationMs.isPresent() || !mLongPressHomeEnabled) {
             mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(false);
             mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(false);
             mNavigationBarView.getHomeButton().setOnLongClickListener(null);
@@ -845,6 +849,8 @@
         pw.println("  mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation);
         pw.println("  mCurrentRotation=" + mCurrentRotation);
         pw.println("  mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs);
+        pw.println("  mLongPressHomeEnabled=" + mLongPressHomeEnabled);
+        pw.println("  mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled);
 
         if (mNavigationBarView != null) {
             pw.println("  mNavigationBarWindowState="
@@ -1206,9 +1212,11 @@
                         return true;
                     }
                 }
-                mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> {
-                    mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration);
-                });
+                if (mLongPressHomeEnabled) {
+                    mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> {
+                        mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration);
+                    });
+                }
                 break;
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
@@ -1480,15 +1488,23 @@
                 | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
     }
 
-    private void sendAssistantAvailability(boolean available) {
+    private void updateAssistantEntrypoints() {
+        mAssistantAvailable = mAssistManagerLazy.get()
+                .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
+        mLongPressHomeEnabled = Settings.Secure.getInt(mContentResolver,
+                Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, 1) != 0;
+        mAssistantTouchGestureEnabled = Settings.Secure.getInt(mContentResolver,
+                Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, 1) != 0;
         if (mOverviewProxyService.getProxy() != null) {
             try {
-                mOverviewProxyService.getProxy().onAssistantAvailable(available
+                mOverviewProxyService.getProxy().onAssistantAvailable(mAssistantAvailable
+                        && mAssistantTouchGestureEnabled
                         && QuickStepContract.isGesturalMode(mNavBarMode));
             } catch (RemoteException e) {
                 Log.w(TAG, "Unable to send assistant availability data to launcher");
             }
         }
+        reconfigureHomeLongClick();
     }
 
     // ----- Methods that DisplayNavigationBarController talks to -----
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 5bc1280..440c5ef 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -175,7 +175,7 @@
 
     /** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */
     public static void setSharedPreferencesStorageForTile(Context context, PeopleTileKey key,
-            int appWidgetId) {
+            int appWidgetId, Uri contactUri) {
         // Write relevant persisted storage.
         SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
                 Context.MODE_PRIVATE);
@@ -186,27 +186,24 @@
         widgetEditor.apply();
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
         SharedPreferences.Editor editor = sp.edit();
-        editor.putString(String.valueOf(appWidgetId), key.getShortcutId());
+        String contactUriString = contactUri == null ? EMPTY_STRING : contactUri.toString();
+        editor.putString(String.valueOf(appWidgetId), contactUriString);
 
         // Don't overwrite existing widgets with the same key.
-        Set<String> storedWidgetIds = new HashSet<>(
-                sp.getStringSet(key.toString(), new HashSet<>()));
-        storedWidgetIds.add(String.valueOf(appWidgetId));
-        editor.putStringSet(key.toString(), storedWidgetIds);
+        addAppWidgetIdForKey(sp, editor, appWidgetId, key.toString());
+        addAppWidgetIdForKey(sp, editor, appWidgetId, contactUriString);
         editor.apply();
     }
 
     /** Removes stored data when tile is deleted. */
     public static void removeSharedPreferencesStorageForTile(Context context, PeopleTileKey key,
-            int widgetId) {
+            int widgetId, String contactUriString) {
         // Delete widgetId mapping to key.
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
         SharedPreferences.Editor editor = sp.edit();
-        Set<String> storedWidgetIds = new HashSet<>(
-                sp.getStringSet(key.toString(), new HashSet<>()));
-        storedWidgetIds.remove(String.valueOf(widgetId));
-        editor.putStringSet(key.toString(), storedWidgetIds);
         editor.remove(String.valueOf(widgetId));
+        removeAppWidgetIdForKey(sp, editor, widgetId, key.toString());
+        removeAppWidgetIdForKey(sp, editor, widgetId, contactUriString);
         editor.apply();
 
         // Delete all data specifically mapped to widgetId.
@@ -219,6 +216,23 @@
         widgetEditor.apply();
     }
 
+    private static void addAppWidgetIdForKey(SharedPreferences sp, SharedPreferences.Editor editor,
+            int widgetId, String storageKey) {
+        Set<String> storedWidgetIdsByKey = new HashSet<>(
+                sp.getStringSet(storageKey, new HashSet<>()));
+        storedWidgetIdsByKey.add(String.valueOf(widgetId));
+        editor.putStringSet(storageKey, storedWidgetIdsByKey);
+    }
+
+    private static void removeAppWidgetIdForKey(SharedPreferences sp,
+            SharedPreferences.Editor editor,
+            int widgetId, String storageKey) {
+        Set<String> storedWidgetIds = new HashSet<>(
+                sp.getStringSet(storageKey, new HashSet<>()));
+        storedWidgetIds.remove(String.valueOf(widgetId));
+        editor.putStringSet(storageKey, storedWidgetIds);
+    }
+
     /** Augments a single {@link PeopleSpaceTile} with notification content, if one is present. */
     public static PeopleSpaceTile augmentSingleTileFromVisibleNotifications(Context context,
             PeopleSpaceTile tile, NotificationEntryManager notificationEntryManager) {
@@ -256,7 +270,7 @@
             PeopleSpaceTile tile, Map<PeopleTileKey, NotificationEntry> visibleNotifications) {
         PeopleTileKey key = new PeopleTileKey(
                 tile.getId(), getUserId(tile), tile.getPackageName());
-
+        // TODO: Match missed calls with matching Uris in addition to keys.
         if (!visibleNotifications.containsKey(key)) {
             if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key.toString());
             return tile;
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index 776e8a2..5be2d4a 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -16,17 +16,23 @@
 
 package com.android.systemui.people.widget;
 
+import static android.Manifest.permission.READ_CONTACTS;
+import static android.app.Notification.CATEGORY_MISSED_CALL;
+import static android.app.Notification.EXTRA_PEOPLE_LIST;
+
 import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING;
 import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID;
 import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
 import static com.android.systemui.people.PeopleSpaceUtils.SHORTCUT_ID;
 import static com.android.systemui.people.PeopleSpaceUtils.USER_ID;
 import static com.android.systemui.people.PeopleSpaceUtils.augmentTileFromNotification;
+import static com.android.systemui.people.PeopleSpaceUtils.getMessagingStyleMessages;
 import static com.android.systemui.people.PeopleSpaceUtils.getStoredWidgetIds;
 import static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetOptionsAndView;
 import static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetViews;
 
 import android.annotation.Nullable;
+import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.PendingIntent;
 import android.app.Person;
@@ -39,6 +45,7 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
 import android.content.pm.ShortcutInfo;
 import android.net.Uri;
 import android.os.Bundle;
@@ -54,16 +61,20 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.UiEventLoggerImpl;
+import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.people.PeopleSpaceUtils;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 
+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;
 import java.util.Set;
 
 import javax.inject.Inject;
@@ -83,11 +94,19 @@
     private SharedPreferences mSharedPrefs;
     private PeopleManager mPeopleManager;
     private NotificationEntryManager mNotificationEntryManager;
+    private PackageManager mPackageManager;
     public UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
     @GuardedBy("mLock")
     public static Map<PeopleTileKey, PeopleSpaceWidgetProvider.TileConversationListener>
             mListeners = new HashMap<>();
 
+    @GuardedBy("mLock")
+    // Map of notification key mapped to widget IDs previously updated by the contact Uri field.
+    // This is required because on notification removal, the contact Uri field is stripped and we
+    // only have the notification key to determine which widget IDs should be updated.
+    private Map<String, Set<String>> mNotificationKeyToWidgetIdsMatchedByUri = new HashMap<>();
+    private boolean mIsForTesting;
+
     @Inject
     public PeopleSpaceWidgetManager(Context context) {
         if (DEBUG) Log.d(TAG, "constructor");
@@ -99,6 +118,7 @@
         mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
         mPeopleManager = mContext.getSystemService(PeopleManager.class);
         mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
+        mPackageManager = mContext.getPackageManager();
     }
 
     /**
@@ -108,12 +128,15 @@
     protected void setAppWidgetManager(
             AppWidgetManager appWidgetManager, IPeopleManager iPeopleManager,
             PeopleManager peopleManager, LauncherApps launcherApps,
-            NotificationEntryManager notificationEntryManager) {
+            NotificationEntryManager notificationEntryManager, PackageManager packageManager,
+            boolean isForTesting) {
         mAppWidgetManager = appWidgetManager;
         mIPeopleManager = iPeopleManager;
         mPeopleManager = peopleManager;
         mLauncherApps = launcherApps;
         mNotificationEntryManager = notificationEntryManager;
+        mPackageManager = packageManager;
+        mIsForTesting = isForTesting;
     }
 
     /**
@@ -222,6 +245,16 @@
     public void updateWidgetsWithNotificationChanged(StatusBarNotification sbn,
             PeopleSpaceUtils.NotificationAction notificationAction) {
         if (DEBUG) Log.d(TAG, "updateWidgetsWithNotificationChanged called");
+        if (mIsForTesting) {
+            updateWidgetsWithNotificationChangedInBackground(sbn, notificationAction);
+            return;
+        }
+        ThreadUtils.postOnBackgroundThread(
+                () -> updateWidgetsWithNotificationChangedInBackground(sbn, notificationAction));
+    }
+
+    private void updateWidgetsWithNotificationChangedInBackground(StatusBarNotification sbn,
+            PeopleSpaceUtils.NotificationAction action) {
         try {
             String sbnShortcutId = sbn.getShortcutId();
             if (sbnShortcutId == null) {
@@ -235,23 +268,175 @@
                 Log.d(TAG, "No app widget ids returned");
                 return;
             }
+            PeopleTileKey key = new PeopleTileKey(
+                    sbnShortcutId,
+                    sbn.getUser().getIdentifier(),
+                    sbn.getPackageName());
+            if (!key.isValid()) {
+                Log.d(TAG, "Invalid key");
+                return;
+            }
             synchronized (mLock) {
-                PeopleTileKey key = new PeopleTileKey(
-                        sbnShortcutId,
-                        UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier(),
-                        sbn.getPackageName());
-                Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, key);
-                for (String widgetIdString : storedWidgetIds) {
-                    int widgetId = Integer.parseInt(widgetIdString);
-                    if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey());
-                    updateStorageAndViewWithNotificationData(sbn, notificationAction, widgetId);
-                }
+                // First, update People Tiles associated with the Notification's package/shortcut.
+                Set<String> tilesUpdatedByKey = getStoredWidgetIds(mSharedPrefs, key);
+                updateWidgetIdsForNotificationAction(tilesUpdatedByKey, sbn, action);
+
+                // Then, update People Tiles across other packages that use the same Uri.
+                updateTilesByUri(key, sbn, action);
             }
         } catch (Exception e) {
             Log.e(TAG, "Exception: " + e);
         }
     }
 
+    /** Updates {@code widgetIdsToUpdate} with {@code action}. */
+    private void updateWidgetIdsForNotificationAction(Set<String> widgetIdsToUpdate,
+            StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction action) {
+        for (String widgetIdString : widgetIdsToUpdate) {
+            int widgetId = Integer.parseInt(widgetIdString);
+            PeopleSpaceTile storedTile = getTileForExistingWidget(widgetId);
+            if (storedTile == null) {
+                if (DEBUG) Log.d(TAG, "Could not find stored tile for notification");
+                continue;
+            }
+            if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey());
+            updateStorageAndViewWithNotificationData(sbn, action, widgetId,
+                    storedTile);
+        }
+    }
+
+    /**
+     * Updates tiles with matched Uris, dependent on the {@code action}.
+     *
+     * <p>If the notification was added, adds the notification based on the contact Uri within
+     * {@code sbn}.
+     * <p>If the notification was removed, removes the notification based on the in-memory map of
+     * widgets previously updated by Uri (since the contact Uri is stripped from the {@code sbn}).
+     */
+    private void updateTilesByUri(PeopleTileKey key, StatusBarNotification sbn,
+            PeopleSpaceUtils.NotificationAction action) {
+        if (action.equals(PeopleSpaceUtils.NotificationAction.POSTED)) {
+            Set<String> widgetIdsUpdatedByUri = supplementTilesByUri(sbn, action, key);
+            if (widgetIdsUpdatedByUri != null && !widgetIdsUpdatedByUri.isEmpty()) {
+                if (DEBUG) Log.d(TAG, "Added due to uri: " + widgetIdsUpdatedByUri);
+                mNotificationKeyToWidgetIdsMatchedByUri.put(sbn.getKey(), widgetIdsUpdatedByUri);
+            }
+        } else {
+            // Remove the notification on any widgets where the notification was added
+            // purely based on the Uri.
+            Set<String> widgetsPreviouslyUpdatedByUri =
+                    mNotificationKeyToWidgetIdsMatchedByUri.remove(sbn.getKey());
+            if (widgetsPreviouslyUpdatedByUri != null && !widgetsPreviouslyUpdatedByUri.isEmpty()) {
+                if (DEBUG) Log.d(TAG, "Remove due to uri: " + widgetsPreviouslyUpdatedByUri);
+                updateWidgetIdsForNotificationAction(widgetsPreviouslyUpdatedByUri, sbn,
+                        action);
+            }
+        }
+    }
+
+    /**
+     * Retrieves from storage any tiles with the same contact Uri as linked via the {@code sbn}.
+     * Supplements the tiles with the notification content only if they still have {@link
+     * android.Manifest.permission.READ_CONTACTS} permission.
+     */
+    @Nullable
+    private Set<String> supplementTilesByUri(StatusBarNotification sbn,
+            PeopleSpaceUtils.NotificationAction notificationAction, PeopleTileKey key) {
+        if (!shouldMatchNotificationByUri(sbn)) {
+            if (DEBUG) Log.d(TAG, "Should not supplement conversation");
+            return null;
+        }
+
+        // Try to get the Contact Uri from the Missed Call notification directly.
+        String contactUri = getContactUri(sbn);
+        if (contactUri == null) {
+            if (DEBUG) Log.d(TAG, "No contact uri");
+            return null;
+        }
+
+        // Supplement any tiles with the same Uri.
+        Set<String> storedWidgetIdsByUri =
+                new HashSet<>(mSharedPrefs.getStringSet(contactUri, new HashSet<>()));
+        if (storedWidgetIdsByUri.isEmpty()) {
+            if (DEBUG) Log.d(TAG, "No tiles for contact");
+            return null;
+        }
+
+        if (mPackageManager.checkPermission(READ_CONTACTS,
+                sbn.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
+            if (DEBUG) Log.d(TAG, "Notifying app missing permissions");
+            return null;
+        }
+
+        Set<String> widgetIdsUpdatedByUri = new HashSet<>();
+        for (String widgetIdString : storedWidgetIdsByUri) {
+            int widgetId = Integer.parseInt(widgetIdString);
+            PeopleSpaceTile storedTile = getTileForExistingWidget(widgetId);
+            // Don't update a widget already updated.
+            if (key.equals(new PeopleTileKey(storedTile))) {
+                continue;
+            }
+            if (storedTile == null || mPackageManager.checkPermission(READ_CONTACTS,
+                    storedTile.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
+                if (DEBUG) Log.d(TAG, "Cannot supplement tile: " + storedTile.getUserName());
+                continue;
+            }
+            if (DEBUG) Log.d(TAG, "Adding notification by uri: " + sbn.getKey());
+            updateStorageAndViewWithNotificationData(sbn, notificationAction,
+                    widgetId, storedTile);
+            widgetIdsUpdatedByUri.add(String.valueOf(widgetId));
+        }
+        return widgetIdsUpdatedByUri;
+    }
+
+    /**
+     * Try to retrieve a valid Uri via {@code sbn}, falling back to the {@code
+     * contactUriFromShortcut} if valid.
+     */
+    @Nullable
+    private String getContactUri(StatusBarNotification sbn) {
+        // First, try to get a Uri from the Person directly set on the Notification.
+        ArrayList<Person> people = sbn.getNotification().extras.getParcelableArrayList(
+                EXTRA_PEOPLE_LIST);
+        if (people != null && people.get(0) != null) {
+            String contactUri = people.get(0).getUri();
+            if (contactUri != null && !contactUri.isEmpty()) {
+                return contactUri;
+            }
+        }
+
+        // Then, try to get a Uri from the Person set on the Notification message.
+        List<Notification.MessagingStyle.Message> messages =
+                getMessagingStyleMessages(sbn.getNotification());
+        if (messages != null && !messages.isEmpty()) {
+            Notification.MessagingStyle.Message message = messages.get(0);
+            Person sender = message.getSenderPerson();
+            if (sender != null && sender.getUri() != null && !sender.getUri().isEmpty()) {
+                return sender.getUri();
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns whether a notification should be matched to other Tiles by Uri.
+     *
+     * <p>Currently only matches missed calls.
+     */
+    private boolean shouldMatchNotificationByUri(StatusBarNotification sbn) {
+        Notification notification = sbn.getNotification();
+        if (notification == null) {
+            if (DEBUG) Log.d(TAG, "Notification is null");
+            return false;
+        }
+        if (!Objects.equals(notification.category, CATEGORY_MISSED_CALL)) {
+            if (DEBUG) Log.d(TAG, "Not missed call");
+            return false;
+        }
+        return true;
+    }
+
     /**
      * Update the tiles associated with the incoming conversation update.
      */
@@ -309,16 +494,11 @@
     private void updateStorageAndViewWithNotificationData(
             StatusBarNotification sbn,
             PeopleSpaceUtils.NotificationAction notificationAction,
-            int appWidgetId) {
-        PeopleSpaceTile storedTile = getTileForExistingWidget(appWidgetId);
-        if (storedTile == null) {
-            if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to");
-            return;
-        }
+            int appWidgetId, PeopleSpaceTile storedTile) {
         if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) {
             if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId);
             storedTile = augmentTileFromNotification(mContext, storedTile, sbn);
-        } else {
+        } else if (storedTile.getNotificationKey().equals(sbn.getKey())) {
             if (DEBUG) {
                 Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId);
             }
@@ -440,7 +620,8 @@
         synchronized (mLock) {
             if (DEBUG) Log.d(TAG, "Add storage for : " + tile.getId());
             PeopleTileKey key = new PeopleTileKey(tile);
-            PeopleSpaceUtils.setSharedPreferencesStorageForTile(mContext, key, appWidgetId);
+            PeopleSpaceUtils.setSharedPreferencesStorageForTile(mContext, key, appWidgetId,
+                    tile.getContactUri());
         }
         try {
             if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId());
@@ -496,6 +677,7 @@
             // Retrieve storage needed for widget deletion.
             PeopleTileKey key;
             Set<String> storedWidgetIdsForKey;
+            String contactUriString;
             synchronized (mLock) {
                 SharedPreferences widgetSp = mContext.getSharedPreferences(String.valueOf(widgetId),
                         Context.MODE_PRIVATE);
@@ -509,9 +691,11 @@
                 }
                 storedWidgetIdsForKey = new HashSet<>(
                         mSharedPrefs.getStringSet(key.toString(), new HashSet<>()));
+                contactUriString = mSharedPrefs.getString(String.valueOf(widgetId), null);
             }
             synchronized (mLock) {
-                PeopleSpaceUtils.removeSharedPreferencesStorageForTile(mContext, key, widgetId);
+                PeopleSpaceUtils.removeSharedPreferencesStorageForTile(mContext, key, widgetId,
+                        contactUriString);
             }
             // If another tile with the conversation is still stored, we need to keep the listener.
             if (DEBUG) Log.d(TAG, "Stored widget IDs: " + storedWidgetIdsForKey.toString());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 05bc6e2..d54d3f2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -140,9 +140,10 @@
     }
 
     private void updateDetailText() {
-        mDetailDoneButton.setText(R.string.quick_settings_done);
-        final int resId =
-                mDetailAdapter != null ? mDetailAdapter.getSettingsText() : Resources.ID_NULL;
+        int resId = mDetailAdapter != null ? mDetailAdapter.getDoneText() : Resources.ID_NULL;
+        mDetailDoneButton.setText(
+                (resId != Resources.ID_NULL) ? resId : R.string.quick_settings_done);
+        resId = mDetailAdapter != null ? mDetailAdapter.getSettingsText() : Resources.ID_NULL;
         mDetailSettingsButton.setText(
                 (resId != Resources.ID_NULL) ? resId : R.string.quick_settings_more_settings);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 5e13fd3..2c5dbcd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -283,31 +283,17 @@
             return mContext.getString(R.string.quick_settings_disclosure_named_vpn,
                     vpnName);
         }
+        if (hasWorkProfile && isNetworkLoggingEnabled) {
+            return mContext.getString(
+                    R.string.quick_settings_disclosure_managed_profile_network_activity);
+        }
         if (isProfileOwnerOfOrganizationOwnedDevice) {
-            if (isNetworkLoggingEnabled) {
-                if (organizationName == null) {
-                    return mContext.getString(
-                            R.string.quick_settings_disclosure_management_monitoring);
-                }
-                return mContext.getString(
-                        R.string.quick_settings_disclosure_named_management_monitoring,
-                        organizationName);
-            }
             if (workProfileOrganizationName == null) {
                 return mContext.getString(R.string.quick_settings_disclosure_management);
             }
             return mContext.getString(R.string.quick_settings_disclosure_named_management,
                     workProfileOrganizationName);
         }
-        if (hasWorkProfile && isNetworkLoggingEnabled) {
-            if (workProfileOrganizationName == null) {
-                return mContext.getString(
-                        R.string.quick_settings_disclosure_managed_profile_monitoring);
-            }
-            return mContext.getString(
-                    R.string.quick_settings_disclosure_named_managed_profile_monitoring,
-                    workProfileOrganizationName);
-        }
         return null;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 3e3451e..0a9c12f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -474,22 +474,21 @@
         if (tiles.contains("internet") || tiles.contains("wifi") || tiles.contains("cell")) {
             if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
                 if (!tiles.contains("internet")) {
-                    tiles.add("internet");
-                }
-                if (tiles.contains("wifi")) {
+                    if (tiles.contains("wifi")) {
+                        // Replace the WiFi with Internet, and remove the Cell
+                        tiles.set(tiles.indexOf("wifi"), "internet");
+                        tiles.remove("cell");
+                    } else if (tiles.contains("cell")) {
+                        // Replace the Cell with Internet
+                        tiles.set(tiles.indexOf("cell"), "internet");
+                    }
+                } else {
                     tiles.remove("wifi");
-                }
-                if (tiles.contains("cell")) {
                     tiles.remove("cell");
                 }
             } else {
                 if (tiles.contains("internet")) {
-                    tiles.remove("internet");
-                }
-                if (!tiles.contains("wifi")) {
-                    tiles.add("wifi");
-                }
-                if (!tiles.contains("cell")) {
+                    tiles.set(tiles.indexOf("internet"), "wifi");
                     tiles.add("cell");
                 }
             }
@@ -513,6 +512,14 @@
                 && GarbageMonitor.ADD_MEMORY_TILE_TO_DEFAULT_ON_DEBUGGABLE_BUILDS) {
             tiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);
         }
+        // TODO(b/174753536): Change the config file directly.
+        // Filter out unused tiles from the default QS config.
+        if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+            tiles.remove("cell");
+            tiles.remove("wifi");
+        } else {
+            tiles.remove("internet");
+        }
         return tiles;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 32b41ec..d72f8e9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -115,34 +115,13 @@
 
         final ArrayList<QSTile> tilesToAdd = new ArrayList<>();
         // TODO(b/174753536): Move it into the config file.
-        // Only do the below hacking when at least one of the below tiles exist
-        //   --InternetTile
-        //   --WiFiTile
-        //   --CellularTIle
-        if (possibleTiles.contains("internet") || possibleTiles.contains("wifi")
-                || possibleTiles.contains("cell")) {
-            if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
-                if (!possibleTiles.contains("internet")) {
-                    possibleTiles.add("internet");
-                }
-                if (possibleTiles.contains("wifi")) {
-                    possibleTiles.remove("wifi");
-                }
-                if (possibleTiles.contains("cell")) {
-                    possibleTiles.remove("cell");
-                }
-            } else {
-                if (possibleTiles.contains("internet")) {
-                    possibleTiles.remove("internet");
-                }
-                if (!possibleTiles.contains("wifi")) {
-                    possibleTiles.add("wifi");
-                }
-                if (!possibleTiles.contains("cell")) {
-                    possibleTiles.add("cell");
-                }
-            }
+        if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+            possibleTiles.remove("cell");
+            possibleTiles.remove("wifi");
+        } else {
+            possibleTiles.remove("internet");
         }
+
         for (String spec : possibleTiles) {
             // Only add current and stock tiles that can be created from QSFactoryImpl.
             // Do not include CustomTile. Those will be created by `addPackageTiles`.
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index fea521f..bdb3926 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -241,15 +241,7 @@
         public void run() {
             final float valFloat;
             final boolean inVrMode = mIsVrModeEnabled;
-            if (inVrMode) {
-                valFloat = Settings.System.getFloatForUser(mContext.getContentResolver(),
-                        Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT, mDefaultBacklightForVr,
-                        UserHandle.USER_CURRENT);
-            } else {
-                valFloat = Settings.System.getFloatForUser(mContext.getContentResolver(),
-                        Settings.System.SCREEN_BRIGHTNESS_FLOAT, mDefaultBacklight,
-                        UserHandle.USER_CURRENT);
-            }
+            valFloat = mDisplayManager.getBrightness(mDisplayId);
             // Value is passed as intbits, since this is what the message takes.
             final int valueAsIntBits = Float.floatToIntBits(valFloat);
             mHandler.obtainMessage(MSG_UPDATE_SLIDER, valueAsIntBits,
@@ -364,14 +356,12 @@
             metric = MetricsEvent.ACTION_BRIGHTNESS_FOR_VR;
             minBacklight = mMinimumBacklightForVr;
             maxBacklight = mMaximumBacklightForVr;
-            settingToChange = Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT;
         } else {
             metric = mAutomatic
                     ? MetricsEvent.ACTION_BRIGHTNESS_AUTO
                     : MetricsEvent.ACTION_BRIGHTNESS;
             minBacklight = PowerManager.BRIGHTNESS_MIN;
             maxBacklight = PowerManager.BRIGHTNESS_MAX;
-            settingToChange = Settings.System.SCREEN_BRIGHTNESS_FLOAT;
         }
         final float valFloat = MathUtils.min(convertGammaToLinearFloat(value,
                 minBacklight, maxBacklight),
@@ -386,8 +376,7 @@
         if (!tracking) {
             AsyncTask.execute(new Runnable() {
                     public void run() {
-                        Settings.System.putFloatForUser(mContext.getContentResolver(),
-                                settingToChange, valFloat, UserHandle.USER_CURRENT);
+                        mDisplayManager.setBrightness(mDisplayId, valFloat);
                     }
                 });
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
index 6f80317..05af08e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
@@ -25,10 +25,8 @@
 import android.graphics.PointF
 import android.util.AttributeSet
 import android.view.View
-import kotlin.math.max
 
-private const val RIPPLE_ANIMATION_DURATION: Long = 1500
-private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f
+private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
 
 /**
  * Expanding ripple effect that shows when charging begins.
@@ -39,17 +37,18 @@
     private val defaultColor: Int = 0xffffffff.toInt()
     private val ripplePaint = Paint()
 
+    var radius: Float = 0.0f
+        set(value) { rippleShader.radius = value }
+    var origin: PointF = PointF()
+        set(value) { rippleShader.origin = value }
+    var duration: Long = 1500
+
     init {
         rippleShader.color = defaultColor
         rippleShader.progress = 0f
         rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
         ripplePaint.shader = rippleShader
-    }
-
-    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
-        rippleShader.origin = PointF(measuredWidth / 2f, measuredHeight.toFloat())
-        rippleShader.radius = max(measuredWidth, measuredHeight).toFloat()
-        super.onLayout(changed, left, top, right, bottom)
+        visibility = View.GONE
     }
 
     fun startRipple() {
@@ -57,7 +56,7 @@
             return // Ignore if ripple effect is already playing
         }
         val animator = ValueAnimator.ofFloat(0f, 1f)
-        animator.duration = RIPPLE_ANIMATION_DURATION
+        animator.duration = duration
         animator.addUpdateListener { animator ->
             val now = animator.currentPlayTime
             val phase = now / 30000f
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt
index 5547c1e..d400205 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt
@@ -53,7 +53,7 @@
                   float s = 0.0;
                   for (float i = 0; i < 4; i += 1) {
                     float l = i * 0.25;
-                    float h = l + 0.025;
+                    float h = l + 0.005;
                     float o = abs(sin(0.1 * PI * (t + i)));
                     s += threshold(n + o, l, h);
                   }
@@ -97,7 +97,7 @@
                     float fadeRipple = min(fadeIn, 1.-fadeOutRipple);
                     float rippleAlpha = softRing(p, in_origin, radius, 0.5)
                         * fadeRipple * in_color.a;
-                    vec4 ripple = in_color * max(circle, rippleAlpha) * 0.4;
+                    vec4 ripple = in_color * max(circle, rippleAlpha) * 0.3;
                     return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
                 }"""
         private const val SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
index b567ad4..2900462 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.content.res.Configuration
+import android.graphics.PointF
 import android.util.DisplayMetrics
 import android.view.View
 import android.view.ViewGroupOverlay
@@ -31,6 +32,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import java.io.PrintWriter
+import java.lang.Integer.max
 import javax.inject.Inject
 
 /***
@@ -46,7 +48,7 @@
     private val context: Context,
     private val keyguardStateController: KeyguardStateController
 ) {
-    private var pluggedIn: Boolean? = null
+    private var charging: Boolean? = null
     private val rippleEnabled: Boolean = featureFlags.isChargingRippleEnabled
     @VisibleForTesting
     var rippleView: ChargingRippleView = ChargingRippleView(context, attrs = null)
@@ -55,16 +57,18 @@
         val batteryStateChangeCallback = object : BatteryController.BatteryStateChangeCallback {
             override fun onBatteryLevelChanged(
                 level: Int,
-                nowPluggedIn: Boolean,
-                charging: Boolean
+                pluggedIn: Boolean,
+                nowCharging: Boolean
             ) {
-                if (!rippleEnabled) {
+                // Suppresses the ripple when it's disabled, or when the state change comes
+                // from wireless charging.
+                if (!rippleEnabled || batteryController.isWirelessCharging) {
                     return
                 }
-                val wasPluggedIn = pluggedIn
-                pluggedIn = nowPluggedIn
+                val wasCharging = charging
+                charging = nowCharging
                 // Only triggers when the keyguard is active and the device is just plugged in.
-                if (wasPluggedIn == false && nowPluggedIn && keyguardStateController.isShowing) {
+                if (wasCharging == false && nowCharging && keyguardStateController.isShowing) {
                     rippleView.startRipple()
                 }
             }
@@ -113,10 +117,13 @@
         val width = displayMetrics.widthPixels
         val height = displayMetrics.heightPixels
         if (width != rippleView.width || height != rippleView.height) {
-            rippleView.measure(
-                    View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
-                    View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY))
-            rippleView.layout(0, 0, width, height)
+            rippleView.apply {
+                measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
+                        View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY))
+                layout(0, 0, width, height)
+                origin = PointF(width / 2f, height.toFloat())
+                radius = max(width, height).toFloat()
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index c83b60d..d581c4b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -184,7 +184,6 @@
 import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.CircleReveal;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.KeyboardShortcuts;
@@ -2469,39 +2468,19 @@
 
     protected void showChargingAnimation(int batteryLevel, int transmittingBatteryLevel,
             long animationDelay) {
-        if (mDozing || mKeyguardManager.isKeyguardLocked()) {
-            // on ambient or lockscreen, hide notification panel
-            WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
-                    transmittingBatteryLevel, batteryLevel,
-                    new WirelessChargingAnimation.Callback() {
-                        @Override
-                        public void onAnimationStarting() {
-                            mNotificationShadeWindowController.setRequestTopUi(true, TAG);
-                            CrossFadeHelper.fadeOut(mNotificationPanelViewController.getView(), 1);
-                        }
+        WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
+                transmittingBatteryLevel, batteryLevel,
+                new WirelessChargingAnimation.Callback() {
+                    @Override
+                    public void onAnimationStarting() {
+                        mNotificationShadeWindowController.setRequestTopUi(true, TAG);
+                    }
 
-                        @Override
-                        public void onAnimationEnded() {
-                            CrossFadeHelper.fadeIn(mNotificationPanelViewController.getView());
-                            mNotificationShadeWindowController.setRequestTopUi(false, TAG);
-                        }
-                    }, mDozing).show(animationDelay);
-        } else {
-            // workspace
-            WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
-                    transmittingBatteryLevel, batteryLevel,
-                    new WirelessChargingAnimation.Callback() {
-                        @Override
-                        public void onAnimationStarting() {
-                            mNotificationShadeWindowController.setRequestTopUi(true, TAG);
-                        }
-
-                        @Override
-                        public void onAnimationEnded() {
-                            mNotificationShadeWindowController.setRequestTopUi(false, TAG);
-                        }
-                    }, false).show(animationDelay);
-        }
+                    @Override
+                    public void onAnimationEnded() {
+                        mNotificationShadeWindowController.setRequestTopUi(false, TAG);
+                    }
+                }, false).show(animationDelay);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index 4615877..fd37d89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -321,6 +321,11 @@
         }
 
         @Override
+        public int getDoneText() {
+            return R.string.quick_settings_close_user_panel;
+        }
+
+        @Override
         public boolean onDoneButtonClicked() {
             if (mNotificationPanelViewController != null) {
                 mNotificationPanelViewController.animateCloseQs(true /* animateAway */);
diff --git a/packages/SystemUI/src/com/android/systemui/telephony/TelephonyCallback.java b/packages/SystemUI/src/com/android/systemui/telephony/TelephonyCallback.java
index 3bc2632..95216c5 100644
--- a/packages/SystemUI/src/com/android/systemui/telephony/TelephonyCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/telephony/TelephonyCallback.java
@@ -28,6 +28,12 @@
 
 import javax.inject.Inject;
 
+/**
+ * Class for use by {@link TelephonyListenerManager} to centralize TelephonyManager Callbacks.
+ *
+ * There are more callback interfaces defined in {@link android.telephony.TelephonyCallback} that
+ * are not currently covered. Add them here if they ever become necessary.
+ */
 class TelephonyCallback extends android.telephony.TelephonyCallback
         implements ActiveDataSubscriptionIdListener, CallStateListener, ServiceStateListener {
 
diff --git a/packages/SystemUI/src/com/android/systemui/telephony/TelephonyListenerManager.java b/packages/SystemUI/src/com/android/systemui/telephony/TelephonyListenerManager.java
index 4e1acca..3111930 100644
--- a/packages/SystemUI/src/com/android/systemui/telephony/TelephonyListenerManager.java
+++ b/packages/SystemUI/src/com/android/systemui/telephony/TelephonyListenerManager.java
@@ -47,7 +47,9 @@
     private boolean mListening = false;
 
     @Inject
-    public TelephonyListenerManager(TelephonyManager telephonyManager, @Main Executor executor,
+    public TelephonyListenerManager(
+            TelephonyManager telephonyManager,
+            @Main Executor executor,
             TelephonyCallback telephonyCallback) {
         mTelephonyManager = telephonyManager;
         mExecutor = executor;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 7090e78..d91625e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -17,11 +17,14 @@
 package com.android.systemui.people.widget;
 
 import static android.app.Notification.CATEGORY_MISSED_CALL;
+import static android.app.Notification.EXTRA_PEOPLE_LIST;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
 import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
 import static android.app.people.ConversationStatus.ACTIVITY_GAME;
+import static android.content.PermissionChecker.PERMISSION_GRANTED;
+import static android.content.PermissionChecker.PERMISSION_HARD_DENIED;
 
 import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING;
 import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID;
@@ -55,6 +58,7 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
 import android.content.pm.ShortcutInfo;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
@@ -86,6 +90,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
@@ -106,6 +111,8 @@
     private static final int SECOND_WIDGET_ID_WITH_SHORTCUT = 3;
     private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2;
     private static final int WIDGET_ID_WITH_KEY_IN_OPTIONS = 4;
+    private static final int WIDGET_ID_WITH_SAME_URI = 5;
+    private static final int WIDGET_ID_WITH_DIFFERENT_URI = 6;
     private static final String SHORTCUT_ID = "101";
     private static final String OTHER_SHORTCUT_ID = "102";
     private static final String NOTIFICATION_KEY = "0|com.android.systemui.tests|0|null|0";
@@ -123,10 +130,21 @@
             new PeopleSpaceTile
                     .Builder(SHORTCUT_ID, "username", ICON, new Intent())
                     .setPackageName(TEST_PACKAGE_A)
-                    .setUserHandle(new UserHandle(1))
+                    .setUserHandle(new UserHandle(0))
                     .setNotificationKey(NOTIFICATION_KEY + "1")
                     .setNotificationContent(NOTIFICATION_CONTENT)
                     .setNotificationDataUri(URI)
+                    .setContactUri(URI)
+                    .build();
+    private static final PeopleSpaceTile PERSON_TILE_WITH_SAME_URI =
+            new PeopleSpaceTile
+                    // Different shortcut ID
+                    .Builder(OTHER_SHORTCUT_ID, "username", ICON, new Intent())
+                    // Different package name
+                    .setPackageName(TEST_PACKAGE_B)
+                    .setUserHandle(new UserHandle(0))
+                    // Same contact uri.
+                    .setContactUri(URI)
                     .build();
     private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext,
             SHORTCUT_ID).setLongLabel("name").build();
@@ -149,6 +167,8 @@
     private LauncherApps mLauncherApps;
     @Mock
     private NotificationEntryManager mNotificationEntryManager;
+    @Mock
+    private PackageManager mPackageManager;
 
     @Captor
     private ArgumentCaptor<NotificationHandler> mListenerCaptor;
@@ -167,7 +187,7 @@
         mDependency.injectTestDependency(NotificationEntryManager.class, mNotificationEntryManager);
         mManager = new PeopleSpaceWidgetManager(mContext);
         mManager.setAppWidgetManager(mAppWidgetManager, mIPeopleManager, mPeopleManager,
-                mLauncherApps, mNotificationEntryManager);
+                mLauncherApps, mNotificationEntryManager, mPackageManager, true);
         mManager.attach(mListenerService);
         mProvider = new PeopleSpaceWidgetProvider();
         mProvider.setPeopleSpaceWidgetManager(mManager);
@@ -177,16 +197,10 @@
         mNoMan.addListener(serviceListener);
 
         clearStorage();
-        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
-
-        Bundle options = new Bundle();
-        options.putParcelable(OPTIONS_PEOPLE_TILE, PERSON_TILE);
-        when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT)))
-                .thenReturn(options);
+        addTileForWidget(PERSON_TILE, WIDGET_ID_WITH_SHORTCUT);
+        addTileForWidget(PERSON_TILE_WITH_SAME_URI, WIDGET_ID_WITH_SAME_URI);
         when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT)))
                 .thenReturn(new Bundle());
-        when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn(
-                getConversationWithShortcutId(SHORTCUT_ID));
     }
 
     @Test
@@ -477,7 +491,7 @@
         addSecondWidgetForPersonTile();
 
         PeopleSpaceUtils.removeSharedPreferencesStorageForTile(
-                mContext, KEY, SECOND_WIDGET_ID_WITH_SHORTCUT);
+                mContext, KEY, SECOND_WIDGET_ID_WITH_SHORTCUT, EMPTY_STRING);
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(createNotification(
                         SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
@@ -501,7 +515,6 @@
             throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
 
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(createNotification(
@@ -527,7 +540,6 @@
             throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
 
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(createNotification(
@@ -548,10 +560,267 @@
     }
 
     @Test
+    public void testUpdateMissedCallNotificationWithContentPostedIfMatchingUriTile()
+            throws Exception {
+        int[] widgetIdsArray =
+                {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_SAME_URI};
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(createNotification(
+                        SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ true))
+                .setId(1));
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                        mBundleArgumentCaptor.capture());
+        Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue());
+        PeopleSpaceTile tileWithMissedCallOrigin = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
+        assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+        assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(
+                NOTIFICATION_CONTENT);
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SAME_URI),
+                        mBundleArgumentCaptor.capture());
+        Bundle bundleForSameUriTile = requireNonNull(mBundleArgumentCaptor.getValue());
+        PeopleSpaceTile tileWithSameUri = bundleForSameUriTile.getParcelable(OPTIONS_PEOPLE_TILE);
+        assertThat(tileWithSameUri.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+        assertThat(tileWithSameUri.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SAME_URI),
+                any());
+    }
+
+    @Test
+    public void testRemoveMissedCallNotificationWithContentPostedIfMatchingUriTile()
+            throws Exception {
+        int[] widgetIdsArray =
+                {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_SAME_URI};
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(createNotification(
+                        SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ true))
+                .setId(1));
+        mClock.advanceTime(MIN_LINGER_DURATION);
+        NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn.cloneLight(), 0);
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, times(2)).updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                mBundleArgumentCaptor.capture());
+        Bundle bundle = mBundleArgumentCaptor.getValue();
+        PeopleSpaceTile tileWithMissedCallOrigin = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
+        assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(null);
+        assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(null);
+        verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+        verify(mAppWidgetManager, times(2))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SAME_URI),
+                        mBundleArgumentCaptor.capture());
+        Bundle bundleForSameUriTile = requireNonNull(mBundleArgumentCaptor.getValue());
+        PeopleSpaceTile tileWithSameUri = bundleForSameUriTile.getParcelable(OPTIONS_PEOPLE_TILE);
+        assertThat(tileWithSameUri.getNotificationKey()).isEqualTo(null);
+        assertThat(tileWithSameUri.getNotificationContent()).isEqualTo(null);
+        verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SAME_URI),
+                any());
+    }
+
+    @Test
+    public void testDoNotRemoveMissedCallIfMatchingUriTileMissingReadContactsPermissionWhenPosted()
+            throws Exception {
+        when(mPackageManager.checkPermission(any(),
+                eq(PERSON_TILE_WITH_SAME_URI.getPackageName()))).thenReturn(
+                PERMISSION_HARD_DENIED);
+        int[] widgetIdsArray =
+                {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_SAME_URI};
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(createNotification(
+                        SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ true))
+                .setId(1));
+        mClock.advanceTime(MIN_LINGER_DURATION);
+        // We should only try to remove the notification if the Missed Call was added when posted.
+        NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn.cloneLight(), 0);
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, times(2)).updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                mBundleArgumentCaptor.capture());
+        Bundle bundle = mBundleArgumentCaptor.getValue();
+        PeopleSpaceTile tileWithMissedCallOrigin = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
+        assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(null);
+        assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(null);
+        verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+        verify(mAppWidgetManager, times(0))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SAME_URI), any());
+        verify(mAppWidgetManager, times(0)).updateAppWidget(eq(WIDGET_ID_WITH_SAME_URI),
+                any());
+    }
+
+    @Test
+    public void testUpdateMissedCallNotificationWithContentPostedIfMatchingUriTileFromSender()
+            throws Exception {
+        int[] widgetIdsArray =
+                {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_SAME_URI};
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        Notification notificationWithPersonOnlyInSender =
+                createMessagingStyleNotificationWithoutExtras(
+                        SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */
+                        true).build();
+        StatusBarNotification sbn = new SbnBuilder()
+                .setNotification(notificationWithPersonOnlyInSender)
+                .setPkg(TEST_PACKAGE_A)
+                .setUid(0)
+                .setUser(new UserHandle(0))
+                .build();
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(sbn)
+                .setId(1));
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                        mBundleArgumentCaptor.capture());
+        Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue());
+        PeopleSpaceTile tileWithMissedCallOrigin = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
+        assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+        assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(
+                NOTIFICATION_CONTENT);
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SAME_URI),
+                        mBundleArgumentCaptor.capture());
+        Bundle bundleForSameUriTile = requireNonNull(mBundleArgumentCaptor.getValue());
+        PeopleSpaceTile tileWithSameUri = bundleForSameUriTile.getParcelable(OPTIONS_PEOPLE_TILE);
+        assertThat(tileWithSameUri.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+        assertThat(tileWithSameUri.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SAME_URI),
+                any());
+    }
+
+    @Test
+    public void testDoNotUpdateMissedCallNotificationWithContentPostedIfNoPersonsAttached()
+            throws Exception {
+        int[] widgetIdsArray =
+                {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_SAME_URI};
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        // Notification posted without any Person attached.
+        Notification notificationWithoutPersonObject =
+                createMessagingStyleNotificationWithoutExtras(
+                        SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */
+                        true).setStyle(new Notification.MessagingStyle("sender")
+                        .addMessage(
+                                new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10,
+                                        "sender"))
+                ).build();
+        StatusBarNotification sbn = new SbnBuilder()
+                .setNotification(notificationWithoutPersonObject)
+                .setPkg(TEST_PACKAGE_A)
+                .setUid(0)
+                .setUser(new UserHandle(0))
+                .build();
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(sbn)
+                .setId(1));
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                        mBundleArgumentCaptor.capture());
+        Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue());
+        PeopleSpaceTile tileWithMissedCallOrigin = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
+        assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+        assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(
+                NOTIFICATION_CONTENT);
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+        // Do not update since notification doesn't include a Person reference.
+        verify(mAppWidgetManager, times(0))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SAME_URI),
+                        any());
+        verify(mAppWidgetManager, times(0)).updateAppWidget(eq(WIDGET_ID_WITH_SAME_URI),
+                any());
+    }
+
+    @Test
+    public void testDoNotUpdateMissedCallNotificationWithContentPostedIfNotMatchingUriTile()
+            throws Exception {
+        clearStorage();
+        addTileForWidget(PERSON_TILE, WIDGET_ID_WITH_SHORTCUT);
+        addTileForWidget(PERSON_TILE_WITH_SAME_URI.toBuilder().setContactUri(
+                Uri.parse("different_uri")).build(), WIDGET_ID_WITH_DIFFERENT_URI);
+        int[] widgetIdsArray =
+                {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_DIFFERENT_URI};
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(createNotification(
+                        SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ true))
+                .setId(1));
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                        mBundleArgumentCaptor.capture());
+        Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue());
+        PeopleSpaceTile tileWithMissedCallOrigin = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
+        assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+        assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(
+                NOTIFICATION_CONTENT);
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+        // Do not update since missing permission to read contacts.
+        verify(mAppWidgetManager, times(0))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_DIFFERENT_URI),
+                        any());
+        verify(mAppWidgetManager, times(0)).updateAppWidget(eq(WIDGET_ID_WITH_DIFFERENT_URI),
+                any());
+    }
+
+    @Test
+    public void testDoNotUpdateMissedCallIfMatchingUriTileMissingReadContactsPermission()
+            throws Exception {
+        when(mPackageManager.checkPermission(any(),
+                eq(PERSON_TILE_WITH_SAME_URI.getPackageName()))).thenReturn(
+                PERMISSION_HARD_DENIED);
+        int[] widgetIdsArray =
+                {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_SAME_URI};
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(createNotification(
+                        SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ true))
+                .setId(1));
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                        mBundleArgumentCaptor.capture());
+        Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue());
+        PeopleSpaceTile tileWithMissedCallOrigin = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
+        assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+        assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(
+                NOTIFICATION_CONTENT);
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+        // Do not update since missing permission to read contacts.
+        verify(mAppWidgetManager, times(0))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SAME_URI),
+                        any());
+        verify(mAppWidgetManager, times(0)).updateAppWidget(eq(WIDGET_ID_WITH_SAME_URI),
+                any());
+    }
+
+    @Test
     public void testUpdateNotificationRemovedIfExistingTile() throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
 
         StatusBarNotification sbn = createNotification(
                 SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
@@ -574,7 +843,8 @@
     }
 
     @Test
-    public void testDeleteAllWidgetsForConversationsUncachesShortcutAndRemovesListeners() {
+    public void testDeleteAllWidgetsForConversationsUncachesShortcutAndRemovesListeners()
+            throws Exception {
         addSecondWidgetForPersonTile();
         mProvider.onUpdate(mContext, mAppWidgetManager,
                 new int[]{WIDGET_ID_WITH_SHORTCUT, SECOND_WIDGET_ID_WITH_SHORTCUT});
@@ -746,19 +1016,26 @@
      * Adds another widget for {@code PERSON_TILE} with widget ID: {@code
      * SECOND_WIDGET_ID_WITH_SHORTCUT}.
      */
-    private void addSecondWidgetForPersonTile() {
-        Bundle options = new Bundle();
-        options.putParcelable(OPTIONS_PEOPLE_TILE, PERSON_TILE);
-        when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
-                .thenReturn(options);
+    private void addSecondWidgetForPersonTile() throws Exception {
         // Set the same Person associated on another People Tile widget ID.
-        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
-        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
+        addTileForWidget(PERSON_TILE, SECOND_WIDGET_ID_WITH_SHORTCUT);
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
                 SECOND_WIDGET_ID_WITH_SHORTCUT};
         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
     }
 
+    private void addTileForWidget(PeopleSpaceTile tile, int widgetId) throws Exception {
+        setStorageForTile(tile.getId(), tile.getPackageName(), widgetId, tile.getContactUri());
+        Bundle options = new Bundle();
+        options.putParcelable(OPTIONS_PEOPLE_TILE, tile);
+        when(mAppWidgetManager.getAppWidgetOptions(eq(widgetId)))
+                .thenReturn(options);
+        when(mIPeopleManager.getConversation(tile.getPackageName(), 0, tile.getId())).thenReturn(
+                getConversationWithShortcutId(tile.getId()));
+        when(mPackageManager.checkPermission(any(), eq(tile.getPackageName()))).thenReturn(
+                PERMISSION_GRANTED);
+    }
+
     /**
      * Returns a single conversation associated with {@code shortcutId}.
      */
@@ -772,7 +1049,7 @@
     private ConversationChannel getConversationWithShortcutId(String shortcutId,
             List<ConversationStatus> statuses) throws Exception {
         ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
-                "name").build();
+                "name").setPerson(PERSON).build();
         ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
                 0L, false, false, statuses);
         return convo;
@@ -780,6 +1057,30 @@
 
     private Notification createMessagingStyleNotification(String shortcutId,
             boolean isMessagingStyle, boolean isMissedCall) {
+        Bundle extras = new Bundle();
+        ArrayList<Person> person = new ArrayList<Person>();
+        person.add(PERSON);
+        extras.putParcelableArrayList(EXTRA_PEOPLE_LIST, person);
+        Notification.Builder builder = new Notification.Builder(mContext)
+                .setContentTitle("TEST_TITLE")
+                .setContentText("TEST_TEXT")
+                .setExtras(extras)
+                .setShortcutId(shortcutId);
+        if (isMessagingStyle) {
+            builder.setStyle(new Notification.MessagingStyle(PERSON)
+                    .addMessage(
+                            new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10,
+                                    PERSON))
+            );
+        }
+        if (isMissedCall) {
+            builder.setCategory(CATEGORY_MISSED_CALL);
+        }
+        return builder.build();
+    }
+
+    private Notification.Builder createMessagingStyleNotificationWithoutExtras(String shortcutId,
+            boolean isMessagingStyle, boolean isMissedCall) {
         Notification.Builder builder = new Notification.Builder(mContext)
                 .setContentTitle("TEST_TITLE")
                 .setContentText("TEST_TEXT")
@@ -794,9 +1095,10 @@
         if (isMissedCall) {
             builder.setCategory(CATEGORY_MISSED_CALL);
         }
-        return builder.build();
+        return builder;
     }
 
+
     private StatusBarNotification createNotification(String shortcutId,
             boolean isMessagingStyle, boolean isMissedCall) {
         Notification notification = createMessagingStyleNotification(
@@ -804,6 +1106,8 @@
         return new SbnBuilder()
                 .setNotification(notification)
                 .setPkg(TEST_PACKAGE_A)
+                .setUid(0)
+                .setUser(new UserHandle(0))
                 .build();
     }
 
@@ -824,11 +1128,16 @@
                 String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS),
                 Context.MODE_PRIVATE);
         widgetSp4.edit().clear().commit();
+        SharedPreferences widgetSp5 = mContext.getSharedPreferences(
+                String.valueOf(WIDGET_ID_WITH_SAME_URI),
+                Context.MODE_PRIVATE);
+        widgetSp5.edit().clear().commit();
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
         sp.edit().clear().commit();
     }
 
-    private void setStorageForTile(String shortcutId, String packageName, int widgetId) {
+    private void setStorageForTile(String shortcutId, String packageName, int widgetId,
+            Uri contactUri) {
         SharedPreferences widgetSp = mContext.getSharedPreferences(
                 String.valueOf(widgetId),
                 Context.MODE_PRIVATE);
@@ -840,11 +1149,17 @@
 
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
         SharedPreferences.Editor editor = sp.edit();
-        editor.putString(String.valueOf(widgetId), shortcutId);
+        editor.putString(String.valueOf(widgetId), contactUri.toString());
+
         String key = new PeopleTileKey(shortcutId, 0, packageName).toString();
         Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
         storedWidgetIds.add(String.valueOf(widgetId));
         editor.putStringSet(key, storedWidgetIds);
+
+        Set<String> storedWidgetIdsByUri = new HashSet<>(
+                sp.getStringSet(contactUri.toString(), new HashSet<>()));
+        storedWidgetIdsByUri.add(String.valueOf(widgetId));
+        editor.putStringSet(contactUri.toString(), storedWidgetIdsByUri);
         editor.apply();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
index 3701b91..9ce7241 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
@@ -93,8 +93,8 @@
 
         captor.value.onBatteryLevelChanged(
                 unusedBatteryLevel,
-                true /* plugged in */,
-                false /* charging */)
+                false /* plugged in */,
+                true /* charging */)
         verify(rippleView).startRipple()
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyCallbackTest.java
index 463b336..ac15903 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyCallbackTest.java
@@ -27,7 +27,6 @@
 
 import com.android.systemui.SysuiTestCase;
 
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -36,7 +35,7 @@
 public class TelephonyCallbackTest extends SysuiTestCase {
 
     private TelephonyCallback mTelephonyCallback = new TelephonyCallback();
-    
+
     @Test
     public void testAddListener_ActiveDataSubscriptionIdListener() {
         assertThat(mTelephonyCallback.hasAnyListeners()).isFalse();
diff --git a/packages/VpnDialogs/res/values/strings.xml b/packages/VpnDialogs/res/values/strings.xml
index 443a9bc..f971a09 100644
--- a/packages/VpnDialogs/res/values/strings.xml
+++ b/packages/VpnDialogs/res/values/strings.xml
@@ -28,6 +28,17 @@
         ]]> appears at the top of your screen when VPN is active.
     </string>
 
+    <!-- TV specific dialog message to warn about the risk of using a VPN application. [CHAR LIMIT=NONE] -->
+    <string name="warning" product="tv">
+      <xliff:g id="app">%s</xliff:g> wants to set up a VPN connection
+        that allows it to monitor network traffic. Only accept if you trust the source.
+        <![CDATA[
+        <br />
+        <br />
+        <img src="vpn_icon" />
+        ]]> appears on your screen when VPN is active.
+    </string>
+
     <!-- Dialog title for built-in VPN. [CHAR LIMIT=40]  -->
     <string name="legacy_title">VPN is connected</string>
     <!-- Label for the name of the current VPN session. [CHAR LIMIT=20] -->
diff --git a/services/Android.bp b/services/Android.bp
index f6bb157..311e8735 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -153,7 +153,7 @@
         " --hide DeprecationMismatch" +
         " --hide HiddenTypedefConstant",
     visibility: ["//visibility:private"],
-    filter_packages: ["com.android."]
+    filter_packages: ["com.android."],
 }
 
 droidstubs {
@@ -168,7 +168,7 @@
         last_released: {
             api_file: ":android.api.system-server.latest",
             removed_api_file: ":removed.api.system-server.latest",
-            baseline_file: ":android-incompatibilities.api.system-server.latest"
+            baseline_file: ":android-incompatibilities.api.system-server.latest",
         },
         api_lint: {
             enabled: true,
@@ -178,18 +178,24 @@
     },
     dists: [
         {
-            targets: ["sdk", "win_sdk"],
+            targets: [
+                "sdk",
+                "win_sdk",
+            ],
             dir: "apistubs/android/system-server/api",
             dest: "android.txt",
-            tag: ".api.txt"
+            tag: ".api.txt",
         },
         {
-            targets: ["sdk", "win_sdk"],
+            targets: [
+                "sdk",
+                "win_sdk",
+            ],
             dir: "apistubs/android/system-server/api",
             dest: "removed.txt",
             tag: ".removed-api.txt",
         },
-    ]
+    ],
 }
 
 java_library {
@@ -223,16 +229,22 @@
     },
     dists: [
         {
-            targets: ["sdk", "win_sdk"],
+            targets: [
+                "sdk",
+                "win_sdk",
+            ],
             dir: "apistubs/android/system-server/api",
             dest: "android-non-updatable.txt",
-            tag: ".api.txt"
+            tag: ".api.txt",
         },
         {
-            targets: ["sdk", "win_sdk"],
+            targets: [
+                "sdk",
+                "win_sdk",
+            ],
             dir: "apistubs/android/system-server/api",
             dest: "android-non-updatable-removed.txt",
             tag: ".removed-api.txt",
         },
-    ]
-}
\ No newline at end of file
+    ],
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java
index d2c1bc1..1fdd908 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java
@@ -108,7 +108,7 @@
                 SystemNotificationChannels.ACCESSIBILITY_MAGNIFICATION);
         final String message = mContext.getString(R.string.window_magnification_prompt_content);
 
-        notificationBuilder.setSmallIcon(R.drawable.ic_settings_24dp)
+        notificationBuilder.setSmallIcon(R.drawable.ic_accessibility_24dp)
                 .setContentTitle(mContext.getString(R.string.window_magnification_prompt_title))
                 .setContentText(message)
                 .setLargeIcon(Icon.createWithResource(mContext,
@@ -118,7 +118,7 @@
                 .setStyle(new Notification.BigTextStyle().bigText(message))
                 .setDeleteIntent(createPendingIntent(ACTION_DISMISS))
                 .setContentIntent(createPendingIntent(ACTION_TURN_ON_IN_SETTINGS))
-                .setActions(buildTurnOnAction(), buildDismissAction());
+                .setActions(buildTurnOnAction());
         mNotificationManager.notify(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE,
                 notificationBuilder.build());
         registerReceiverIfNeeded();
@@ -145,11 +145,6 @@
                 createPendingIntent(ACTION_TURN_ON_IN_SETTINGS)).build();
     }
 
-    private Notification.Action buildDismissAction() {
-        return new Notification.Action.Builder(null, mContext.getString(R.string.dismiss_action),
-                createPendingIntent(ACTION_DISMISS)).build();
-    }
-
     private PendingIntent createPendingIntent(String action) {
         final Intent intent = new Intent(action);
         intent.setPackage(mContext.getPackageName());
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index b160d78..483f67a 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -639,6 +639,16 @@
             }));
         }
 
+        @Override
+        public boolean createAssociation(String packageName, String macAddress, int userId) {
+            getContext().enforceCallingOrSelfPermission(
+                    android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES, "createAssociation");
+
+            addAssociation(new Association(
+                    userId, macAddress, packageName, null, false, System.currentTimeMillis()));
+            return true;
+        }
+
         private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
             checkCallerIsSystemOr(callingPackage);
             int userId = getCallingUserId();
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 8f56842..8bb9ce9 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3716,6 +3716,7 @@
             mDnsManager.removeNetwork(nai.network);
         }
         mNetIdManager.releaseNetId(nai.network.getNetId());
+        nai.onNetworkDisconnected();
     }
 
     private boolean createNativeNetwork(@NonNull NetworkAgentInfo networkAgent) {
@@ -8126,6 +8127,7 @@
                 updateCapabilitiesForNetwork(networkAgent);
             }
             networkAgent.created = true;
+            networkAgent.onNetworkCreated();
         }
 
         if (!networkAgent.everConnected && state == NetworkInfo.State.CONNECTED) {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 411fc67..3ea4458 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -366,11 +366,13 @@
                 || events.contains(TelephonyCallback.EVENT_BARRING_INFO_CHANGED);
     }
 
-    private boolean isPhoneStatePermissionRequired(Set<Integer> events) {
+    private boolean isPhoneStatePermissionRequired(Set<Integer> events, int targetSdk) {
         return events.contains(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
                 || events.contains(TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)
                 || events.contains(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)
-                || events.contains(TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
+                || events.contains(TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)
+                || (targetSdk <= android.os.Build.VERSION_CODES.R ? events.contains(
+                TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED) : false);
     }
 
     private boolean isPrecisePhoneStatePermissionRequired(Set<Integer> events) {
@@ -882,12 +884,12 @@
             remove(callback.asBinder());
             return;
         }
-
+        int callerTargetSdk = TelephonyPermissions.getTargetSdk(mContext, callingPackage);
         // Checks permission and throws SecurityException for disallowed operations. For pre-M
         // apps whose runtime permission has been revoked, we return immediately to skip sending
         // events to the app without crashing it.
         if (!checkListenerPermission(events, subId, callingPackage, callingFeatureId,
-                "listen")) {
+                "listen", callerTargetSdk)) {
             return;
         }
 
@@ -920,7 +922,7 @@
             }
             r.phoneId = phoneId;
             r.eventList = events;
-            r.targetSdk = TelephonyPermissions.getTargetSdk(mContext, callingPackage);
+            r.targetSdk = callerTargetSdk;
 
             if (DBG) {
                 log("listen:  Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId);
@@ -1180,7 +1182,9 @@
                         TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)) {
                     try {
                         r.callback.onPhysicalChannelConfigChanged(
-                                mPhysicalChannelConfigs);
+                                shouldSanitizeLocationForPhysicalChannelConfig(r)
+                                        ? getLocationSanitizedConfigs(mPhysicalChannelConfigs)
+                                        : mPhysicalChannelConfigs);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
@@ -2423,8 +2427,10 @@
             return;
         }
 
+        List<PhysicalChannelConfig> sanitizedConfigs = getLocationSanitizedConfigs(configs);
         if (VDBG) {
-            log("notifyPhysicalChannelConfig: subId=" + subId + " configs=" + configs);
+            log("notifyPhysicalChannelConfig: subId=" + subId + " configs=" + configs
+                    + " sanitizedConfigs=" + sanitizedConfigs);
         }
 
         synchronized (mRecords) {
@@ -2437,11 +2443,14 @@
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             if (DBG_LOC) {
-                                log("notifyPhysicalChannelConfig: "
-                                        + "mPhysicalChannelConfigs="
-                                        + configs + " r=" + r);
+                                log("notifyPhysicalChannelConfig: mPhysicalChannelConfigs="
+                                        + (shouldSanitizeLocationForPhysicalChannelConfig(r)
+                                                ? sanitizedConfigs : configs)
+                                        + " r=" + r);
                             }
-                            r.callback.onPhysicalChannelConfigChanged(configs);
+                            r.callback.onPhysicalChannelConfigChanged(
+                                    shouldSanitizeLocationForPhysicalChannelConfig(r)
+                                            ? sanitizedConfigs : configs);
                         } catch (RemoteException ex) {
                             mRemoveList.add(r.binder);
                         }
@@ -2452,6 +2461,25 @@
         }
     }
 
+    private static boolean shouldSanitizeLocationForPhysicalChannelConfig(Record record) {
+        // Always redact location info from PhysicalChannelConfig if the registrant is from neither
+        // PHONE nor SYSTEM process. There is no user case that the registrant needs the location
+        // info (e.g. physicalCellId). This also remove the need for the location permissions check.
+        return record.callerUid != Process.PHONE_UID && record.callerUid != Process.SYSTEM_UID;
+    }
+
+    /**
+     * Return a copy of the PhysicalChannelConfig list but with location info removed.
+     */
+    private static List<PhysicalChannelConfig> getLocationSanitizedConfigs(
+            List<PhysicalChannelConfig> configs) {
+        List<PhysicalChannelConfig> sanitizedConfigs = new ArrayList<>(configs.size());
+        for (PhysicalChannelConfig config : configs) {
+            sanitizedConfigs.add(config.createLocationInfoSanitizedCopy());
+        }
+        return sanitizedConfigs;
+    }
+
     /**
      * Notify that the data enabled has changed.
      *
@@ -2876,7 +2904,7 @@
     }
 
     private boolean checkListenerPermission(Set<Integer> events, int subId, String callingPackage,
-                                            @Nullable String callingFeatureId, String message) {
+            @Nullable String callingFeatureId, String message, int targetSdk) {
         LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder =
                 new LocationAccessPolicy.LocationPermissionQuery.Builder()
                         .setCallingPackage(callingPackage)
@@ -2912,7 +2940,7 @@
             }
         }
 
-        if (isPhoneStatePermissionRequired(events)) {
+        if (isPhoneStatePermissionRequired(events, targetSdk)) {
             if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
                     mContext, subId, callingPackage, callingFeatureId, message)) {
                 isPermissionCheckSuccessful = false;
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index cfa110e..cd2d6d4 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -29,7 +29,10 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.net.LinkProperties;
 import android.net.NetworkCapabilities;
@@ -158,6 +161,7 @@
     @NonNull private final TelephonySubscriptionTrackerCallback mTelephonySubscriptionTrackerCb;
     @NonNull private final TelephonySubscriptionTracker mTelephonySubscriptionTracker;
     @NonNull private final VcnContext mVcnContext;
+    @NonNull private final BroadcastReceiver mPkgChangeReceiver;
 
     /** Can only be assigned when {@link #systemReady()} is called, since it uses AppOpsManager. */
     @Nullable private LocationPermissionChecker mLocationPermissionChecker;
@@ -203,6 +207,29 @@
         mConfigDiskRwHelper = mDeps.newPersistableBundleLockingReadWriteHelper(VCN_CONFIG_FILE);
         mVcnContext = mDeps.newVcnContext(mContext, mLooper, mNetworkProvider);
 
+        mPkgChangeReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final String action = intent.getAction();
+
+                if (Intent.ACTION_PACKAGE_ADDED.equals(action)
+                        || Intent.ACTION_PACKAGE_REPLACED.equals(action)
+                        || Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                    mTelephonySubscriptionTracker.handleSubscriptionsChanged();
+                } else {
+                    Log.wtf(TAG, "received unexpected intent: " + action);
+                }
+            }
+        };
+
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        intentFilter.addDataScheme("package");
+        mContext.registerReceiver(
+                mPkgChangeReceiver, intentFilter, null /* broadcastPermission */, mHandler);
+
         // Run on handler to ensure I/O does not block system server startup
         mHandler.post(() -> {
             PersistableBundle configBundle = null;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 98931a5..c6405e0 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -18,7 +18,6 @@
 
 import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
 import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
-import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
 import static android.app.ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
 import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
@@ -49,6 +48,7 @@
 import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
 import static android.os.PowerWhitelistManager.getReasonCodeFromProcState;
 import static android.os.PowerWhitelistManager.reasonCodeToString;
+import static android.os.Process.INVALID_UID;
 import static android.os.Process.NFC_UID;
 import static android.os.Process.ROOT_UID;
 import static android.os.Process.SHELL_UID;
@@ -660,7 +660,7 @@
         }
 
         ServiceRecord r = res.record;
-        setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, r,
+        setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, r, userId,
                 allowBackgroundActivityStarts);
 
         if (!mAm.mUserController.exists(r.userId)) {
@@ -693,19 +693,7 @@
                         + r.shortInstanceName;
                 Slog.w(TAG, msg);
                 showFgsBgRestrictedNotificationLocked(r);
-                ApplicationInfo aInfo = null;
-                try {
-                    aInfo = AppGlobals.getPackageManager().getApplicationInfo(
-                            callingPackage, ActivityManagerService.STOCK_PM_FLAGS,
-                            userId);
-                } catch (android.os.RemoteException e) {
-                    // pm is in same process, this will never happen.
-                }
-                if (aInfo == null) {
-                    throw new SecurityException("startServiceLocked failed, "
-                            + "could not resolve client package " + callingPackage);
-                }
-                if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, aInfo.uid)) {
+                if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, callingUid)) {
                     throw new ForegroundServiceStartNotAllowedException(msg);
                 }
                 return null;
@@ -839,7 +827,7 @@
         boolean addToStarting = false;
         if (!callerFg && !fgRequired && r.app == null
                 && mAm.mUserController.hasStartedUserState(r.userId)) {
-            ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
+            ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid);
             if (proc == null || proc.mState.getCurProcState() > PROCESS_STATE_RECEIVER) {
                 // If this is not coming from a foreground caller, then we may want
                 // to delay the start if there are already other background services
@@ -1808,7 +1796,7 @@
                             final long delayMs = SystemClock.elapsedRealtime() - r.createRealTime;
                             if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) {
                                 setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
-                                        r.appInfo.uid, r.intent.getIntent(), r, false);
+                                        r.appInfo.uid, r.intent.getIntent(), r, r.userId,false);
                                 final String temp = "startForegroundDelayMs:" + delayMs;
                                 if (r.mInfoAllowStartForeground != null) {
                                     r.mInfoAllowStartForeground += "; " + temp;
@@ -1825,7 +1813,7 @@
                                 r.mLastSetFgsRestrictionTime;
                         if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) {
                             setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
-                                    r.appInfo.uid, r.intent.getIntent(), r, false);
+                                    r.appInfo.uid, r.intent.getIntent(), r, r.userId,false);
                         }
                     }
                     logFgsBackgroundStart(r);
@@ -2579,7 +2567,8 @@
                     return 0;
                 }
             }
-            setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, false);
+            setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, userId,
+                    false);
 
             if (s.app != null) {
                 ProcessServiceRecord servicePsr = s.app.mServices;
@@ -3451,7 +3440,7 @@
         ProcessRecord app;
 
         if (!isolated) {
-            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
+            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid);
             if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
                         + " app=" + app);
             if (app != null) {
@@ -3499,7 +3488,7 @@
             // TODO (chriswailes): Change the Zygote policy flags based on if the launch-for-service
             //  was initiated from a notification tap or not.
             if ((app = mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
-                        hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated, false)) == null) {
+                        hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated)) == null) {
                 String msg = "Unable to launch app "
                         + r.appInfo.packageName + "/"
                         + r.appInfo.uid + " for service "
@@ -5469,7 +5458,7 @@
      * @return true if allow, false otherwise.
      */
     private void setFgsRestrictionLocked(String callingPackage,
-            int callingPid, int callingUid, Intent intent, ServiceRecord r,
+            int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId,
             boolean allowBackgroundActivityStarts) {
         r.mLastSetFgsRestrictionTime = SystemClock.elapsedRealtime();
         // Check DeviceConfig flag.
@@ -5487,7 +5476,7 @@
             if (r.mAllowStartForeground == REASON_DENIED) {
                 r.mAllowStartForeground = shouldAllowFgsStartForegroundLocked(allowWhileInUse,
                         callingPackage, callingPid, callingUid, intent, r,
-                        allowBackgroundActivityStarts);
+                        userId);
             }
         }
     }
@@ -5630,13 +5619,20 @@
      */
     private @ReasonCode int shouldAllowFgsStartForegroundLocked(
             @ReasonCode int allowWhileInUse, String callingPackage, int callingPid,
-            int callingUid, Intent intent, ServiceRecord r, boolean allowBackgroundActivityStarts) {
+            int callingUid, Intent intent, ServiceRecord r, int userId) {
         FgsStartTempAllowList.TempFgsAllowListEntry tempAllowListReason =
                 r.mInfoTempFgsAllowListReason = mAm.isAllowlistedForFgsStartLOSP(callingUid);
         int ret = shouldAllowFgsStartForegroundLocked(allowWhileInUse, callingPid, callingUid,
                 callingPackage, r);
 
         final int uidState = mAm.getUidStateLocked(callingUid);
+        int callerTargetSdkVersion = INVALID_UID;
+        try {
+            ApplicationInfo ai = mAm.mContext.getPackageManager().getApplicationInfoAsUser(
+                    callingPackage, PackageManager.MATCH_KNOWN_PACKAGES, userId);
+            callerTargetSdkVersion = ai.targetSdkVersion;
+        } catch (PackageManager.NameNotFoundException e) {
+        }
         final String debugInfo =
                 "[callingPackage: " + callingPackage
                         + "; callingUid: " + callingUid
@@ -5652,6 +5648,7 @@
                                         + ",callingUid:" + tempAllowListReason.mCallingUid))
                         + ">"
                         + "; targetSdkVersion:" + r.appInfo.targetSdkVersion
+                        + "; callerTargetSdkVersion:" + callerTargetSdkVersion
                         + "; startForegroundCount:" + r.mStartForegroundCount
                         + "]";
         if (!debugInfo.equals(r.mInfoAllowStartForeground)) {
@@ -5823,7 +5820,12 @@
 
     private boolean isBgFgsRestrictionEnabled(ServiceRecord r) {
         return mAm.mConstants.mFlagFgsStartRestrictionEnabled
-                && CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r.appInfo.uid);
+                // Checking service's targetSdkVersion.
+                && CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r.appInfo.uid)
+                && (!mAm.mConstants.mFgsStartRestrictionCheckCallerTargetSdk
+                    // Checking callingUid's targetSdkVersion.
+                    || CompatChanges.isChangeEnabled(
+                            FGS_BG_START_RESTRICTION_CHANGE_ID, r.mRecentCallingUid));
     }
 
     private void logFgsBackgroundStart(ServiceRecord r) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index f7abf6a..e16f4c9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -172,6 +172,13 @@
             "default_fgs_starts_restriction_enabled";
 
     /**
+     * Default value for mFgsStartRestrictionCheckCallerTargetSdk if not explicitly set in
+     * Settings.Global.
+     */
+    private static final String KEY_DEFAULT_FGS_STARTS_RESTRICTION_CHECK_CALLER_TARGET_SDK =
+            "default_fgs_starts_restriction_check_caller_target_sdk";
+
+    /**
      * Whether FGS notification display is deferred following the transition into
      * the foreground state.  Default behavior is {@code true} unless overridden.
      */
@@ -371,6 +378,13 @@
     // at all.
     volatile boolean mFlagFgsStartRestrictionEnabled = true;
 
+    /**
+     * Indicates whether the foreground service background start restriction is enabled for
+     * caller app that is targeting S+.
+     * This is in addition to check of {@link #mFlagFgsStartRestrictionEnabled} flag.
+     */
+    volatile boolean mFgsStartRestrictionCheckCallerTargetSdk = true;
+
     // Whether we defer FGS notifications a few seconds following their transition to
     // the foreground state.  Applies only to S+ apps; enabled by default.
     volatile boolean mFlagFgsNotificationDeferralEnabled = true;
@@ -554,6 +568,9 @@
                             case KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED:
                                 updateFgsStartsRestriction();
                                 break;
+                            case KEY_DEFAULT_FGS_STARTS_RESTRICTION_CHECK_CALLER_TARGET_SDK:
+                                updateFgsStartsRestrictionCheckCallerTargetSdk();
+                                break;
                             case KEY_DEFERRED_FGS_NOTIFICATIONS_ENABLED:
                                 updateFgsNotificationDeferralEnable();
                                 break;
@@ -829,6 +846,13 @@
                 /*defaultValue*/ true);
     }
 
+    private void updateFgsStartsRestrictionCheckCallerTargetSdk() {
+        mFgsStartRestrictionCheckCallerTargetSdk = DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_DEFAULT_FGS_STARTS_RESTRICTION_CHECK_CALLER_TARGET_SDK,
+                /*defaultValue*/ true);
+    }
+
     private void updateFgsNotificationDeferralEnable() {
         mFlagFgsNotificationDeferralEnabled = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -1090,6 +1114,14 @@
         pw.println(mFgToBgFgsGraceDuration);
         pw.print("  "); pw.print(KEY_FGS_START_FOREGROUND_TIMEOUT); pw.print("=");
         pw.println(mFgsStartForegroundTimeoutMs);
+        pw.print("  "); pw.print(KEY_DEFAULT_BACKGROUND_ACTIVITY_STARTS_ENABLED); pw.print("=");
+        pw.println(mFlagBackgroundActivityStartsEnabled);
+        pw.print("  "); pw.print(KEY_DEFAULT_BACKGROUND_FGS_STARTS_RESTRICTION_ENABLED);
+        pw.print("="); pw.println(mFlagBackgroundFgsStartRestrictionEnabled);
+        pw.print("  "); pw.print(KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED); pw.print("=");
+        pw.println(mFlagFgsStartRestrictionEnabled);
+        pw.print("  "); pw.print(KEY_DEFAULT_FGS_STARTS_RESTRICTION_CHECK_CALLER_TARGET_SDK);
+        pw.print("="); pw.println(mFgsStartRestrictionCheckCallerTargetSdk);
 
         pw.println();
         if (mOverrideMaxCachedProcesses >= 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 321e3b1..2bceb20 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2604,8 +2604,8 @@
     }
 
     @GuardedBy("this")
-    final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean keepIfLarge) {
-        return mProcessList.getProcessRecordLocked(processName, uid, keepIfLarge);
+    final ProcessRecord getProcessRecordLocked(String processName, int uid) {
+        return mProcessList.getProcessRecordLocked(processName, uid);
     }
 
     @GuardedBy(anyOf = {"this", "mProcLock"})
@@ -2639,8 +2639,7 @@
                     false /* knownToBeDead */, 0 /* intentFlags */,
                     sNullHostingRecord  /* hostingRecord */, ZYGOTE_POLICY_FLAG_EMPTY,
                     true /* allowWhileBooting */, true /* isolated */,
-                    uid, true /* keepIfLarge */, abiOverride, entryPoint, entryPointArgs,
-                    crashHandler);
+                    uid, abiOverride, entryPoint, entryPointArgs, crashHandler);
             return proc != null;
         }
     }
@@ -2649,10 +2648,10 @@
     final ProcessRecord startProcessLocked(String processName,
             ApplicationInfo info, boolean knownToBeDead, int intentFlags,
             HostingRecord hostingRecord, int zygotePolicyFlags, boolean allowWhileBooting,
-            boolean isolated, boolean keepIfLarge) {
+            boolean isolated) {
         return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
                 hostingRecord, zygotePolicyFlags, allowWhileBooting, isolated, 0 /* isolatedUid */,
-                keepIfLarge, null /* ABI override */, null /* entryPoint */,
+                null /* ABI override */, null /* entryPoint */,
                 null /* entryPointArgs */, null /* crashHandler */);
     }
 
@@ -3905,7 +3904,7 @@
         // Only the system server can kill an application
         if (callerUid == SYSTEM_UID) {
             synchronized (this) {
-                ProcessRecord app = getProcessRecordLocked(processName, uid, true);
+                ProcessRecord app = getProcessRecordLocked(processName, uid);
                 IApplicationThread thread;
                 if (app != null && (thread = app.getThread()) != null) {
                     try {
@@ -6101,7 +6100,7 @@
         ProcessRecord app;
         if (!isolated) {
             app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
-                    info.uid, true);
+                    info.uid);
         } else {
             app = null;
         }
@@ -11933,7 +11932,7 @@
             ProcessRecord proc = startProcessLocked(app.processName, app,
                     false, 0,
                     new HostingRecord("backup", hostingName),
-                    ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS, false, false, false);
+                    ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS, false, false);
             if (proc == null) {
                 Slog.e(TAG, "Unable to start backup agent process " + r);
                 return false;
@@ -13643,7 +13642,7 @@
             ProcessRecord app;
             synchronized (mProcLock) {
                 if (noRestart) {
-                    app = getProcessRecordLocked(ai.processName, ai.uid, true);
+                    app = getProcessRecordLocked(ai.processName, ai.uid);
                 } else {
                     // Instrumentation can kill and relaunch even persistent processes
                     forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false,
@@ -13686,7 +13685,7 @@
             ApplicationInfo targetInfo) {
         ProcessRecord pr;
         synchronized (this) {
-            pr = getProcessRecordLocked(targetInfo.processName, targetInfo.uid, true);
+            pr = getProcessRecordLocked(targetInfo.processName, targetInfo.uid);
         }
 
         try {
@@ -15433,8 +15432,7 @@
         @Override
         public void killProcess(String processName, int uid, String reason) {
             synchronized (ActivityManagerService.this) {
-                final ProcessRecord proc = getProcessRecordLocked(processName, uid,
-                        true /* keepIfLarge */);
+                final ProcessRecord proc = getProcessRecordLocked(processName, uid);
                 if (proc != null) {
                     mProcessList.removeProcessLocked(proc, false /* callerWillRestart */,
                             true /* allowRestart */,  ApplicationExitInfo.REASON_OTHER, reason);
@@ -15841,7 +15839,7 @@
                     startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
                             new HostingRecord(hostingType, hostingName, isTop),
                             ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE, false /* allowWhileBooting */,
-                            false /* isolated */, true /* keepIfLarge */);
+                            false /* isolated */);
                 }
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index e74c936..60e8d54 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -487,8 +487,10 @@
                 Slog.wtf(TAG, "Error updating external stats: ", e);
             }
 
-            synchronized (BatteryExternalStatsWorker.this) {
-                mLastCollectionTimeStamp = SystemClock.elapsedRealtime();
+            if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) {
+                synchronized (BatteryExternalStatsWorker.this) {
+                    mLastCollectionTimeStamp = SystemClock.elapsedRealtime();
+                }
             }
         }
     };
@@ -658,6 +660,11 @@
                     mStats.updateDisplayMeasuredEnergyStatsLocked(displayChargeUC, screenState,
                             elapsedRealtime);
                 }
+
+                final long gnssChargeUC = measuredEnergyDeltas.gnssChargeUC;
+                if (gnssChargeUC != MeasuredEnergySnapshot.UNAVAILABLE) {
+                    mStats.updateGnssMeasuredEnergyStatsLocked(displayChargeUC, elapsedRealtime);
+                }
             }
             // Inform mStats about each applicable custom energy bucket.
             if (measuredEnergyDeltas != null
@@ -698,7 +705,10 @@
         }
 
         if (modemInfo != null) {
-            mStats.noteModemControllerActivity(modemInfo, elapsedRealtime, uptime);
+            final long mobileRadioChargeUC = measuredEnergyDeltas != null
+                    ? measuredEnergyDeltas.mobileRadioChargeUC : MeasuredEnergySnapshot.UNAVAILABLE;
+            mStats.noteModemControllerActivity(modemInfo, mobileRadioChargeUC, elapsedRealtime,
+                    uptime);
         }
 
         if (updateFlags == UPDATE_ALL) {
@@ -830,6 +840,12 @@
                 case EnergyConsumerType.CPU_CLUSTER:
                     buckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true;
                     break;
+                case EnergyConsumerType.GNSS:
+                    buckets[MeasuredEnergyStats.POWER_BUCKET_GNSS] = true;
+                    break;
+                case EnergyConsumerType.MOBILE_RADIO:
+                    buckets[MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO] = true;
+                    break;
                 case EnergyConsumerType.DISPLAY:
                     buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON] = true;
                     buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_DOZE] = true;
@@ -883,6 +899,9 @@
         if ((flags & UPDATE_DISPLAY) != 0) {
             addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.DISPLAY);
         }
+        if ((flags & UPDATE_RADIO) != 0) {
+            addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.MOBILE_RADIO);
+        }
         if ((flags & UPDATE_WIFI) != 0) {
             addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.WIFI);
         }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index c3f97ad..5937a18 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -674,6 +674,13 @@
     public List<BatteryUsageStats> getBatteryUsageStats(List<BatteryUsageStatsQuery> queries) {
         mContext.enforceCallingPermission(
                 android.Manifest.permission.BATTERY_STATS, null);
+        awaitCompletion();
+
+        if (mBatteryUsageStatsProvider.shouldUpdateStats(queries,
+                mWorker.getLastCollectionTimeStamp())) {
+            syncStats("get-stats", BatteryExternalStatsWorker.UPDATE_ALL);
+        }
+
         return mBatteryUsageStatsProvider.getBatteryUsageStats(queries);
     }
 
@@ -1966,7 +1973,8 @@
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
-                mStats.noteModemControllerActivity(info, elapsedRealtime, uptime);
+                mStats.noteModemControllerActivity(info, POWER_DATA_UNAVAILABLE, elapsedRealtime,
+                        uptime);
             });
         }
     }
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index e79f096..9ecae42 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1544,7 +1544,7 @@
         }
         String targetProcess = info.activityInfo.processName;
         ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
-                info.activityInfo.applicationInfo.uid, false);
+                info.activityInfo.applicationInfo.uid);
 
         if (!skip) {
             final int allowed = mService.getAppStartModeLOSP(
@@ -1678,7 +1678,7 @@
                 r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
                 new HostingRecord("broadcast", r.curComponent), isActivityCapable
                 ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
-                (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false);
+                (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false);
         if (r.curApp == null) {
             // Ah, this recipient is unavailable.  Finish it if necessary,
             // and mark the broadcast record as ready for the next.
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 2c8794d..ee4526b 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -436,7 +436,7 @@
                         // Use existing process if already started
                         checkTime(startTime, "getContentProviderImpl: looking for process record");
                         ProcessRecord proc = mService.getProcessRecordLocked(
-                                cpi.processName, cpr.appInfo.uid, false);
+                                cpi.processName, cpr.appInfo.uid);
                         IApplicationThread thread;
                         if (proc != null && (thread = proc.getThread()) != null
                                 && !proc.isKilled()) {
@@ -459,7 +459,7 @@
                                     new HostingRecord("content provider",
                                         new ComponentName(
                                                 cpi.applicationInfo.packageName, cpi.name)),
-                                    Process.ZYGOTE_POLICY_FLAG_EMPTY, false, false, false);
+                                    Process.ZYGOTE_POLICY_FLAG_EMPTY, false, false);
                             checkTime(startTime, "getContentProviderImpl: after start process");
                             if (proc == null) {
                                 Slog.w(TAG, "Unable to launch app "
diff --git a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
index d789bbb..f9296a0 100644
--- a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
+++ b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
@@ -118,6 +118,12 @@
         /** The chargeUC for {@link EnergyConsumerType#DISPLAY}. */
         public long displayChargeUC = UNAVAILABLE;
 
+        /** The chargeUC for {@link EnergyConsumerType#GNSS}. */
+        public long gnssChargeUC = UNAVAILABLE;
+
+        /** The chargeUC for {@link EnergyConsumerType#MOBILE_RADIO}. */
+        public long mobileRadioChargeUC = UNAVAILABLE;
+
         /** The chargeUC for {@link EnergyConsumerType#WIFI}. */
         public long wifiChargeUC = UNAVAILABLE;
 
@@ -217,6 +223,14 @@
                     output.displayChargeUC = deltaChargeUC;
                     break;
 
+                case EnergyConsumerType.GNSS:
+                    output.gnssChargeUC = deltaChargeUC;
+                    break;
+
+                case EnergyConsumerType.MOBILE_RADIO:
+                    output.mobileRadioChargeUC = deltaChargeUC;
+                    break;
+
                 case EnergyConsumerType.WIFI:
                     output.wifiChargeUC = deltaChargeUC;
                     break;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 51bcde8..2cde423 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -36,7 +36,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESS_OBSERVERS;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -54,7 +53,6 @@
 import static com.android.server.am.ActivityManagerService.TAG_NETWORK;
 import static com.android.server.am.ActivityManagerService.TAG_PROCESSES;
 import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
-import static com.android.server.am.AppProfiler.TAG_PSS;
 
 import android.annotation.NonNull;
 import android.app.ActivityManager;
@@ -1501,8 +1499,7 @@
     }
 
     @GuardedBy("mService")
-    final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean
-            keepIfLarge) {
+    ProcessRecord getProcessRecordLocked(String processName, int uid) {
         if (uid == SYSTEM_UID) {
             // The system gets to run in any process.  If there are multiple
             // processes with the same uid, just pick the first (this
@@ -1519,41 +1516,7 @@
                 return procs.valueAt(i);
             }
         }
-        ProcessRecord proc = mProcessNames.get(processName, uid);
-        if (false && proc != null && !keepIfLarge
-                && proc.mState.getSetProcState() >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
-                && proc.mProfile.getLastCachedPss() >= 4000) {
-            // Turn this condition on to cause killing to happen regularly, for testing.
-            synchronized (mService.mAppProfiler.mProfilerLock) {
-                proc.mProfile.reportCachedKill();
-            }
-            proc.killLocked(Long.toString(proc.mProfile.getLastCachedPss()) + "k from cached",
-                    ApplicationExitInfo.REASON_OTHER,
-                    ApplicationExitInfo.SUBREASON_LARGE_CACHED,
-                    true);
-        } else if (proc != null && !keepIfLarge
-                && !mService.mAppProfiler.isLastMemoryLevelNormal()
-                && proc.mState.getSetProcState() >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
-            final long lastCachedPss;
-            boolean doKilling = false;
-            synchronized (mService.mAppProfiler.mProfilerLock) {
-                lastCachedPss = proc.mProfile.getLastCachedPss();
-                if (lastCachedPss >= getCachedRestoreThresholdKb()) {
-                    proc.mProfile.reportCachedKill();
-                    doKilling = true;
-                }
-            }
-            if (DEBUG_PSS) {
-                Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + lastCachedPss);
-            }
-            if (doKilling) {
-                proc.killLocked(Long.toString(lastCachedPss) + "k from cached",
-                        ApplicationExitInfo.REASON_OTHER,
-                        ApplicationExitInfo.SUBREASON_LARGE_CACHED,
-                        true);
-            }
-        }
-        return proc;
+        return mProcessNames.get(processName, uid);
     }
 
     void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
@@ -1756,12 +1719,13 @@
 
     private boolean enableNativeHeapZeroInit(ProcessRecord app) {
         // Look at the process attribute first.
-        if (app.processInfo != null && app.processInfo.nativeHeapZeroInit != null) {
-            return app.processInfo.nativeHeapZeroInit;
+        if (app.processInfo != null
+                && app.processInfo.nativeHeapZeroInitialized != ApplicationInfo.ZEROINIT_DEFAULT) {
+            return app.processInfo.nativeHeapZeroInitialized == ApplicationInfo.ZEROINIT_ENABLED;
         }
         // Then at the application attribute.
-        if (app.info.isNativeHeapZeroInit() != null) {
-            return app.info.isNativeHeapZeroInit();
+        if (app.info.getNativeHeapZeroInitialized() != ApplicationInfo.ZEROINIT_DEFAULT) {
+            return app.info.getNativeHeapZeroInitialized() == ApplicationInfo.ZEROINIT_ENABLED;
         }
         // Compat feature last.
         if (mPlatformCompat.isChangeEnabled(NATIVE_HEAP_ZERO_INIT, app.info)) {
@@ -2408,12 +2372,11 @@
     ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
             boolean knownToBeDead, int intentFlags, HostingRecord hostingRecord,
             int zygotePolicyFlags, boolean allowWhileBooting, boolean isolated, int isolatedUid,
-            boolean keepIfLarge, String abiOverride, String entryPoint, String[] entryPointArgs,
-            Runnable crashHandler) {
+            String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
         long startTime = SystemClock.uptimeMillis();
         ProcessRecord app;
         if (!isolated) {
-            app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
+            app = getProcessRecordLocked(processName, info.uid);
             checkSlow(startTime, "startProcess: after getProcessRecord");
 
             if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
@@ -2476,12 +2439,8 @@
             ProcessList.killProcessGroup(app.uid, app.getPid());
             checkSlow(startTime, "startProcess: done killing old proc");
 
-            if (!app.isKilled()
-                    || mService.mAppProfiler.isLastMemoryLevelNormal()
-                    || app.mState.getSetProcState() < ActivityManager.PROCESS_STATE_CACHED_EMPTY
-                    || app.mProfile.getLastCachedPss() < getCachedRestoreThresholdKb()) {
-                // Throw a wtf if it's not killed, or killed but not because the system was in
-                // memory pressure + the app was in "cch-empty" and used large amount of memory
+            if (!app.isKilled()) {
+                // Throw a wtf if it's not killed
                 Slog.wtf(TAG_PROCESSES, app.toString() + " is attached to a previous process");
             } else {
                 Slog.w(TAG_PROCESSES, app.toString() + " is attached to a previous process");
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 42e7ff4..cb23b89 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -479,7 +479,7 @@
                 if (procInfo != null && procInfo.deniedPermissions == null
                         && procInfo.gwpAsanMode == ApplicationInfo.GWP_ASAN_DEFAULT
                         && procInfo.memtagMode == ApplicationInfo.MEMTAG_DEFAULT
-                        && procInfo.nativeHeapZeroInit == null) {
+                        && procInfo.nativeHeapZeroInitialized == ApplicationInfo.ZEROINIT_DEFAULT) {
                     // If this process hasn't asked for permissions to be denied, or for a
                     // non-default GwpAsan mode, or any other non-default setting, then we don't
                     // care about it.
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 143a1cf..31183cac 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1641,6 +1641,13 @@
      * PIN or pattern.
      */
     private boolean maybeUnlockUser(final @UserIdInt int userId) {
+        if (mInjector.isFileEncryptedNativeOnly() && mLockPatternUtils.isSecure(userId)) {
+            // A token is needed, so don't bother trying to unlock without one.
+            // This keeps misleading error messages from being logged.
+            Slog.d(TAG, "Not unlocking user " + userId
+                    + "'s CE storage yet because a credential token is needed");
+            return false;
+        }
         // Try unlocking storage using empty token
         return unlockUserCleared(userId, null, null, null);
     }
@@ -3101,5 +3108,11 @@
         protected IStorageManager getStorageManager() {
             return IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
         }
+
+        // This is needed because isFileEncryptedNativeOnly is a static method,
+        // but it needs to be mocked out in tests.
+        protected boolean isFileEncryptedNativeOnly() {
+            return StorageManager.isFileEncryptedNativeOnly();
+        }
     }
 }
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 2ee41ac..57de708 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -16,6 +16,17 @@
 
 package com.android.server.app;
 
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_CHANGED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+
+import static com.android.server.wm.CompatModePackages.DOWNSCALED;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_50;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_60;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_70;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_80;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_90;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
@@ -23,27 +34,41 @@
 import android.app.GameManager;
 import android.app.GameManager.GameMode;
 import android.app.IGameManagerService;
+import android.compat.Compatibility;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.ServiceManager;
 import android.os.ShellCallback;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
 import android.util.ArrayMap;
-import android.util.Log;
+import android.util.KeyValueListParser;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.CompatibilityChangeConfig;
+import com.android.internal.compat.IPlatformCompat;
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
 
 import java.io.FileDescriptor;
+import java.util.HashSet;
+import java.util.List;
 
 /**
  * Service to manage game related features.
@@ -60,14 +85,20 @@
 
     static final int WRITE_SETTINGS = 1;
     static final int REMOVE_SETTINGS = 2;
+    static final int POPULATE_GAME_MODE_SETTINGS = 3;
     static final int WRITE_SETTINGS_DELAY = 10 * 1000;  // 10 seconds
 
     private final Context mContext;
-    private final PackageManager mPackageManager;
     private final Object mLock = new Object();
+    private final Object mDeviceConfigLock = new Object();
     private final Handler mHandler;
+    private final PackageManager mPackageManager;
+    private final IPlatformCompat mPlatformCompat;
+    private DeviceConfigListener mDeviceConfigListener;
     @GuardedBy("mLock")
     private final ArrayMap<Integer, GameManagerSettings> mSettings = new ArrayMap<>();
+    @GuardedBy("mDeviceConfigLock")
+    private final ArrayMap<String, GamePackageConfiguration> mConfigs = new ArrayMap<>();
 
     public GameManagerService(Context context) {
         this(context, createServiceThread().getLooper());
@@ -76,7 +107,9 @@
     GameManagerService(Context context, Looper looper) {
         mContext = context;
         mHandler = new SettingsHandler(looper);
-        mPackageManager = context.getPackageManager();
+        mPackageManager = mContext.getPackageManager();
+        mPlatformCompat = IPlatformCompat.Stub.asInterface(
+                ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
     }
 
     @Override
@@ -143,10 +176,186 @@
                     }
                     break;
                 }
+                case POPULATE_GAME_MODE_SETTINGS: {
+                    removeMessages(POPULATE_GAME_MODE_SETTINGS, msg.obj);
+                    loadDeviceConfigLocked();
+                    break;
+                }
             }
         }
     }
 
+    private class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener {
+
+        DeviceConfigListener() {
+            super();
+            DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_GAME_OVERLAY,
+                    mContext.getMainExecutor(), this);
+        }
+
+        @Override
+        public void onPropertiesChanged(Properties properties) {
+            synchronized (mDeviceConfigLock) {
+                for (String key : properties.getKeyset()) {
+                    try {
+                        // Check if the package is installed before caching it.
+                        final String packageName = keyToPackageName(key);
+                        mPackageManager.getPackageInfo(packageName, 0);
+                        final GamePackageConfiguration config =
+                                GamePackageConfiguration.fromProperties(key, properties);
+                        if (config.isValid()) {
+                            putConfig(config);
+                        } else {
+                            // This means that we received a bad config, or the config was deleted.
+                            Slog.i(TAG, "Removing config for: " + packageName);
+                            mConfigs.remove(packageName);
+                            disableCompatScale(packageName);
+                        }
+                    } catch (PackageManager.NameNotFoundException e) {
+                        if (DEBUG) {
+                            Slog.v(TAG, "Package name not found", e);
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void finalize() {
+            DeviceConfig.removeOnPropertiesChangedListener(this);
+        }
+    }
+
+    private static class GameModeConfiguration {
+        public static final String TAG = "GameManagerService_GameModeConfiguration";
+        public static final String MODE_KEY = "mode";
+        public static final String SCALING_KEY = "downscaleFactor";
+
+        private final @GameMode int mGameMode;
+        private final String mScaling;
+
+        private GameModeConfiguration(@NonNull int gameMode,
+                @NonNull String scaling) {
+            mGameMode = gameMode;
+            mScaling = scaling;
+        }
+
+        public static GameModeConfiguration fromKeyValueListParser(KeyValueListParser parser) {
+            return new GameModeConfiguration(
+                    parser.getInt(MODE_KEY, GameManager.GAME_MODE_UNSUPPORTED),
+                    parser.getString(SCALING_KEY, "1.0")
+            );
+        }
+
+        public int getGameMode() {
+            return mGameMode;
+        }
+
+        public String getScaling() {
+            return mScaling;
+        }
+
+        public boolean isValid() {
+            return (mGameMode == GameManager.GAME_MODE_PERFORMANCE
+                    || mGameMode == GameManager.GAME_MODE_BATTERY) && getCompatChangeId() != 0;
+        }
+
+        public String toString() {
+            return "[Game Mode:" + mGameMode + ",Scaling:" + mScaling + "]";
+        }
+
+        public long getCompatChangeId() {
+            switch (mScaling) {
+                case "0.5":
+                    return DOWNSCALE_50;
+                case "0.6":
+                    return DOWNSCALE_60;
+                case "0.7":
+                    return DOWNSCALE_70;
+                case "0.8":
+                    return DOWNSCALE_80;
+                case "0.9":
+                    return DOWNSCALE_90;
+            }
+            return 0;
+        }
+    }
+
+    private static class GamePackageConfiguration {
+        public static final String TAG = "GameManagerService_GamePackageConfiguration";
+
+        private final String mPackageName;
+        private final ArrayMap<Integer, GameModeConfiguration> mModeConfigs;
+
+        private GamePackageConfiguration(String keyName) {
+            mPackageName = keyToPackageName(keyName);
+            mModeConfigs = new ArrayMap<>();
+        }
+
+        public String getPackageName() {
+            return mPackageName;
+        }
+
+        public @GameMode int[] getAvailableGameModes() {
+            if (mModeConfigs.keySet().size() > 0) {
+                return mModeConfigs.keySet().stream()
+                            .mapToInt(Integer::intValue).toArray();
+            }
+            return new int[]{GameManager.GAME_MODE_UNSUPPORTED};
+        }
+
+        /**
+         * Get a GameModeConfiguration for a given game mode.
+         *
+         * @return The package's GameModeConfiguration for the provided mode or null if absent
+         */
+        public GameModeConfiguration getGameModeConfiguration(@GameMode int gameMode) {
+            return mModeConfigs.get(gameMode);
+        }
+
+        /**
+         * Insert a new GameModeConfiguration
+         */
+        public void addModeConfig(GameModeConfiguration config) {
+            if (config.isValid()) {
+                mModeConfigs.put(config.getGameMode(), config);
+            } else {
+                Slog.w(TAG, "Invalid game mode config for "
+                        + mPackageName + ":" + config.toString());
+            }
+        }
+
+        /**
+         * Create a new instance from a package name and DeviceConfig.Properties instance
+         */
+        public static GamePackageConfiguration fromProperties(String key,
+                Properties properties) {
+            final GamePackageConfiguration packageConfig = new GamePackageConfiguration(key);
+            final String configString = properties.getString(key, "");
+            final String[] gameModeConfigStrings = configString.split(":");
+            for (String gameModeConfigString : gameModeConfigStrings) {
+                try {
+                    final KeyValueListParser parser = new KeyValueListParser(',');
+                    parser.setString(gameModeConfigString);
+                    final GameModeConfiguration config =
+                            GameModeConfiguration.fromKeyValueListParser(parser);
+                    packageConfig.addModeConfig(config);
+                } catch (IllegalArgumentException e) {
+                    Slog.e(TAG, "Invalid config string");
+                }
+            }
+            return packageConfig;
+        }
+
+        public boolean isValid() {
+            return mModeConfigs.size() > 0;
+        }
+
+        public String toString() {
+            return "[Name:" + mPackageName + " Modes: " + mModeConfigs.toString() + "]";
+        }
+    }
+
     /**
      * SystemService lifecycle for GameService.
      *
@@ -163,6 +372,8 @@
         public void onStart() {
             mService = new GameManagerService(getContext());
             publishBinderService(Context.GAME_SERVICE, mService);
+            mService.registerDeviceConfigListener();
+            mService.registerPackageReceiver();
         }
 
         @Override
@@ -200,10 +411,27 @@
         }
     }
 
+    /**
+     * Get an array of game modes available for a given package.
+     * Checks that the caller has {@link android.Manifest.permission#MANAGE_GAME_MODE}.
+     */
+    @Override
+    @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+    public @GameMode int[] getAvailableGameModes(String packageName) throws SecurityException {
+        checkPermission(Manifest.permission.MANAGE_GAME_MODE);
+        synchronized (mDeviceConfigLock) {
+            final GamePackageConfiguration config = mConfigs.get(packageName);
+            if (config == null) {
+                return new int[]{GameManager.GAME_MODE_UNSUPPORTED};
+            }
+            return config.getAvailableGameModes();
+        }
+    }
+
     private @GameMode int getGameModeFromSettings(String packageName, int userId) {
         synchronized (mLock) {
             if (!mSettings.containsKey(userId)) {
-                Log.w(TAG, "User ID '" + userId + "' does not have a Game Mode"
+                Slog.w(TAG, "User ID '" + userId + "' does not have a Game Mode"
                         + " selected for package: '" + packageName + "'");
                 return GameManager.GAME_MODE_UNSUPPORTED;
             }
@@ -229,7 +457,7 @@
             final ApplicationInfo applicationInfo = mPackageManager
                     .getApplicationInfoAsUser(packageName, PackageManager.MATCH_ALL, userId);
             if (applicationInfo.category != ApplicationInfo.CATEGORY_GAME) {
-                Log.e(TAG, "Ignoring attempt to get the Game Mode for '" + packageName
+                Slog.e(TAG, "Ignoring attempt to get the Game Mode for '" + packageName
                         + "' which is not categorized as a game: applicationInfo.flags = "
                         + applicationInfo.flags + ", category = " + applicationInfo.category);
                 return GameManager.GAME_MODE_UNSUPPORTED;
@@ -269,7 +497,7 @@
             final ApplicationInfo applicationInfo = mPackageManager
                     .getApplicationInfoAsUser(packageName, PackageManager.MATCH_ALL, userId);
             if (applicationInfo.category != ApplicationInfo.CATEGORY_GAME) {
-                Log.e(TAG, "Ignoring attempt to set the Game Mode for '" + packageName
+                Slog.e(TAG, "Ignoring attempt to set the Game Mode for '" + packageName
                         + "' which is not categorized as a game: applicationInfo.flags = "
                         + applicationInfo.flags + ", category = " + applicationInfo.category);
                 return;
@@ -295,6 +523,7 @@
                 mHandler.sendMessageDelayed(msg, WRITE_SETTINGS_DELAY);
             }
         }
+        updateCompatModeDownscale(packageName, gameMode);
     }
 
     /**
@@ -303,6 +532,8 @@
     @VisibleForTesting
     void onBootCompleted() {
         Slog.d(TAG, "onBootCompleted");
+        final Message msg = mHandler.obtainMessage(POPULATE_GAME_MODE_SETTINGS);
+        mHandler.sendMessage(msg);
     }
 
     void onUserStarting(int userId) {
@@ -329,6 +560,187 @@
         }
     }
 
+    private void loadDeviceConfigLocked() {
+        final List<PackageInfo> packages = mPackageManager.getInstalledPackages(0);
+        final String[] packageNames = packages.stream().map(e -> packageNameToKey(e.packageName))
+                .toArray(String[]::new);
+        synchronized (mDeviceConfigLock) {
+            final Properties properties = DeviceConfig.getProperties(
+                    DeviceConfig.NAMESPACE_GAME_OVERLAY, packageNames);
+            for (String key : properties.getKeyset()) {
+                final GamePackageConfiguration config =
+                        GamePackageConfiguration.fromProperties(key, properties);
+                putConfig(config);
+            }
+        }
+    }
+
+    private void disableCompatScale(String packageName) {
+        final long uid = Binder.clearCallingIdentity();
+        try {
+            final HashSet<Long> disabledSet = new HashSet<>();
+            disabledSet.add(DOWNSCALED);
+            final CompatibilityChangeConfig changeConfig = new CompatibilityChangeConfig(
+                    new Compatibility.ChangeConfig(new HashSet<>(), disabledSet));
+            // TODO: switch to new API provided by aosp/1599153 once merged
+            try {
+                mPlatformCompat.setOverridesForTest(changeConfig, packageName);
+            } catch (SecurityException e) {
+                Slog.e(TAG, "Missing compat override permission", e);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to call IPlatformCompat#setOverridesForTest", e);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(uid);
+        }
+    }
+
+    private void enableCompatScale(String packageName, long scaleId) {
+        final long uid = Binder.clearCallingIdentity();
+        try {
+            final HashSet<Long> disabledSet = new HashSet<>();
+            final HashSet<Long> enabledSet = new HashSet<>();
+            disabledSet.add(DOWNSCALE_50);
+            disabledSet.add(DOWNSCALE_60);
+            disabledSet.add(DOWNSCALE_70);
+            disabledSet.add(DOWNSCALE_80);
+            disabledSet.add(DOWNSCALE_90);
+            disabledSet.remove(scaleId);
+            enabledSet.add(DOWNSCALED);
+            enabledSet.add(scaleId);
+            final CompatibilityChangeConfig changeConfig = new CompatibilityChangeConfig(
+                    new Compatibility.ChangeConfig(enabledSet, disabledSet));
+            // TODO: switch to new API provided by aosp/1599153 once merged
+            try {
+                mPlatformCompat.setOverridesForTest(changeConfig, packageName);
+            } catch (SecurityException e) {
+                Slog.e(TAG, "Missing compat override permission", e);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to call IPlatformCompat#setOverridesForTest", e);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(uid);
+        }
+    }
+
+    private void updateCompatModeDownscale(String packageName, @GameMode int gameMode) {
+        synchronized (mDeviceConfigLock) {
+            if (gameMode == GameManager.GAME_MODE_STANDARD
+                    || gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
+                disableCompatScale(packageName);
+                Slog.v(TAG, "Disabling downscale");
+                return;
+            }
+            if (DEBUG) {
+                Slog.v(TAG, dumpDeviceConfigs());
+            }
+            final GamePackageConfiguration packageConfig = mConfigs.get(packageName);
+            if (packageConfig == null) {
+                Slog.w(TAG, "Package configuration not found for " + packageName);
+                return;
+            }
+            final GameModeConfiguration modeConfig = packageConfig.getGameModeConfiguration(
+                    gameMode);
+            if (modeConfig == null) {
+                Slog.w(TAG, "Game mode " + gameMode + " not found for " + packageName);
+                return;
+            }
+            long scaleId = modeConfig.getCompatChangeId();
+            if (scaleId == 0) {
+                Slog.w(TAG, "Invalid downscaling change id " + scaleId + " for "
+                        + packageName);
+                return;
+            }
+            Slog.i(TAG, "Enabling downscale: " + scaleId + " for " + packageName);
+            enableCompatScale(packageName, scaleId);
+        }
+    }
+
+    private void putConfig(GamePackageConfiguration config) {
+        if (config.isValid()) {
+            if (DEBUG) {
+                Slog.i(TAG, "Adding config: " + config.toString());
+            }
+            mConfigs.put(config.getPackageName(), config);
+        } else {
+            Slog.w(TAG, "Invalid package config for "
+                    + config.getPackageName() + ":" + config.toString());
+        }
+    }
+
+    private void registerPackageReceiver() {
+        final IntentFilter packageFilter = new IntentFilter();
+        packageFilter.addAction(ACTION_PACKAGE_ADDED);
+        packageFilter.addAction(ACTION_PACKAGE_CHANGED);
+        packageFilter.addAction(ACTION_PACKAGE_REMOVED);
+        packageFilter.addDataScheme("package");
+        final BroadcastReceiver packageReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
+                final Uri data = intent.getData();
+                try {
+                    final String packageName = data.getSchemeSpecificPart();
+                    switch (intent.getAction()) {
+                        case ACTION_PACKAGE_ADDED:
+                        case ACTION_PACKAGE_CHANGED:
+                            synchronized (mDeviceConfigLock) {
+                                Properties properties = DeviceConfig.getProperties(
+                                        DeviceConfig.NAMESPACE_GAME_OVERLAY,
+                                        packageNameToKey(packageName));
+                                for (String key : properties.getKeyset()) {
+                                    GamePackageConfiguration config =
+                                            GamePackageConfiguration.fromProperties(key,
+                                                    properties);
+                                    putConfig(config);
+                                }
+                            }
+                            break;
+                        case ACTION_PACKAGE_REMOVED:
+                            disableCompatScale(packageName);
+                            mConfigs.remove(packageName);
+                            break;
+                        default:
+                            // do nothing
+                            break;
+                    }
+                } catch (NullPointerException e) {
+                    Slog.e(TAG, "Failed to get package name for new package", e);
+                }
+            }
+        };
+        mContext.registerReceiver(packageReceiver, packageFilter);
+    }
+
+    private void registerDeviceConfigListener() {
+        mDeviceConfigListener = new DeviceConfigListener();
+    }
+
+    /**
+     * Valid package name characters are [a-zA-Z0-9_] with a '.' delimiter. Policy keys can only use
+     * [a-zA-Z0-9_] so we must handle periods. We do this by appending a '_' to any existing
+     * sequence of '_', then we replace all '.' chars with '_';
+     */
+    private static String packageNameToKey(String name) {
+        return name.replaceAll("(_+)", "_$1").replaceAll("\\.", "_");
+    }
+
+    /**
+     * Replace the last '_' in a sequence with '.' (this can be one or more chars), then replace the
+     * resulting special case '_.' with just '_' to get the original package name.
+     */
+    private static String keyToPackageName(String key) {
+        return key.replaceAll("(_)(?!\\1)", ".").replaceAll("_\\.", "_");
+    }
+
+    private String dumpDeviceConfigs() {
+        StringBuilder out = new StringBuilder();
+        for (String key : mConfigs.keySet()) {
+            out.append("[\nName: ").append(key)
+                    .append("\nConfig: ").append(mConfigs.get(key).toString()).append("\n]");
+        }
+        return out.toString();
+    }
+
     private static ServiceThread createServiceThread() {
         ServiceThread handlerThread = new ServiceThread(TAG,
                 Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index 3cfaaf7..677ea5d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -52,7 +52,8 @@
          *
          * @param clientMonitor Reference of the ClientMonitor that is starting.
          */
-        default void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {}
+        default void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+        }
 
         /**
          * Invoked when the ClientMonitor operation is complete. This abstracts away asynchronous
@@ -63,10 +64,11 @@
          * @param clientMonitor Reference of the ClientMonitor that finished.
          * @param success True if the operation completed successfully.
          */
-        default void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {}
+        default void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
+        }
     }
 
-    protected final int mSequentialId;
+    private final int mSequentialId;
     @NonNull private final Context mContext;
     private final int mTargetUserId;
     @NonNull private final String mOwner;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 07d173c..0c883b0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -92,7 +92,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            mCancellationSignal = getFreshDaemon().authenticate(mSequentialId, mOperationId);
+            mCancellationSignal = getFreshDaemon().authenticate(mOperationId);
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting auth", e);
             onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index 0eb51fd..58eb3ba 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -160,7 +160,7 @@
                 features = new byte[0];
             }
 
-            mCancellationSignal = getFreshDaemon().enroll(mSequentialId,
+            mCancellationSignal = getFreshDaemon().enroll(
                     HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken),
                     EnrollmentType.DEFAULT, features, mPreviewSurface);
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
index 7a846f5..8cbb896 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
@@ -42,7 +42,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().generateChallenge(mSequentialId);
+            getFreshDaemon().generateChallenge();
         } catch (RemoteException e) {
             Slog.e(TAG, "Unable to generateChallenge", e);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
index 773647b..af826c2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
@@ -56,7 +56,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().getAuthenticatorId(mSequentialId);
+            getFreshDaemon().getAuthenticatorId();
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception", e);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java
index 75888a5..0ece884 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java
@@ -48,7 +48,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().enumerateEnrollments(mSequentialId);
+            getFreshDaemon().enumerateEnrollments();
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting enumerate", e);
             mCallback.onClientFinished(this, false /* success */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java
index 855ee1d..405e2b2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java
@@ -40,7 +40,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().invalidateAuthenticatorId(mSequentialId);
+            getFreshDaemon().invalidateAuthenticatorId();
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception", e);
             mCallback.onClientFinished(this, false /* success */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
index 48796c1..ba678f3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
@@ -53,7 +53,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().removeEnrollments(mSequentialId, mBiometricIds);
+            getFreshDaemon().removeEnrollments(mBiometricIds);
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting remove", e);
             mCallback.onClientFinished(this, false /* success */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index ce728bc..5e57950 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -71,7 +71,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().resetLockout(mSequentialId, mHardwareAuthToken);
+            getFreshDaemon().resetLockout(mHardwareAuthToken);
         } catch (RemoteException e) {
             Slog.e(TAG, "Unable to reset lockout", e);
             mCallback.onClientFinished(this, false /* success */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
index 2863f9f..2294173 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
@@ -45,7 +45,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().revokeChallenge(mSequentialId, mChallenge);
+            getFreshDaemon().revokeChallenge(mChallenge);
         } catch (RemoteException e) {
             Slog.e(TAG, "Unable to revokeChallenge", e);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
index 8d3853b..06328e3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
@@ -44,7 +44,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().close(mSequentialId);
+            getFreshDaemon().close();
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception", e);
             getCallback().onClientFinished(this, false /* success */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 768f464..ee36775 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -138,11 +138,6 @@
         }
 
         @Override
-        public void onStateChanged(int cookie, byte state) {
-            // TODO(b/162973174)
-        }
-
-        @Override
         public void onChallengeGenerated(long challenge) {
             mHandler.post(() -> {
                 final BaseClientMonitor client = mScheduler.getCurrentClient();
@@ -535,7 +530,7 @@
                 if (mCurrentSession != null && mCurrentSession.mSession != null) {
                     // TODO(181984005): This should be scheduled instead of directly invoked
                     Slog.d(mTag, "Closing old session");
-                    mCurrentSession.mSession.close(888 /* cookie */);
+                    mCurrentSession.mSession.close();
                 }
             } catch (RemoteException e) {
                 Slog.e(mTag, "RemoteException", e);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
index 36327bb..c63af7e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
@@ -22,7 +22,6 @@
 import android.hardware.biometrics.face.ISession;
 import android.hardware.biometrics.face.ISessionCallback;
 import android.hardware.biometrics.face.SensorProps;
-import android.hardware.biometrics.face.SessionState;
 import android.hardware.common.NativeHandle;
 import android.hardware.keymaster.HardwareAuthToken;
 import android.os.RemoteException;
@@ -45,21 +44,21 @@
 
         return new ISession.Stub() {
             @Override
-            public void generateChallenge(int cookie) throws RemoteException {
-                Slog.w(TAG, "generateChallenge, cookie: " + cookie);
+            public void generateChallenge() throws RemoteException {
+                Slog.w(TAG, "generateChallenge");
                 cb.onChallengeGenerated(0L);
             }
 
             @Override
-            public void revokeChallenge(int cookie, long challenge) throws RemoteException {
-                Slog.w(TAG, "revokeChallenge: " + challenge + ", cookie: " + cookie);
+            public void revokeChallenge(long challenge) throws RemoteException {
+                Slog.w(TAG, "revokeChallenge: " + challenge);
                 cb.onChallengeRevoked(challenge);
             }
 
             @Override
-            public ICancellationSignal enroll(int cookie, HardwareAuthToken hat,
+            public ICancellationSignal enroll(HardwareAuthToken hat,
                     byte enrollmentType, byte[] features, NativeHandle previewSurface) {
-                Slog.w(TAG, "enroll, cookie: " + cookie);
+                Slog.w(TAG, "enroll");
                 return new ICancellationSignal.Stub() {
                     @Override
                     public void cancel() throws RemoteException {
@@ -69,8 +68,8 @@
             }
 
             @Override
-            public ICancellationSignal authenticate(int cookie, long operationId) {
-                Slog.w(TAG, "authenticate, cookie: " + cookie);
+            public ICancellationSignal authenticate(long operationId) {
+                Slog.w(TAG, "authenticate");
                 return new ICancellationSignal.Stub() {
                     @Override
                     public void cancel() throws RemoteException {
@@ -80,8 +79,8 @@
             }
 
             @Override
-            public ICancellationSignal detectInteraction(int cookie) {
-                Slog.w(TAG, "detectInteraction, cookie: " + cookie);
+            public ICancellationSignal detectInteraction() {
+                Slog.w(TAG, "detectInteraction");
                 return new ICancellationSignal.Stub() {
                     @Override
                     public void cancel() throws RemoteException {
@@ -91,51 +90,51 @@
             }
 
             @Override
-            public void enumerateEnrollments(int cookie) throws RemoteException {
-                Slog.w(TAG, "enumerateEnrollments, cookie: " + cookie);
+            public void enumerateEnrollments() throws RemoteException {
+                Slog.w(TAG, "enumerateEnrollments");
                 cb.onEnrollmentsEnumerated(new int[0]);
             }
 
             @Override
-            public void removeEnrollments(int cookie, int[] enrollmentIds) throws RemoteException {
-                Slog.w(TAG, "removeEnrollments, cookie: " + cookie);
+            public void removeEnrollments(int[] enrollmentIds) throws RemoteException {
+                Slog.w(TAG, "removeEnrollments");
                 cb.onEnrollmentsRemoved(enrollmentIds);
             }
 
             @Override
-            public void getFeatures(int cookie, int enrollmentId) throws RemoteException {
-                Slog.w(TAG, "getFeatures, cookie: " + cookie);
+            public void getFeatures(int enrollmentId) throws RemoteException {
+                Slog.w(TAG, "getFeatures");
                 cb.onFeaturesRetrieved(new byte[0], enrollmentId);
             }
 
             @Override
-            public void setFeature(int cookie, HardwareAuthToken hat, int enrollmentId,
+            public void setFeature(HardwareAuthToken hat, int enrollmentId,
                     byte feature, boolean enabled) throws RemoteException {
-                Slog.w(TAG, "setFeature, cookie: " + cookie);
+                Slog.w(TAG, "setFeature");
                 cb.onFeatureSet(enrollmentId, feature);
             }
 
             @Override
-            public void getAuthenticatorId(int cookie) throws RemoteException {
-                Slog.w(TAG, "getAuthenticatorId, cookie: " + cookie);
+            public void getAuthenticatorId() throws RemoteException {
+                Slog.w(TAG, "getAuthenticatorId");
                 cb.onAuthenticatorIdRetrieved(0L);
             }
 
             @Override
-            public void invalidateAuthenticatorId(int cookie) throws RemoteException {
-                Slog.w(TAG, "invalidateAuthenticatorId, cookie: " + cookie);
+            public void invalidateAuthenticatorId() throws RemoteException {
+                Slog.w(TAG, "invalidateAuthenticatorId");
                 cb.onAuthenticatorIdInvalidated(0L);
             }
 
             @Override
-            public void resetLockout(int cookie, HardwareAuthToken hat) throws RemoteException {
-                Slog.w(TAG, "resetLockout, cookie: " + cookie);
+            public void resetLockout(HardwareAuthToken hat) throws RemoteException {
+                Slog.w(TAG, "resetLockout");
                 cb.onLockoutCleared();
             }
 
             @Override
-            public void close(int cookie) throws RemoteException {
-                Slog.w(TAG, "close, cookie: " + cookie);
+            public void close() throws RemoteException {
+                Slog.w(TAG, "close");
                 cb.onSessionClosed();
             }
         };
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 50756c8..79e361f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -528,6 +528,8 @@
                 }
             }
 
+            scheduleUpdateActiveUserWithoutHandler(userId);
+
             final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
                     mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), opPackageName,
                     mSensorId, mCurrentChallengeOwner);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 76a47d3..4e5d12d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -92,7 +92,7 @@
         UdfpsHelper.showUdfpsOverlay(getSensorId(), Utils.getUdfpsAuthReason(this),
                 mUdfpsOverlayController, this);
         try {
-            mCancellationSignal = getFreshDaemon().authenticate(mSequentialId, mOperationId);
+            mCancellationSignal = getFreshDaemon().authenticate(mOperationId);
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception", e);
             onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index 620a9cf..9e9d0ee 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -74,7 +74,7 @@
                 IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD,
                 mUdfpsOverlayController, this);
         try {
-            mCancellationSignal = getFreshDaemon().detectInteraction(mSequentialId);
+            mCancellationSignal = getFreshDaemon().detectInteraction();
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting finger detect", e);
             UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 63fa66c..fd4aece 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -123,7 +123,7 @@
                 UdfpsHelper.getReasonFromEnrollReason(mEnrollReason),
                 mUdfpsOverlayController, this);
         try {
-            mCancellationSignal = getFreshDaemon().enroll(mSequentialId,
+            mCancellationSignal = getFreshDaemon().enroll(
                     HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken));
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting enroll", e);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
index 3c9cced..83c6421 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
@@ -44,7 +44,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().generateChallenge(mSequentialId);
+            getFreshDaemon().generateChallenge();
         } catch (RemoteException e) {
             Slog.e(TAG, "Unable to generateChallenge", e);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
index ce1a318..ed2345e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
@@ -56,7 +56,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().getAuthenticatorId(mSequentialId);
+            getFreshDaemon().getAuthenticatorId();
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception", e);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java
index c930360..e20544a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java
@@ -48,7 +48,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().enumerateEnrollments(mSequentialId);
+            getFreshDaemon().enumerateEnrollments();
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting enumerate", e);
             mCallback.onClientFinished(this, false /* success */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java
index 80d1a0f..6cd2ef1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java
@@ -40,7 +40,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().invalidateAuthenticatorId(mSequentialId);
+            getFreshDaemon().invalidateAuthenticatorId();
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception", e);
             mCallback.onClientFinished(this, false /* success */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
index c622208..9a9d6ab 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
@@ -54,7 +54,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().removeEnrollments(mSequentialId, mBiometricIds);
+            getFreshDaemon().removeEnrollments(mBiometricIds);
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting remove", e);
             mCallback.onClientFinished(this, false /* success */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index adffba2..b00c592 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -71,7 +71,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().resetLockout(mSequentialId, mHardwareAuthToken);
+            getFreshDaemon().resetLockout(mHardwareAuthToken);
         } catch (RemoteException e) {
             Slog.e(TAG, "Unable to reset lockout", e);
             mCallback.onClientFinished(this, false /* success */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
index ebb4fe6..d9bf1c3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
@@ -45,7 +45,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().revokeChallenge(mSequentialId, mChallenge);
+            getFreshDaemon().revokeChallenge(mChallenge);
         } catch (RemoteException e) {
             Slog.e(TAG, "Unable to revokeChallenge", e);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
index ba81357..7055d65 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
@@ -44,7 +44,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().close(mSequentialId);
+            getFreshDaemon().close();
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception", e);
             getCallback().onClientFinished(this, false /* success */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index cd12d02..4862d849 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -136,11 +136,6 @@
         }
 
         @Override
-        public void onStateChanged(int cookie, byte state) {
-            // TODO(b/162973174)
-        }
-
-        @Override
         public void onChallengeGenerated(long challenge) {
             mHandler.post(() -> {
                 final BaseClientMonitor client = mScheduler.getCurrentClient();
@@ -515,7 +510,7 @@
                 if (mCurrentSession != null && mCurrentSession.mSession != null) {
                     // TODO(181984005): This should be scheduled instead of directly invoked
                     Slog.d(mTag, "Closing old session");
-                    mCurrentSession.mSession.close(999 /* cookie */);
+                    mCurrentSession.mSession.close();
                 }
             } catch (RemoteException e) {
                 Slog.e(mTag, "RemoteException", e);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
index 31fc068..abc3597 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
@@ -22,7 +22,6 @@
 import android.hardware.biometrics.fingerprint.ISession;
 import android.hardware.biometrics.fingerprint.ISessionCallback;
 import android.hardware.biometrics.fingerprint.SensorProps;
-import android.hardware.biometrics.fingerprint.SessionState;
 import android.hardware.keymaster.HardwareAuthToken;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -45,20 +44,20 @@
 
         return new ISession.Stub() {
             @Override
-            public void generateChallenge(int cookie) throws RemoteException {
-                Slog.w(TAG, "generateChallenge, cookie: " + cookie);
+            public void generateChallenge() throws RemoteException {
+                Slog.w(TAG, "generateChallenge");
                 cb.onChallengeGenerated(0L);
             }
 
             @Override
-            public void revokeChallenge(int cookie, long challenge) throws RemoteException {
-                Slog.w(TAG, "revokeChallenge: " + challenge + ", cookie: " + cookie);
+            public void revokeChallenge(long challenge) throws RemoteException {
+                Slog.w(TAG, "revokeChallenge: " + challenge);
                 cb.onChallengeRevoked(challenge);
             }
 
             @Override
-            public ICancellationSignal enroll(int cookie, HardwareAuthToken hat) {
-                Slog.w(TAG, "enroll, cookie: " + cookie);
+            public ICancellationSignal enroll(HardwareAuthToken hat) {
+                Slog.w(TAG, "enroll");
                 return new ICancellationSignal.Stub() {
                     @Override
                     public void cancel() throws RemoteException {
@@ -68,8 +67,8 @@
             }
 
             @Override
-            public ICancellationSignal authenticate(int cookie, long operationId) {
-                Slog.w(TAG, "authenticate, cookie: " + cookie);
+            public ICancellationSignal authenticate(long operationId) {
+                Slog.w(TAG, "authenticate");
                 return new ICancellationSignal.Stub() {
                     @Override
                     public void cancel() throws RemoteException {
@@ -79,8 +78,8 @@
             }
 
             @Override
-            public ICancellationSignal detectInteraction(int cookie) {
-                Slog.w(TAG, "detectInteraction, cookie: " + cookie);
+            public ICancellationSignal detectInteraction() {
+                Slog.w(TAG, "detectInteraction");
                 return new ICancellationSignal.Stub() {
                     @Override
                     public void cancel() throws RemoteException {
@@ -90,38 +89,38 @@
             }
 
             @Override
-            public void enumerateEnrollments(int cookie) throws RemoteException {
-                Slog.w(TAG, "enumerateEnrollments, cookie: " + cookie);
+            public void enumerateEnrollments() throws RemoteException {
+                Slog.w(TAG, "enumerateEnrollments");
                 cb.onEnrollmentsEnumerated(new int[0]);
             }
 
             @Override
-            public void removeEnrollments(int cookie, int[] enrollmentIds) throws RemoteException {
-                Slog.w(TAG, "removeEnrollments, cookie: " + cookie);
+            public void removeEnrollments(int[] enrollmentIds) throws RemoteException {
+                Slog.w(TAG, "removeEnrollments");
                 cb.onEnrollmentsRemoved(enrollmentIds);
             }
 
             @Override
-            public void getAuthenticatorId(int cookie) throws RemoteException {
-                Slog.w(TAG, "getAuthenticatorId, cookie: " + cookie);
+            public void getAuthenticatorId() throws RemoteException {
+                Slog.w(TAG, "getAuthenticatorId");
                 cb.onAuthenticatorIdRetrieved(0L);
             }
 
             @Override
-            public void invalidateAuthenticatorId(int cookie) throws RemoteException {
-                Slog.w(TAG, "invalidateAuthenticatorId, cookie: " + cookie);
+            public void invalidateAuthenticatorId() throws RemoteException {
+                Slog.w(TAG, "invalidateAuthenticatorId");
                 cb.onAuthenticatorIdInvalidated(0L);
             }
 
             @Override
-            public void resetLockout(int cookie, HardwareAuthToken hat) throws RemoteException {
-                Slog.w(TAG, "resetLockout, cookie: " + cookie);
+            public void resetLockout(HardwareAuthToken hat) throws RemoteException {
+                Slog.w(TAG, "resetLockout");
                 cb.onLockoutCleared();
             }
 
             @Override
-            public void close(int cookie) throws RemoteException {
-                Slog.w(TAG, "close, cookie: " + cookie);
+            public void close() throws RemoteException {
+                Slog.w(TAG, "close");
                 cb.onSessionClosed();
             }
 
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 00b39f1..cd24576 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -15,17 +15,33 @@
  */
 package com.android.server.camera;
 
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.os.Build.VERSION_CODES.M;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.TaskStackListener;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.graphics.Rect;
 import android.hardware.CameraSessionStats;
 import android.hardware.CameraStreamStats;
 import android.hardware.ICameraService;
 import android.hardware.ICameraServiceProxy;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.display.DisplayManager;
 import android.media.AudioManager;
 import android.metrics.LogMaker;
 import android.nfc.INfcAdapter;
@@ -41,7 +57,10 @@
 import android.stats.camera.nano.CameraProtos.CameraStreamProto;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.Slog;
+import android.view.Display;
+import android.view.Surface;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.framework.protobuf.nano.MessageNano;
@@ -61,6 +80,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
@@ -203,6 +223,63 @@
         }
     }
 
+    private final TaskStateHandler mTaskStackListener = new TaskStateHandler();
+
+    private final class TaskInfo {
+        private int frontTaskId;
+        private boolean isResizeable;
+        private boolean isFixedOrientationLandscape;
+        private boolean isFixedOrientationPortrait;
+        private int displayId;
+    }
+
+    private final class TaskStateHandler extends TaskStackListener {
+        private final Object mMapLock = new Object();
+
+        // maps the current top level task id to its corresponding package name
+        @GuardedBy("mMapLock")
+        private final ArrayMap<String, TaskInfo> mTaskInfoMap = new ArrayMap<>();
+
+        @Override
+        public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo)
+                throws RemoteException {
+            synchronized (mMapLock) {
+                TaskInfo info = new TaskInfo();
+                info.frontTaskId = taskInfo.taskId;
+                info.isResizeable = taskInfo.isResizeable;
+                info.displayId = taskInfo.displayId;
+                info.isFixedOrientationLandscape = ActivityInfo.isFixedOrientationLandscape(
+                        taskInfo.topActivityInfo.screenOrientation);
+                info.isFixedOrientationPortrait = ActivityInfo.isFixedOrientationPortrait(
+                        taskInfo.topActivityInfo.screenOrientation);
+                mTaskInfoMap.put(taskInfo.topActivityInfo.packageName, info);
+            }
+        }
+
+        @Override
+        public void onTaskRemoved(int taskId) throws RemoteException {
+            synchronized (mMapLock) {
+                for (Map.Entry<String, TaskInfo> entry : mTaskInfoMap.entrySet()){
+                    if (entry.getValue().frontTaskId == taskId) {
+                        mTaskInfoMap.remove(entry.getKey());
+                        break;
+                    }
+                }
+            }
+        }
+
+        public @Nullable TaskInfo getFrontTaskInfo(String packageName) {
+            synchronized (mMapLock) {
+                if (mTaskInfoMap.containsKey(packageName)) {
+                    return mTaskInfoMap.get(packageName);
+                }
+            }
+
+            Log.e(TAG, "Top task with package name: " + packageName + " not found!");
+            return null;
+        }
+    };
+
     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -229,6 +306,102 @@
     };
 
     private final ICameraServiceProxy.Stub mCameraServiceProxy = new ICameraServiceProxy.Stub() {
+        private boolean isMOrBelow(Context ctx, String packageName) {
+            try {
+                return ctx.getPackageManager().getPackageInfo(
+                        packageName, 0).applicationInfo.targetSdkVersion <= M;
+            } catch (PackageManager.NameNotFoundException e) {
+                Slog.e(TAG,"Package name not found!");
+            }
+            return false;
+        }
+
+        /**
+         * Gets whether crop-rotate-scale is needed.
+         */
+        private boolean getNeedCropRotateScale(Context ctx, String packageName,
+                @Nullable TaskInfo taskInfo, int sensorOrientation, int lensFacing) {
+            if (taskInfo == null) {
+                return false;
+            }
+
+            // External cameras do not need crop-rotate-scale.
+            if (lensFacing != CameraMetadata.LENS_FACING_FRONT
+                    && lensFacing != CameraMetadata.LENS_FACING_BACK) {
+                Log.v(TAG, "lensFacing=" + lensFacing + ". Crop-rotate-scale is disabled.");
+                return false;
+            }
+
+            // Only enable the crop-rotate-scale workaround if the app targets M or below and is not
+            // resizeable.
+            if ((ctx != null) && !isMOrBelow(ctx, packageName) && taskInfo.isResizeable) {
+                Slog.v(TAG,
+                        "The activity is N or above and claims to support resizeable-activity. "
+                                + "Crop-rotate-scale is disabled.");
+                return false;
+            }
+
+            DisplayManager displayManager = ctx.getSystemService(DisplayManager.class);
+            Display display = displayManager.getDisplay(taskInfo.displayId);
+            int rotation = display.getRotation();
+            int rotationDegree = 0;
+            switch (rotation) {
+                case Surface.ROTATION_0:
+                    rotationDegree = 0;
+                    break;
+                case Surface.ROTATION_90:
+                    rotationDegree = 90;
+                    break;
+                case Surface.ROTATION_180:
+                    rotationDegree = 180;
+                    break;
+                case Surface.ROTATION_270:
+                    rotationDegree = 270;
+                    break;
+            }
+
+            // Here we only need to know whether the camera is landscape or portrait. Therefore we
+            // don't need to consider whether it is a front or back camera. The formula works for
+            // both.
+            boolean landscapeCamera = ((rotationDegree + sensorOrientation) % 180 == 0);
+            Slog.v(TAG,
+                    "Display.getRotation()=" + rotationDegree
+                            + " CameraCharacteristics.SENSOR_ORIENTATION=" + sensorOrientation
+                            + " isFixedOrientationPortrait=" + taskInfo.isFixedOrientationPortrait
+                            + " isFixedOrientationLandscape=" +
+                            taskInfo.isFixedOrientationLandscape);
+            // We need to do crop-rotate-scale when camera is landscape and activity is portrait or
+            // vice versa.
+            if ((taskInfo.isFixedOrientationPortrait && landscapeCamera)
+                    || (taskInfo.isFixedOrientationLandscape && !landscapeCamera)) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public boolean isRotateAndCropOverrideNeeded(String packageName, int sensorOrientation,
+                int lensFacing) {
+            if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
+                Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " +
+                        " camera service UID!");
+                return false;
+            }
+
+            // A few remaining todos:
+            // 1) Do the same check when working in WM compatible mode. The sequence needs
+            //    to be adjusted and use orientation events as triggers for all active camera
+            //    clients.
+            // 2) Modify the sensor orientation in camera characteristics along with any 3A regions
+            //    in capture requests/results to account for thea physical rotation. The former
+            //    is somewhat tricky as it assumes that camera clients always check for the current
+            //    value by retrieving the camera characteristics from the camera device.
+            return getNeedCropRotateScale(mContext, packageName,
+                    mTaskStackListener.getFrontTaskInfo(packageName), sensorOrientation,
+                    lensFacing);
+        }
+
         @Override
         public void pingForUserUpdate() {
             if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
@@ -350,6 +523,12 @@
         publishBinderService(CAMERA_SERVICE_PROXY_BINDER_NAME, mCameraServiceProxy);
         publishLocalService(CameraServiceProxy.class, this);
 
+        try {
+            ActivityTaskManager.getService().registerTaskStackListener(mTaskStackListener);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to register task stack listener!");
+        }
+
         CameraStatsJobService.schedule(mContext);
     }
 
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index ae9b001..d29a0c7 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -23,7 +23,9 @@
 import android.annotation.Nullable;
 import android.app.compat.PackageOverride;
 import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
 import android.compat.annotation.EnabledSince;
+import android.compat.annotation.Overridable;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -60,6 +62,15 @@
     static final long CTS_SYSTEM_API_CHANGEID = 149391281; // This is a bug id.
 
     /**
+     * An overridable change ID to be used only in the CTS test for this SystemApi
+     */
+    @ChangeId
+    @Disabled
+    @Overridable
+    static final long CTS_SYSTEM_API_OVERRIDABLE_CHANGEID = 174043039; // This is a bug id.
+
+
+    /**
      * Callback listener for when compat changes are updated for a package.
      * See {@link #registerListener(ChangeListener)} for more details.
      */
@@ -211,6 +222,7 @@
     boolean hasPackageOverride(String pname) {
         return mRawOverrides.containsKey(pname);
     }
+
     /**
      * Remove any package override for the given package name, restoring the default behaviour.
      *
@@ -355,7 +367,7 @@
             override.setPackageName(entry.getKey());
             override.setMinVersionCode(entry.getValue().getMinVersionCode());
             override.setMaxVersionCode(entry.getValue().getMaxVersionCode());
-            override.setEnabled(entry.getValue().getEnabled());
+            override.setEnabled(entry.getValue().isEnabled());
             rawList.add(override);
         }
         changeOverrides.setRaw(rawOverrides);
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index ef86f42..55e2696 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -304,6 +304,16 @@
     }
 
     /**
+     * Returns whether the change is overridable.
+     */
+    boolean isOverridable(long changeId) {
+        synchronized (mChanges) {
+            CompatChange c = mChanges.get(changeId);
+            return c != null && c.getOverridable();
+        }
+    }
+
+    /**
      * Removes an override previously added via {@link #addOverride(long, String, boolean)}.
      *
      * <p>This restores the default behaviour for the given change and app, once any app processes
@@ -343,7 +353,7 @@
 
     /**
      * Removes all overrides previously added via {@link #addOverride(long, String, boolean)} or
-     * {@link #addOverrides(CompatibilityChangeConfig, String)} for a certain package.
+     * {@link #addOverrides(CompatibilityOverrideConfig, String)} for a certain package.
      *
      * <p>This restores the default behaviour for the given app.
      *
@@ -632,8 +642,11 @@
         }
         boolean shouldInvalidateCache = false;
         for (CompatChange c: changes) {
+            if (!c.hasPackageOverride(packageName)) {
+                continue;
+            }
             OverrideAllowedState allowedState =
-                    mOverrideValidator.getOverrideAllowedState(c.getId(), packageName);
+                    mOverrideValidator.getOverrideAllowedStateForRecheck(c.getId(), packageName);
             shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, mContext);
         }
         if (shouldInvalidateCache) {
diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
index aa66a1a..b500691 100644
--- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
+++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
@@ -16,6 +16,9 @@
 
 package com.android.server.compat;
 
+import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
 import static com.android.internal.compat.OverrideAllowedState.ALLOWED;
 import static com.android.internal.compat.OverrideAllowedState.DEFERRED_VERIFICATION;
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK;
@@ -24,6 +27,7 @@
 import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE;
 import static com.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -69,8 +73,25 @@
         mForceNonDebuggableFinalBuild = false;
     }
 
+    /**
+     * Check the allowed state for the given changeId and packageName on a recheck.
+     *
+     * <p>Recheck happens when the given app is getting updated. In this case we cannot do a
+     * permission check on the caller, so we're using the fact that the override was present as
+     * proof that the original caller was allowed to set this override.
+     */
+    OverrideAllowedState getOverrideAllowedStateForRecheck(long changeId,
+            @NonNull String packageName) {
+        return getOverrideAllowedStateInternal(changeId, packageName, true);
+    }
+
     @Override
     public OverrideAllowedState getOverrideAllowedState(long changeId, String packageName) {
+        return getOverrideAllowedStateInternal(changeId, packageName, false);
+    }
+
+    private OverrideAllowedState getOverrideAllowedStateInternal(long changeId, String packageName,
+            boolean isRecheck) {
         if (mCompatConfig.isLoggingOnly(changeId)) {
             return new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1);
         }
@@ -99,6 +120,16 @@
         } catch (NameNotFoundException e) {
             return new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1);
         }
+        // If the change is annotated as @Overridable, apps with the specific permission can
+        // set the override even on production builds. When rechecking the override, e.g. during an
+        // app update we can bypass this check, as it wouldn't have been here in the first place.
+        if (mCompatConfig.isOverridable(changeId)
+                && (isRecheck
+                        || mContext.checkCallingOrSelfPermission(
+                                OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
+                                        == PERMISSION_GRANTED)) {
+            return new OverrideAllowedState(ALLOWED, -1, -1);
+        }
         int appTargetSdk = applicationInfo.targetSdkVersion;
         // Only allow overriding debuggable apps.
         if ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
@@ -130,5 +161,4 @@
     void forceNonDebuggableFinalForTest(boolean value) {
         mForceNonDebuggableFinalBuild = value;
     }
-
 }
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 2be39aa..62de369 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.LOG_COMPAT_CHANGE;
 import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG;
+import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD;
 import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Process.SYSTEM_UID;
@@ -182,11 +183,12 @@
     }
 
     @Override
-    public void setOverridesFromInstaller(CompatibilityOverrideConfig overrides,
+    public void setOverridesOnReleaseBuilds(CompatibilityOverrideConfig overrides,
             String packageName) {
-        checkCompatChangeOverridePermission();
+        // TODO(b/183630314): Unify the permission enforcement with the other setOverrides* methods.
+        checkCompatChangeOverrideOverridablePermission();
+        checkAllCompatOverridesAreOverridable(overrides);
         mCompatConfig.addOverrides(overrides, packageName);
-        killPackage(packageName);
     }
 
     @Override
@@ -383,6 +385,26 @@
         }
     }
 
+    private void checkCompatChangeOverrideOverridablePermission() {
+        // Don't check for permissions within the system process
+        if (Binder.getCallingUid() == SYSTEM_UID) {
+            return;
+        }
+        if (mContext.checkCallingOrSelfPermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
+                != PERMISSION_GRANTED) {
+            throw new SecurityException("Cannot override compat change");
+        }
+    }
+
+    private void checkAllCompatOverridesAreOverridable(CompatibilityOverrideConfig overrides) {
+        for (Long changeId : overrides.overrides.keySet()) {
+            if (!mCompatConfig.isOverridable(changeId)) {
+                throw new SecurityException("Only change ids marked as Overridable can be "
+                        + "overridden.");
+            }
+        }
+    }
+
     private void checkCompatChangeReadAndLogPermission() {
         checkCompatChangeReadPermission();
         checkCompatChangeLogPermission();
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 103ab95..97df5bf 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -49,6 +49,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.telephony.data.NrQosSessionAttributes;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
@@ -576,6 +577,28 @@
         }
     }
 
+    /**
+     * Notify the NetworkAgent that the network is successfully connected.
+     */
+    public void onNetworkCreated() {
+        try {
+            networkAgent.onNetworkCreated();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error sending network created event", e);
+        }
+    }
+
+    /**
+     * Notify the NetworkAgent that the network is disconnected and destroyed.
+     */
+    public void onNetworkDisconnected() {
+        try {
+            networkAgent.onNetworkDisconnected();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error sending network disconnected event", e);
+        }
+    }
+
     // TODO: consider moving out of NetworkAgentInfo into its own class
     private class NetworkAgentMessageHandler extends INetworkAgentRegistry.Stub {
         private final Handler mHandler;
@@ -633,7 +656,13 @@
         @Override
         public void sendEpsQosSessionAvailable(final int qosCallbackId, final QosSession session,
                 final EpsBearerQosSessionAttributes attributes) {
-            mQosCallbackTracker.sendEventQosSessionAvailable(qosCallbackId, session, attributes);
+            mQosCallbackTracker.sendEventEpsQosSessionAvailable(qosCallbackId, session, attributes);
+        }
+
+        @Override
+        public void sendNrQosSessionAvailable(final int qosCallbackId, final QosSession session,
+                final NrQosSessionAttributes attributes) {
+            mQosCallbackTracker.sendEventNrQosSessionAvailable(qosCallbackId, session, attributes);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java b/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java
index 0f5400d..534dbe7 100644
--- a/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java
+++ b/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java
@@ -27,6 +27,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.telephony.data.NrQosSessionAttributes;
 import android.util.Log;
 
 import java.util.Objects;
@@ -146,13 +147,23 @@
         mNetworkAgentInfo.onQosCallbackUnregistered(mAgentCallbackId);
     }
 
-    void sendEventQosSessionAvailable(final QosSession session,
+    void sendEventEpsQosSessionAvailable(final QosSession session,
             final EpsBearerQosSessionAttributes attributes) {
         try {
-            if (DBG) log("sendEventQosSessionAvailable: sending...");
+            if (DBG) log("sendEventEpsQosSessionAvailable: sending...");
             mCallback.onQosEpsBearerSessionAvailable(session, attributes);
         } catch (final RemoteException e) {
-            loge("sendEventQosSessionAvailable: remote exception", e);
+            loge("sendEventEpsQosSessionAvailable: remote exception", e);
+        }
+    }
+
+    void sendEventNrQosSessionAvailable(final QosSession session,
+            final NrQosSessionAttributes attributes) {
+        try {
+            if (DBG) log("sendEventNrQosSessionAvailable: sending...");
+            mCallback.onNrQosSessionAvailable(session, attributes);
+        } catch (final RemoteException e) {
+            loge("sendEventNrQosSessionAvailable: remote exception", e);
         }
     }
 
diff --git a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
index 8bda532..b6ab47b 100644
--- a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
+++ b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
@@ -27,6 +27,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.telephony.data.NrQosSessionAttributes;
 import android.util.Log;
 
 import com.android.net.module.util.CollectionUtils;
@@ -179,17 +180,31 @@
     }
 
     /**
-     * Called when the NetworkAgent sends the qos session available event
+     * Called when the NetworkAgent sends the qos session available event for EPS
      *
      * @param qosCallbackId the callback id that the qos session is now available to
      * @param session the qos session that is now available
      * @param attributes the qos attributes that are now available on the qos session
      */
-    public void sendEventQosSessionAvailable(final int qosCallbackId,
+    public void sendEventEpsQosSessionAvailable(final int qosCallbackId,
             final QosSession session,
             final EpsBearerQosSessionAttributes attributes) {
-        runOnAgentConnection(qosCallbackId, "sendEventQosSessionAvailable: ",
-                ac -> ac.sendEventQosSessionAvailable(session, attributes));
+        runOnAgentConnection(qosCallbackId, "sendEventEpsQosSessionAvailable: ",
+                ac -> ac.sendEventEpsQosSessionAvailable(session, attributes));
+    }
+
+    /**
+     * Called when the NetworkAgent sends the qos session available event for NR
+     *
+     * @param qosCallbackId the callback id that the qos session is now available to
+     * @param session the qos session that is now available
+     * @param attributes the qos attributes that are now available on the qos session
+     */
+    public void sendEventNrQosSessionAvailable(final int qosCallbackId,
+            final QosSession session,
+            final NrQosSessionAttributes attributes) {
+        runOnAgentConnection(qosCallbackId, "sendEventNrQosSessionAvailable: ",
+                ac -> ac.sendEventNrQosSessionAvailable(session, attributes));
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/BrightnessSetting.java b/services/core/java/com/android/server/display/BrightnessSetting.java
new file mode 100644
index 0000000..8ce7b66
--- /dev/null
+++ b/services/core/java/com/android/server/display/BrightnessSetting.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+import android.view.Display;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Saves brightness to a persistent data store, enabling each logical display to have its own
+ * brightness.
+ */
+public class BrightnessSetting {
+    private static final String TAG = "BrightnessSetting";
+
+    private static final int MSG_BRIGHTNESS_CHANGED = 1;
+    private static final Uri BRIGHTNESS_FLOAT_URI =
+            Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT);
+    private final PersistentDataStore mPersistentDataStore;
+
+    private final boolean mIsDefaultDisplay;
+    private final Context mContext;
+    private final LogicalDisplay mLogicalDisplay;
+    private final Object mLock = new Object();
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == MSG_BRIGHTNESS_CHANGED) {
+                float brightnessVal = Float.intBitsToFloat(msg.arg1);
+                notifyListeners(brightnessVal);
+            }
+        }
+    };
+
+    private final ContentObserver mBrightnessSettingsObserver = new ContentObserver(mHandler) {
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (selfChange) {
+                return;
+            }
+            if (BRIGHTNESS_FLOAT_URI.equals(uri)) {
+                float brightness = getScreenBrightnessSettingFloat();
+                setBrightness(brightness, true);
+            }
+        }
+    };
+
+    private final CopyOnWriteArrayList<BrightnessSettingListener> mListeners =
+            new CopyOnWriteArrayList<BrightnessSettingListener>();
+
+    private float mBrightness;
+
+    BrightnessSetting(@NonNull PersistentDataStore persistentDataStore,
+            @NonNull LogicalDisplay logicalDisplay,
+            @NonNull Context context) {
+        mPersistentDataStore = persistentDataStore;
+        mLogicalDisplay = logicalDisplay;
+        mContext = context;
+        mIsDefaultDisplay = mLogicalDisplay.getDisplayIdLocked() == Display.DEFAULT_DISPLAY;
+        mBrightness = mPersistentDataStore.getBrightness(
+                mLogicalDisplay.getPrimaryDisplayDeviceLocked());
+        if (mIsDefaultDisplay) {
+            mContext.getContentResolver().registerContentObserver(BRIGHTNESS_FLOAT_URI,
+                    false, mBrightnessSettingsObserver);
+        }
+    }
+
+    /**
+     * Returns the brightness from the brightness setting
+     *
+     * @return brightness for the current display
+     */
+    public float getBrightness() {
+        return mBrightness;
+    }
+
+    /**
+     * Registers listener for brightness setting change events.
+     */
+    public void registerListener(BrightnessSettingListener l) {
+        if (!mListeners.contains(l)) {
+            mListeners.add(l);
+        }
+    }
+
+    /**
+     * Unregisters listener for brightness setting change events.
+     *
+     * @param l listener
+     */
+    public void unregisterListener(BrightnessSettingListener l) {
+        mListeners.remove(l);
+    }
+
+    void setBrightness(float brightness) {
+        setBrightness(brightness, false);
+    }
+
+    private void setBrightness(float brightness, boolean isFromSystemSetting) {
+        if (brightness == mBrightness) {
+            return;
+        }
+        if (Float.isNaN(brightness)) {
+            Slog.w(TAG, "Attempting to set invalid brightness");
+            return;
+        }
+        synchronized (mLock) {
+
+            mBrightness = brightness;
+
+            // If it didn't come from us
+            if (mIsDefaultDisplay && !isFromSystemSetting) {
+                Settings.System.putFloatForUser(mContext.getContentResolver(),
+                        Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightness,
+                        UserHandle.USER_CURRENT);
+            }
+            mPersistentDataStore.setBrightness(mLogicalDisplay.getPrimaryDisplayDeviceLocked(),
+                    brightness);
+            int toSend = Float.floatToIntBits(mBrightness);
+            Message msg = mHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED, toSend, 0);
+            mHandler.sendMessage(msg);
+        }
+    }
+
+    private float getScreenBrightnessSettingFloat() {
+        return Settings.System.getFloatForUser(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_FLOAT, PowerManager.BRIGHTNESS_INVALID_FLOAT,
+                UserHandle.USER_CURRENT);
+    }
+
+    private void notifyListeners(float brightness) {
+        for (BrightnessSettingListener l : mListeners) {
+            l.onBrightnessChanged(brightness);
+        }
+    }
+
+    /**
+     * Listener for changes to system brightness.
+     */
+    public interface BrightnessSettingListener {
+
+        /**
+         * Notify that the brightness has changed.
+         */
+        void onBrightnessChanged(float brightness);
+    }
+}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c010906..e38d91c 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1955,9 +1955,12 @@
         if (mBrightnessTracker == null) {
             mBrightnessTracker = new BrightnessTracker(mContext, null);
         }
+
+        final BrightnessSetting brightnessSetting = new BrightnessSetting(mPersistentDataStore,
+                display, mContext);
         final DisplayPowerController displayPowerController = new DisplayPowerController(
                 mContext, mDisplayPowerCallbacks, mPowerHandler, mSensorManager,
-                mDisplayBlanker, display, mBrightnessTracker);
+                mDisplayBlanker, display, mBrightnessTracker, brightnessSetting);
         mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
     }
 
@@ -2662,6 +2665,48 @@
         }
 
         @Override // Binder call
+        public void setBrightness(int displayId, float brightness) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
+                    "Permission required to set the display's brightness");
+            if (!isValidBrightness(brightness)) {
+                Slog.w(TAG, "Attempted to set invalid brightness" + brightness);
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mSyncRoot) {
+                    DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+                    if (dpc != null) {
+                        dpc.putScreenBrightnessSetting(brightness);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
+        public float getBrightness(int displayId) {
+            float brightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
+                    "Permission required to set the display's brightness");
+            final long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mSyncRoot) {
+                    DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+                    if (dpc != null) {
+                        brightness = dpc.getScreenBrightnessSetting();
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            return brightness;
+        }
+
+        @Override // Binder call
         public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
@@ -2809,6 +2854,13 @@
             Slog.w(TAG, msg);
             return false;
         }
+
+    }
+
+    private static boolean isValidBrightness(float brightness) {
+        return !Float.isNaN(brightness)
+                && (brightness >= PowerManager.BRIGHTNESS_MIN)
+                && (brightness <= PowerManager.BRIGHTNESS_MAX);
     }
 
     private final class LocalService extends DisplayManagerInternal {
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index d1d0496..48edb73 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -16,13 +16,11 @@
 
 package com.android.server.display;
 
-import android.Manifest;
 import android.content.Context;
 import android.content.Intent;
-import android.os.Binder;
+import android.hardware.display.DisplayManager;
 import android.os.ShellCommand;
-import android.os.UserHandle;
-import android.provider.Settings;
+import android.view.Display;
 
 import java.io.PrintWriter;
 
@@ -111,17 +109,8 @@
         }
 
         final Context context = mService.getContext();
-        context.enforceCallingOrSelfPermission(
-                Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
-                "Permission required to set the display's brightness");
-        final long token = Binder.clearCallingIdentity();
-        try {
-            Settings.System.putFloatForUser(context.getContentResolver(),
-                    Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightness,
-                    UserHandle.USER_CURRENT);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
+        final DisplayManager dm = context.getSystemService(DisplayManager.class);
+        dm.setBrightness(Display.DEFAULT_DISPLAY, brightness);
         return 0;
     }
 
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 645ca7a..4bbd338 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -263,6 +263,8 @@
                 highestConsideredPriority = Vote.PRIORITY_APP_REQUEST_SIZE;
             }
 
+            // We try to find a range of priorities which define a non-empty set of allowed display
+            // modes. Each time we fail we increase the lowest priority.
             while (lowestConsideredPriority <= highestConsideredPriority) {
                 summarizeVotes(
                         votes, lowestConsideredPriority, highestConsideredPriority, primarySummary);
@@ -343,8 +345,15 @@
             }
 
             if (baseModeId == INVALID_DISPLAY_MODE_ID) {
-                throw new IllegalStateException("Can't select a base display mode for display "
-                        + displayId + ". The votes are " + mVotesByDisplay.valueAt(displayId));
+                Slog.w(TAG, "Can't find a set of allowed modes which satisfies the votes. Falling"
+                        + " back to the default mode. Display = " + displayId + ", votes = " + votes
+                        + ", supported modes = " + Arrays.toString(modes));
+
+                float fps = defaultMode.getRefreshRate();
+                return new DesiredDisplayModeSpecs(defaultMode.getModeId(),
+                        /*allowGroupSwitching */ false,
+                        new RefreshRateRange(fps, fps),
+                        new RefreshRateRange(fps, fps));
             }
 
             if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 7110d3e..56ad01b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -122,6 +122,7 @@
     private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 7;
     private static final int MSG_IGNORE_PROXIMITY = 8;
     private static final int MSG_STOP = 9;
+    private static final int MSG_UPDATE_BRIGHTNESS = 10;
 
     private static final int PROXIMITY_UNKNOWN = -1;
     private static final int PROXIMITY_NEGATIVE = 0;
@@ -355,13 +356,14 @@
 
     private final HighBrightnessModeController mHbmController;
 
+    private final BrightnessSetting mBrightnessSetting;
+
     // A record of state for skipping brightness ramps.
     private int mSkipRampState = RAMP_STATE_SKIP_NONE;
 
     // The first autobrightness value set when entering RAMP_STATE_SKIP_INITIAL.
     private float mInitialAutoBrightness;
 
-
     // The controller for the automatic brightness level.
     private AutomaticBrightnessController mAutomaticBrightnessController;
 
@@ -410,6 +412,7 @@
     private ObjectAnimator mColorFadeOnAnimator;
     private ObjectAnimator mColorFadeOffAnimator;
     private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
+    private BrightnessSetting.BrightnessSettingListener mBrightnessSettingListener;
 
     // True if this DisplayPowerController has been stopped and should no longer be running.
     private boolean mStopped;
@@ -420,7 +423,7 @@
     public DisplayPowerController(Context context,
             DisplayPowerCallbacks callbacks, Handler handler,
             SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
-            BrightnessTracker brightnessTracker) {
+            BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting) {
         mLogicalDisplay = logicalDisplay;
         mDisplayId = mLogicalDisplay.getDisplayIdLocked();
         mHandler = new DisplayControllerHandler(handler.getLooper());
@@ -439,7 +442,7 @@
         mContext = context;
         mBrightnessTracker = brightnessTracker;
 
-
+        mBrightnessSetting = brightnessSetting;
         PowerManager pm = context.getSystemService(PowerManager.class);
 
         final Resources resources = context.getResources();
@@ -785,6 +788,10 @@
                 mAutomaticBrightnessController.stop();
             }
 
+            if (mBrightnessSetting != null) {
+                mBrightnessSetting.unregisterListener(mBrightnessSettingListener);
+            }
+
             mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
         }
     }
@@ -831,10 +838,12 @@
         if (brightness >= PowerManager.BRIGHTNESS_MIN) {
             mBrightnessTracker.start(brightness);
         }
+        mBrightnessSettingListener = brightnessValue -> {
+            Message msg = mHandler.obtainMessage(MSG_UPDATE_BRIGHTNESS, brightnessValue);
+            mHandler.sendMessage(msg);
+        };
 
-        mContext.getContentResolver().registerContentObserver(
-                Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT),
-                false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
+        mBrightnessSetting.registerListener(mBrightnessSettingListener);
         mContext.getContentResolver().registerContentObserver(
                 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT),
                 false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
@@ -1150,7 +1159,7 @@
             // before applying the low power or dim transformations so that the slider
             // accurately represents the full possible range, even if they range changes what
             // it means in absolute terms.
-            putScreenBrightnessSetting(brightnessState);
+            putScreenBrightnessSetting(brightnessState, /* updateCurrent */ true);
         }
 
         // Apply dimming by at least some minimum amount when user activity
@@ -1804,7 +1813,6 @@
 
     private void handleSettingsChange(boolean userSwitch) {
         mPendingScreenBrightnessSetting = getScreenBrightnessSetting();
-
         if (userSwitch) {
             // Don't treat user switches as user initiated change.
             mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting;
@@ -1825,10 +1833,11 @@
         return Float.isNaN(adj) ? 0.0f : clampAutoBrightnessAdjustment(adj);
     }
 
-    private float getScreenBrightnessSetting() {
-        final float brightness = Settings.System.getFloatForUser(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_FLOAT, mScreenBrightnessDefault,
-                UserHandle.USER_CURRENT);
+    float getScreenBrightnessSetting() {
+        float brightness = mBrightnessSetting.getBrightness();
+        if (Float.isNaN(brightness)) {
+            brightness = mScreenBrightnessDefault;
+        }
         return clampAbsoluteBrightness(brightness);
     }
 
@@ -1839,13 +1848,15 @@
         return clampScreenBrightnessForVr(brightnessFloat);
     }
 
-    private void putScreenBrightnessSetting(float brightnessValue) {
-        if (mDisplayId == Display.DEFAULT_DISPLAY) {
+    void putScreenBrightnessSetting(float brightnessValue) {
+        putScreenBrightnessSetting(brightnessValue, false);
+    }
+
+    private void putScreenBrightnessSetting(float brightnessValue, boolean updateCurrent) {
+        if (updateCurrent) {
             mCurrentScreenBrightnessSetting = brightnessValue;
-            Settings.System.putFloatForUser(mContext.getContentResolver(),
-                    Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightnessValue,
-                    UserHandle.USER_CURRENT);
         }
+        mBrightnessSetting.setBrightness(brightnessValue);
     }
 
     private void putAutoBrightnessAdjustmentSetting(float adjustment) {
@@ -2175,7 +2186,7 @@
                     }
                     break;
                 case MSG_CONFIGURE_BRIGHTNESS:
-                    mBrightnessConfiguration = (BrightnessConfiguration)msg.obj;
+                    mBrightnessConfiguration = (BrightnessConfiguration) msg.obj;
                     updatePowerState();
                     break;
 
@@ -2197,6 +2208,12 @@
                 case MSG_STOP:
                     cleanupHandlerThreadAfterStop();
                     break;
+
+                case MSG_UPDATE_BRIGHTNESS:
+                    if (mStopped) {
+                        return;
+                    }
+                    handleSettingsChange(false /*userSwitch*/);
             }
         }
     }
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index a62642b..c90ddf4 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -62,6 +62,7 @@
  *   &lt;display-states>
  *      &lt;display unique-id="XXXXXXX">
  *          &lt;color-mode>0&lt;/color-mode>
+ *          &lt;brightness-value>0&lt;/brightness-value>
  *      &lt;/display>
  *  &lt;/display-states>
  *  &lt;stable-device-values>
@@ -82,7 +83,7 @@
  * TODO: refactor this to extract common code shared with the input manager's data store
  */
 final class PersistentDataStore {
-    static final String TAG = "DisplayManager";
+    static final String TAG = "DisplayManager.PersistentDataStore";
 
     private static final String TAG_DISPLAY_MANAGER_STATE = "display-manager-state";
 
@@ -95,6 +96,7 @@
     private static final String TAG_DISPLAY_STATES = "display-states";
     private static final String TAG_DISPLAY = "display";
     private static final String TAG_COLOR_MODE = "color-mode";
+    private static final String TAG_BRIGHTNESS_VALUE = "brightness-value";
     private static final String ATTR_UNIQUE_ID = "unique-id";
 
     private static final String TAG_STABLE_DEVICE_VALUES = "stable-device-values";
@@ -255,6 +257,30 @@
         return false;
     }
 
+    public float getBrightness(DisplayDevice device) {
+        if (device == null || !device.hasStableUniqueId()) {
+            return Float.NaN;
+        }
+        final DisplayState state = getDisplayState(device.getUniqueId(), false);
+        if (state == null) {
+            return Float.NaN;
+        }
+        return state.getBrightness();
+    }
+
+    public boolean setBrightness(DisplayDevice displayDevice, float brightness) {
+        final String displayDeviceUniqueId = displayDevice.getUniqueId();
+        if (!displayDevice.hasStableUniqueId() || displayDeviceUniqueId == null) {
+            return false;
+        }
+        final DisplayState state = getDisplayState(displayDeviceUniqueId, true);
+        if (state.setBrightness(brightness)) {
+            setDirty();
+            return true;
+        }
+        return false;
+    }
+
     public Point getStableDisplaySize() {
         loadIfNeeded();
         return mStableDeviceValues.getDisplaySize();
@@ -473,6 +499,7 @@
 
     private static final class DisplayState {
         private int mColorMode;
+        private float mBrightness;
 
         public boolean setColorMode(int colorMode) {
             if (colorMode == mColorMode) {
@@ -486,14 +513,33 @@
             return mColorMode;
         }
 
+        public boolean setBrightness(float brightness) {
+            if (brightness == mBrightness) {
+                return false;
+            }
+            mBrightness = brightness;
+            return true;
+        }
+
+        public float getBrightness() {
+            return mBrightness;
+        }
+
+
         public void loadFromXml(TypedXmlPullParser parser)
                 throws IOException, XmlPullParserException {
             final int outerDepth = parser.getDepth();
 
             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
-                if (parser.getName().equals(TAG_COLOR_MODE)) {
-                    String value = parser.nextText();
-                    mColorMode = Integer.parseInt(value);
+                switch (parser.getName()) {
+                    case TAG_COLOR_MODE:
+                        String value = parser.nextText();
+                        mColorMode = Integer.parseInt(value);
+                        break;
+                    case TAG_BRIGHTNESS_VALUE:
+                        String brightness = parser.nextText();
+                        mBrightness = Float.parseFloat(brightness);
+                        break;
                 }
             }
         }
@@ -502,10 +548,15 @@
             serializer.startTag(null, TAG_COLOR_MODE);
             serializer.text(Integer.toString(mColorMode));
             serializer.endTag(null, TAG_COLOR_MODE);
+            serializer.startTag(null, TAG_BRIGHTNESS_VALUE);
+            serializer.text(Float.toString(mBrightness));
+            serializer.endTag(null, TAG_BRIGHTNESS_VALUE);
+
         }
 
         public void dump(final PrintWriter pw, final String prefix) {
             pw.println(prefix + "ColorMode=" + mColorMode);
+            pw.println(prefix + "BrightnessValue=" + mBrightness);
         }
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index c9364c6..27f8fd3 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4830,6 +4830,9 @@
                 setInputMethodEnabledLocked(defaultImiId, true);
             }
         }
+
+        updateDefaultVoiceImeIfNeededLocked();
+
         // Here is not the perfect place to reset the switching controller. Ideally
         // mSwitchingController and mSettings should be able to share the same state.
         // TODO: Make sure that mSwitchingController and mSettings are sharing the
@@ -4842,6 +4845,37 @@
                 mSettings.getCurrentUserId(), 0 /* unused */, inputMethodList).sendToTarget();
     }
 
+    @GuardedBy("mMethodMap")
+    private void updateDefaultVoiceImeIfNeededLocked() {
+        final String systemSpeechRecognizer =
+                mContext.getString(com.android.internal.R.string.config_systemSpeechRecognizer);
+        final String currentDefaultVoiceImeId = mSettings.getDefaultVoiceInputMethod();
+        final InputMethodInfo newSystemVoiceIme = InputMethodUtils.chooseSystemVoiceIme(
+                mMethodMap, systemSpeechRecognizer, currentDefaultVoiceImeId);
+        if (newSystemVoiceIme == null) {
+            if (DEBUG) {
+                Slog.i(TAG, "Found no valid default Voice IME. If the user is still locked,"
+                        + " this may be expected.");
+            }
+            // Clear DEFAULT_VOICE_INPUT_METHOD when necessary.  Note that InputMethodSettings
+            // does not update the actual Secure Settings until the user is unlocked.
+            if (!TextUtils.isEmpty(currentDefaultVoiceImeId)) {
+                mSettings.putDefaultVoiceInputMethod("");
+                // We don't support disabling the voice ime when a package is removed from the
+                // config.
+            }
+            return;
+        }
+        if (TextUtils.equals(currentDefaultVoiceImeId, newSystemVoiceIme.getId())) {
+            return;
+        }
+        if (DEBUG) {
+            Slog.i(TAG, "Enabling the default Voice IME:" + newSystemVoiceIme);
+        }
+        setInputMethodEnabledLocked(newSystemVoiceIme.getId(), true);
+        mSettings.putDefaultVoiceInputMethod(newSystemVoiceIme.getId());
+    }
+
     // ----------------------------------------------------------------------
 
     private void showInputMethodAndSubtypeEnabler(String inputMethodId) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 0e908d4..ac3c31d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -337,6 +337,52 @@
         return getDefaultEnabledImes(context, imis, false /* onlyMinimum */);
     }
 
+    /**
+     * Chooses an eligible system voice IME from the given IMEs.
+     *
+     * @param methodMap Map from the IME ID to {@link InputMethodInfo}.
+     * @param systemSpeechRecognizerPackageName System speech recognizer configured by the system
+     *                                          config.
+     * @param currentDefaultVoiceImeId IME ID currently set to
+     *                                 {@link Settings.Secure#DEFAULT_VOICE_INPUT_METHOD}
+     * @return {@link InputMethodInfo} that is found in {@code methodMap} and most suitable for
+     *                                 the system voice IME.
+     */
+    @Nullable
+    static InputMethodInfo chooseSystemVoiceIme(
+            @NonNull ArrayMap<String, InputMethodInfo> methodMap,
+            @Nullable String systemSpeechRecognizerPackageName,
+            @Nullable String currentDefaultVoiceImeId) {
+        if (TextUtils.isEmpty(systemSpeechRecognizerPackageName)) {
+            return null;
+        }
+        final InputMethodInfo defaultVoiceIme = methodMap.get(currentDefaultVoiceImeId);
+        // If the config matches the package of the setting, use the current one.
+        if (defaultVoiceIme != null && defaultVoiceIme.isSystem()
+                && defaultVoiceIme.getPackageName().equals(systemSpeechRecognizerPackageName)) {
+            return defaultVoiceIme;
+        }
+        InputMethodInfo firstMatchingIme = null;
+        final int methodCount = methodMap.size();
+        for (int i = 0; i < methodCount; ++i) {
+            final InputMethodInfo imi = methodMap.valueAt(i);
+            if (!imi.isSystem()) {
+                continue;
+            }
+            if (!TextUtils.equals(imi.getPackageName(), systemSpeechRecognizerPackageName)) {
+                continue;
+            }
+            if (firstMatchingIme != null) {
+                Slog.e(TAG, "At most one InputMethodService can be published in "
+                        + "systemSpeechRecognizer: " + systemSpeechRecognizerPackageName
+                        + ". Ignoring all of them.");
+                return null;
+            }
+            firstMatchingIme = imi;
+        }
+        return firstMatchingIme;
+    }
+
     static boolean containsSubtypeOf(InputMethodInfo imi, @Nullable Locale locale,
             boolean checkCountry, String mode) {
         if (locale == null) {
@@ -1233,6 +1279,22 @@
             return imi;
         }
 
+        void putDefaultVoiceInputMethod(String imeId) {
+            if (DEBUG) {
+                Slog.d(TAG, "putDefaultVoiceInputMethodStr: " + imeId + ", " + mCurrentUserId);
+            }
+            putString(Settings.Secure.DEFAULT_VOICE_INPUT_METHOD, imeId);
+        }
+
+        @Nullable
+        String getDefaultVoiceInputMethod() {
+            final String imi = getString(Settings.Secure.DEFAULT_VOICE_INPUT_METHOD, null);
+            if (DEBUG) {
+                Slog.d(TAG, "getDefaultVoiceInputMethodStr: " + imi);
+            }
+            return imi;
+        }
+
         boolean isSubtypeSelected() {
             return getSelectedInputMethodSubtypeHashCode() != NOT_A_SUBTYPE_ID;
         }
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index f173fc7..4d302b1 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -259,13 +259,16 @@
             BroadcastReceiver wifiReceiver = new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
-                    if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) {
+                    if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())
+                            || WifiManager.ACTION_WIFI_SCAN_AVAILABILITY_CHANGED.equals(
+                                intent.getAction())) {
                         sendWifiSettingUpdate(false /* forceUpdate */);
                     }
                 }
             };
             IntentFilter filter = new IntentFilter();
             filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+            filter.addAction(WifiManager.ACTION_WIFI_SCAN_AVAILABILITY_CHANGED);
             mContext.registerReceiver(wifiReceiver, filter);
 
             mContext.getContentResolver().registerContentObserver(
@@ -298,7 +301,7 @@
             mSensorPrivacyManagerInternal.addSensorPrivacyListenerForAllUsers(
                     SensorPrivacyManager.Sensors.MICROPHONE, (userId, enabled) -> {
                         if (userId == getCurrentUserId()) {
-                            Log.d(TAG, "User: " + userId + " enabled: " + enabled);
+                            Log.d(TAG, "User: " + userId + "mic privacy: " + enabled);
                             sendMicrophoneDisableSettingUpdate(enabled);
                         }
                 });
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 3245fdf..7be47a4 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -324,8 +324,11 @@
         }
 
         public void onMicrophoneDisableSettingChanged(boolean enabled) {
-            sendSettingChanged(android.hardware.contexthub.V1_2.Setting.GLOBAL_MIC_DISABLE,
-                    enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
+            // The SensorPrivacyManager reports if microphone privacy was enabled,
+            // which translates to microphone access being disabled (and vice-versa).
+            // With this in mind, we flip the argument before piping it to CHRE.
+            sendSettingChanged(android.hardware.contexthub.V1_2.Setting.MICROPHONE,
+                    enabled ? SettingValue.DISABLED : SettingValue.ENABLED);
         }
 
         private void sendSettingChanged(byte setting, byte newValue) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 4299d9e..3859285 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -422,8 +422,8 @@
     private static final int MSG_LIMIT_REACHED = 5;
     private static final int MSG_RESTRICT_BACKGROUND_CHANGED = 6;
     private static final int MSG_ADVISE_PERSIST_THRESHOLD = 7;
-    private static final int MSG_UPDATE_INTERFACE_QUOTA = 10;
-    private static final int MSG_REMOVE_INTERFACE_QUOTA = 11;
+    private static final int MSG_UPDATE_INTERFACE_QUOTAS = 10;
+    private static final int MSG_REMOVE_INTERFACE_QUOTAS = 11;
     private static final int MSG_POLICIES_CHANGED = 13;
     private static final int MSG_RESET_FIREWALL_RULES_BY_UID = 15;
     private static final int MSG_SUBSCRIPTION_OVERRIDE = 16;
@@ -2035,33 +2035,44 @@
             final boolean hasWarning = policy.warningBytes != LIMIT_DISABLED;
             final boolean hasLimit = policy.limitBytes != LIMIT_DISABLED;
             long limitBytes = Long.MAX_VALUE;
-            if (hasLimit && policy.hasCycle()) {
+            long warningBytes = Long.MAX_VALUE;
+            if ((hasLimit || hasWarning) && policy.hasCycle()) {
                 final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager
                         .cycleIterator(policy).next();
                 final long start = cycle.first.toInstant().toEpochMilli();
                 final long end = cycle.second.toInstant().toEpochMilli();
                 final long totalBytes = getTotalBytes(policy.template, start, end);
 
-                if (policy.lastLimitSnooze < start) {
+                // If the limit notification is not snoozed, the limit quota needs to be calculated.
+                if (hasLimit && policy.lastLimitSnooze < start) {
                     // remaining "quota" bytes are based on total usage in
                     // current cycle. kernel doesn't like 0-byte rules, so we
                     // set 1-byte quota and disable the radio later.
                     limitBytes = Math.max(1, policy.limitBytes - totalBytes);
                 }
+
+                // If the warning notification was snoozed by user, or the service already knows
+                // it is over warning bytes, doesn't need to calculate warning bytes.
+                if (hasWarning && policy.lastWarningSnooze < start
+                        && !policy.isOverWarning(totalBytes)) {
+                    warningBytes = Math.max(1, policy.warningBytes - totalBytes);
+                }
             }
 
-            if (hasLimit || policy.metered) {
+            if (hasWarning || hasLimit || policy.metered) {
                 if (matchingIfaces.size() > 1) {
                     // TODO: switch to shared quota once NMS supports
                     Slog.w(TAG, "shared quota unsupported; generating rule for each iface");
                 }
 
-                // Set the interface limit. For interfaces which has no cycle, or metered with
-                // no policy limit, or snoozed limit notification; we still need to put iptables
-                // rule hooks to restrict apps for data saver, so push really high quota.
+                // Set the interface warning and limit. For interfaces which has no cycle,
+                // or metered with no policy quotas, or snoozed notification; we still need to put
+                // iptables rule hooks to restrict apps for data saver, so push really high quota.
+                // TODO: Push NetworkStatsProvider.QUOTA_UNLIMITED instead of Long.MAX_VALUE to
+                //  providers.
                 for (int j = matchingIfaces.size() - 1; j >= 0; j--) {
                     final String iface = matchingIfaces.valueAt(j);
-                    setInterfaceQuotaAsync(iface, limitBytes);
+                    setInterfaceQuotasAsync(iface, warningBytes, limitBytes);
                     newMeteredIfaces.add(iface);
                 }
             }
@@ -2084,7 +2095,7 @@
                 for (int j = matchingIfaces.size() - 1; j >= 0; j--) {
                     final String iface = matchingIfaces.valueAt(j);
                     if (!newMeteredIfaces.contains(iface)) {
-                        setInterfaceQuotaAsync(iface, Long.MAX_VALUE);
+                        setInterfaceQuotasAsync(iface, Long.MAX_VALUE, Long.MAX_VALUE);
                         newMeteredIfaces.add(iface);
                     }
                 }
@@ -2096,7 +2107,7 @@
             for (int i = mMeteredIfaces.size() - 1; i >= 0; i--) {
                 final String iface = mMeteredIfaces.valueAt(i);
                 if (!newMeteredIfaces.contains(iface)) {
-                    removeInterfaceQuotaAsync(iface);
+                    removeInterfaceQuotasAsync(iface);
                 }
             }
             mMeteredIfaces = newMeteredIfaces;
@@ -5036,19 +5047,20 @@
                     mNetworkStats.advisePersistThreshold(persistThreshold);
                     return true;
                 }
-                case MSG_UPDATE_INTERFACE_QUOTA: {
-                    final String iface = (String) msg.obj;
-                    // int params need to be stitched back into a long
-                    final long quota = ((long) msg.arg1 << 32) | (msg.arg2 & 0xFFFFFFFFL);
-                    removeInterfaceQuota(iface);
-                    setInterfaceQuota(iface, quota);
-                    mNetworkStats.setStatsProviderLimitAsync(iface, quota);
+                case MSG_UPDATE_INTERFACE_QUOTAS: {
+                    final IfaceQuotas val = (IfaceQuotas) msg.obj;
+                    // TODO: Consider set a new limit before removing the original one.
+                    removeInterfaceLimit(val.iface);
+                    setInterfaceLimit(val.iface, val.limit);
+                    mNetworkStats.setStatsProviderWarningAndLimitAsync(val.iface, val.warning,
+                            val.limit);
                     return true;
                 }
-                case MSG_REMOVE_INTERFACE_QUOTA: {
+                case MSG_REMOVE_INTERFACE_QUOTAS: {
                     final String iface = (String) msg.obj;
-                    removeInterfaceQuota(iface);
-                    mNetworkStats.setStatsProviderLimitAsync(iface, QUOTA_UNLIMITED);
+                    removeInterfaceLimit(iface);
+                    mNetworkStats.setStatsProviderWarningAndLimitAsync(iface, QUOTA_UNLIMITED,
+                            QUOTA_UNLIMITED);
                     return true;
                 }
                 case MSG_RESET_FIREWALL_RULES_BY_UID: {
@@ -5196,15 +5208,32 @@
         }
     }
 
-    private void setInterfaceQuotaAsync(String iface, long quotaBytes) {
-        // long quotaBytes split up into two ints to fit in message
-        mHandler.obtainMessage(MSG_UPDATE_INTERFACE_QUOTA, (int) (quotaBytes >> 32),
-                (int) (quotaBytes & 0xFFFFFFFF), iface).sendToTarget();
+    private static final class IfaceQuotas {
+        @NonNull public final String iface;
+        // Warning and limit bytes of interface qutoas, could be QUOTA_UNLIMITED or Long.MAX_VALUE
+        // if not set. 0 is not acceptable since kernel doesn't like 0-byte rules.
+        public final long warning;
+        public final long limit;
+
+        private IfaceQuotas(@NonNull String iface, long warning, long limit) {
+            this.iface = iface;
+            this.warning = warning;
+            this.limit = limit;
+        }
     }
 
-    private void setInterfaceQuota(String iface, long quotaBytes) {
+    private void setInterfaceQuotasAsync(@NonNull String iface,
+            long warningBytes, long limitBytes) {
+        mHandler.obtainMessage(MSG_UPDATE_INTERFACE_QUOTAS,
+                new IfaceQuotas(iface, warningBytes, limitBytes)).sendToTarget();
+    }
+
+    private void setInterfaceLimit(String iface, long limitBytes) {
         try {
-            mNetworkManager.setInterfaceQuota(iface, quotaBytes);
+            // For legacy design the data warning is covered by global alert, where the
+            // kernel will notify upper layer for a small amount of change of traffic
+            // statistics. Thus, passing warning is not needed.
+            mNetworkManager.setInterfaceQuota(iface, limitBytes);
         } catch (IllegalStateException e) {
             Log.wtf(TAG, "problem setting interface quota", e);
         } catch (RemoteException e) {
@@ -5212,11 +5241,11 @@
         }
     }
 
-    private void removeInterfaceQuotaAsync(String iface) {
-        mHandler.obtainMessage(MSG_REMOVE_INTERFACE_QUOTA, iface).sendToTarget();
+    private void removeInterfaceQuotasAsync(String iface) {
+        mHandler.obtainMessage(MSG_REMOVE_INTERFACE_QUOTAS, iface).sendToTarget();
     }
 
-    private void removeInterfaceQuota(String iface) {
+    private void removeInterfaceLimit(String iface) {
         try {
             mNetworkManager.removeInterfaceQuota(iface);
         } catch (IllegalStateException e) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java b/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java
index 0cb0bc2c..0e9a9da 100644
--- a/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java
@@ -37,8 +37,9 @@
     public abstract void forceUpdate();
 
     /**
-     * Set the quota limit to all registered custom network stats providers.
+     * Set the warning and limit to all registered custom network stats providers.
      * Note that invocation of any interface will be sent to all providers.
      */
-    public abstract void setStatsProviderLimitAsync(@NonNull String iface, long quota);
+    public abstract void setStatsProviderWarningAndLimitAsync(@NonNull String iface, long warning,
+            long limit);
 }
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 785e487..de5aae0 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -1674,11 +1674,14 @@
         }
 
         @Override
-        public void setStatsProviderLimitAsync(@NonNull String iface, long quota) {
-            if (LOGV) Slog.v(TAG, "setStatsProviderLimitAsync(" + iface + "," + quota + ")");
-            // TODO: Set warning accordingly.
+        public void setStatsProviderWarningAndLimitAsync(
+                @NonNull String iface, long warning, long limit) {
+            if (LOGV) {
+                Slog.v(TAG, "setStatsProviderWarningAndLimitAsync("
+                        + iface + "," + warning + "," + limit + ")");
+            }
             invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
-                    NetworkStatsProvider.QUOTA_UNLIMITED, quota));
+                    warning, limit));
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 8c3c423..fd8ec7f 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -2347,7 +2347,7 @@
                 }
                 final List<ShortcutInfo> page = new ArrayList<>(results.size());
                 for (SearchResult result : results) {
-                    final ShortcutInfo si = new AppSearchShortcutInfo(result.getDocument())
+                    final ShortcutInfo si = new AppSearchShortcutInfo(result.getGenericDocument())
                             .toShortcutInfo(mShortcutUser.getUserId());
                     page.add(si);
                 }
@@ -2398,8 +2398,7 @@
             @NonNull final Function<AppSearchSession, CompletableFuture<T>> cb) {
         final long callingIdentity = Binder.clearCallingIdentity();
         final AppSearchManager.SearchContext searchContext =
-                new AppSearchManager.SearchContext.Builder()
-                        .setDatabaseName(getPackageName()).build();
+                new AppSearchManager.SearchContext.Builder(getPackageName()).build();
         final AppSearchSession session;
         try {
             session = ConcurrentUtils.waitForFutureNoInterrupt(
@@ -2408,6 +2407,8 @@
             mAppSearchSession = session;
             return cb.apply(mAppSearchSession);
         } catch (Exception e) {
+            Slog.e(TAG, "Failed to initiate app search for shortcut package "
+                    + getPackageName() + " user " + mShortcutUser.getUserId(), e);
             return AndroidFuture.completedFuture(null);
         } finally {
             Binder.restoreCallingIdentity(callingIdentity);
@@ -2427,7 +2428,8 @@
                             AppSearchShortcutInfo.SCHEMA_TYPE, true, pi);
         }
         final AndroidFuture<AppSearchSession> future = new AndroidFuture<>();
-        session.setSchema(schemaBuilder.build(), mShortcutUser.mExecutor, result -> {
+        session.setSchema(
+                schemaBuilder.build(), mShortcutUser.mExecutor, mShortcutUser.mExecutor, result -> {
             if (!result.isSuccess()) {
                 future.completeExceptionally(
                         new IllegalArgumentException(result.getErrorMessage()));
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 0b21487..e1d1c26 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -342,7 +342,7 @@
 
     private final IPackageManager mIPackageManager;
     private final PackageManagerInternal mPackageManagerInternal;
-    private final UserManagerInternal mUserManagerInternal;
+    final UserManagerInternal mUserManagerInternal;
     private final UsageStatsManagerInternal mUsageStatsManagerInternal;
     private final ActivityManagerInternal mActivityManagerInternal;
     private final IUriGrantsManager mUriGrantsManager;
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 51cb995..ce49d88 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -722,6 +722,12 @@
             future.completeExceptionally(new RuntimeException("app search manager is null"));
             return future;
         }
+        if (!mService.mUserManagerInternal.isUserUnlockingOrUnlocked(getUserId())) {
+            // In rare cases the user might be stopped immediate after it started, in these cases
+            // any on-going session will need to be abandoned.
+            future.completeExceptionally(new RuntimeException("User " + getUserId() + " is "));
+            return future;
+        }
         final long callingIdentity = Binder.clearCallingIdentity();
         try {
             mAppSearchManager.createSearchSession(searchContext, mExecutor, result -> {
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 629f120..21e44ab 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -407,7 +407,7 @@
             retProcs.put(proc.getName(),
                     new ProcessInfo(proc.getName(), new ArraySet<>(proc.getDeniedPermissions()),
                             proc.getGwpAsanMode(), proc.getMemtagMode(),
-                            proc.getNativeHeapZeroInit()));
+                            proc.getNativeHeapZeroInitialized()));
         }
         return retProcs;
     }
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 f0d54b4..e16c67f 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1729,6 +1729,14 @@
         @Override
         public void grantPermission(@NonNull String permission, @NonNull PackageInfo pkg,
                 @NonNull UserHandle user) {
+            if (PermissionManager.DEBUG_TRACE_GRANTS
+                    && PermissionManager.shouldTraceGrant(
+                    pkg.packageName, permission, user.getIdentifier())) {
+                Log.i(PermissionManager.LOG_TAG_TRACE_GRANTS,
+                        "PregrantPolicy is granting " + pkg.packageName + " "
+                                + permission + " for user " + user.getIdentifier(),
+                        new RuntimeException());
+            }
             PermissionState state = getPermissionState(permission, pkg, user);
             state.initGranted();
             state.newGranted = true;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 3244c44..e63426f 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1338,7 +1338,7 @@
             boolean overridePolicy, int callingUid, final int userId, PermissionCallback callback) {
         if (PermissionManager.DEBUG_TRACE_GRANTS
                 && PermissionManager.shouldTraceGrant(packageName, permName, userId)) {
-            Log.i(TAG, "System is granting " + packageName + " "
+            Log.i(PermissionManager.LOG_TAG_TRACE_GRANTS, "System is granting " + packageName + " "
                     + permName + " for user " + userId + " on behalf of uid " + callingUid
                     + " " + mPackageManagerInt.getNameForUid(callingUid),
                     new RuntimeException());
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
index f4bcd3e..0b48b5c 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
@@ -70,11 +70,8 @@
                 break;
             default:
                 if (!proxy.isCallerVerifier(callingUid)) {
-                    mContext.enforcePermission(
-                            android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION,
-                            Binder.getCallingPid(), callingUid,
-                            "Caller " + callingUid
-                                    + " is not allowed to query domain verification state");
+                    throw new SecurityException(
+                            "Caller is not allowed to query domain verification state");
                 }
 
                 mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES,
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 8075bdb..1b2ff08 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -148,6 +148,8 @@
     @NonNull
     private DomainVerificationProxy mProxy = new DomainVerificationProxyUnavailable();
 
+    private boolean mCanSendBroadcasts;
+
     public DomainVerificationService(@NonNull Context context, @NonNull SystemConfig systemConfig,
             @NonNull PlatformCompat platformCompat) {
         super(context);
@@ -181,11 +183,18 @@
     @Override
     public void onBootPhase(int phase) {
         super.onBootPhase(phase);
-        if (phase != SystemService.PHASE_BOOT_COMPLETED || !hasRealVerifier()) {
+        if (!hasRealVerifier()) {
             return;
         }
 
-        verifyPackages(null, false);
+        switch (phase) {
+            case PHASE_ACTIVITY_MANAGER_READY:
+                mCanSendBroadcasts = true;
+                break;
+            case PHASE_BOOT_COMPLETED:
+                verifyPackages(null, false);
+                break;
+        }
     }
 
     @Override
@@ -858,7 +867,7 @@
         }
 
         if (sendBroadcast) {
-            sendBroadcastForPackage(pkgName);
+            sendBroadcast(pkgName);
         }
     }
 
@@ -937,7 +946,7 @@
         }
 
         if (sendBroadcast && hasAutoVerifyDomains) {
-            sendBroadcastForPackage(pkgName);
+            sendBroadcast(pkgName);
         }
     }
 
@@ -1098,8 +1107,19 @@
         return mCollector;
     }
 
-    private void sendBroadcastForPackage(@NonNull String packageName) {
-        mProxy.sendBroadcastForPackages(Collections.singleton(packageName));
+    private void sendBroadcast(@NonNull String packageName) {
+        sendBroadcast(Collections.singleton(packageName));
+    }
+
+    private void sendBroadcast(@NonNull Set<String> packageNames) {
+        if (!mCanSendBroadcasts) {
+            // If the system cannot send broadcasts, it's probably still in boot, so dropping this
+            // request should be fine. The verification agent should re-scan packages once boot
+            // completes.
+            return;
+        }
+
+        mProxy.sendBroadcastForPackages(packageNames);
     }
 
     private boolean hasRealVerifier() {
@@ -1183,7 +1203,7 @@
         }
 
         if (!packagesToBroadcast.isEmpty()) {
-            mProxy.sendBroadcastForPackages(packagesToBroadcast);
+            sendBroadcast(packagesToBroadcast);
         }
     }
 
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 1b5bb95..f185464 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2622,23 +2622,19 @@
                                 Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
                                 UserHandle.USER_CURRENT_OR_SELF);
                     }
-                    float minFloat = mPowerManager.getBrightnessConstraint(
+                    float min = mPowerManager.getBrightnessConstraint(
                             PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
-                    float maxFloat = mPowerManager.getBrightnessConstraint(
+                    float max = mPowerManager.getBrightnessConstraint(
                             PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
-                    float stepFloat = (maxFloat - minFloat) / BRIGHTNESS_STEPS * direction;
-                    float brightnessFloat = Settings.System.getFloatForUser(
-                            mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_FLOAT,
-                            mContext.getDisplay().getBrightnessDefault(),
-                            UserHandle.USER_CURRENT_OR_SELF);
-                    brightnessFloat += stepFloat;
+                    float step = (max - min) / BRIGHTNESS_STEPS * direction;
+                    int screenDisplayId = displayId < 0 ? DEFAULT_DISPLAY : displayId;
+                    float brightness = mDisplayManager.getBrightness(screenDisplayId);
+                    brightness += step;
                     // Make sure we don't go beyond the limits.
-                    brightnessFloat = Math.min(maxFloat, brightnessFloat);
-                    brightnessFloat = Math.max(minFloat, brightnessFloat);
+                    brightness = Math.min(max, brightness);
+                    brightness = Math.max(min, brightness);
 
-                    Settings.System.putFloatForUser(mContext.getContentResolver(),
-                            Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightnessFloat,
-                            UserHandle.USER_CURRENT_OR_SELF);
+                    mDisplayManager.setBrightness(screenDisplayId, brightness);
                     startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG),
                             UserHandle.CURRENT_OR_SELF);
                 }
diff --git a/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java b/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java
index 2fcd178..9732eba3 100644
--- a/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java
+++ b/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java
@@ -248,6 +248,30 @@
         return false;
     }
 
+    long getLastUserActivityTimeLocked(int groupId) {
+        return mDisplayGroupInfos.get(groupId).lastUserActivityTime;
+    }
+
+    long getLastUserActivityTimeNoChangeLightsLocked(int groupId) {
+        return mDisplayGroupInfos.get(groupId).lastUserActivityTimeNoChangeLights;
+    }
+
+    int getUserActivitySummaryLocked(int groupId) {
+        return mDisplayGroupInfos.get(groupId).userActivitySummary;
+    }
+
+    void setLastUserActivityTimeLocked(int groupId, long time) {
+        mDisplayGroupInfos.get(groupId).lastUserActivityTime = time;
+    }
+
+    void setLastUserActivityTimeNoChangeLightsLocked(int groupId, long time) {
+        mDisplayGroupInfos.get(groupId).lastUserActivityTimeNoChangeLights = time;
+    }
+
+    void setUserActivitySummaryLocked(int groupId, int summary) {
+        mDisplayGroupInfos.get(groupId).userActivitySummary = summary;
+    }
+
     /**
      * Interface through which an interested party may be informed of {@link DisplayGroup} events.
      */
@@ -265,6 +289,9 @@
         public boolean ready;
         public long lastPowerOnTime;
         public boolean sandmanSummoned;
+        public long lastUserActivityTime;
+        public long lastUserActivityTimeNoChangeLights;
+        public int userActivitySummary;
 
         /** {@code true} if this DisplayGroup supports dreaming; otherwise {@code false}. */
         public boolean supportsSandman;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index d2a4cd6..54d5b7a 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -29,6 +29,7 @@
 import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
 import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
 import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
+import static android.os.PowerManagerInternal.wakefulnessToString;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -92,6 +93,7 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
+import android.view.DisplayInfo;
 import android.view.KeyEvent;
 
 import com.android.internal.annotations.GuardedBy;
@@ -180,8 +182,8 @@
     private static final int DIRTY_VR_MODE_CHANGED = 1 << 13;
     // Dirty bit: attentive timer may have timed out
     private static final int DIRTY_ATTENTIVE = 1 << 14;
-    // Dirty bit: display group power state has changed
-    private static final int DIRTY_DISPLAY_GROUP_POWER_UPDATED = 1 << 16;
+    // Dirty bit: display group wakefulness has changed
+    private static final int DIRTY_DISPLAY_GROUP_WAKEFULNESS = 1 << 16;
 
     // Summarizes the state of all active wakelocks.
     private static final int WAKE_LOCK_CPU = 1 << 0;
@@ -338,10 +340,6 @@
     private @WakeReason int mLastWakeReason;
     private int mLastSleepReason;
 
-    // Timestamp of the last call to user activity.
-    private long mLastUserActivityTime;
-    private long mLastUserActivityTimeNoChangeLights;
-
     // Timestamp of last time power boost interaction was sent.
     private long mLastInteractivePowerHintTime;
 
@@ -349,9 +347,6 @@
     private long mLastScreenBrightnessBoostTime;
     private boolean mScreenBrightnessBoostInProgress;
 
-    // A bitfield that summarizes the effect of the user activity timer.
-    private int mUserActivitySummary;
-
     // Manages the desired power state of displays. The actual state may lag behind the
     // requested because it is updated asynchronously by the display power controller.
     private DisplayGroupPowerStateMapper mDisplayGroupPowerStateMapper;
@@ -634,6 +629,13 @@
         public void onDisplayGroupEventLocked(int event, int groupId) {
             final int oldWakefulness = getWakefulnessLocked();
             final int newWakefulness = mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked();
+
+            if (event == DISPLAY_GROUP_ADDED && newWakefulness == WAKEFULNESS_AWAKE) {
+                // Kick user activity to prevent newly added group from timing out instantly.
+                userActivityNoUpdateLocked(groupId, mClock.uptimeMillis(),
+                        PowerManager.USER_ACTIVITY_EVENT_OTHER, /* flags= */ 0, Process.SYSTEM_UID);
+            }
+
             if (oldWakefulness != newWakefulness) {
                 final int reason;
                 switch (newWakefulness) {
@@ -656,7 +658,7 @@
                         mContext.getOpPackageName(), "groupId: " + groupId);
             }
 
-            mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
+            mDirty |= DIRTY_DISPLAY_GROUP_WAKEFULNESS;
             updatePowerStateLocked();
         }
     }
@@ -1059,6 +1061,10 @@
     private void onFlip(boolean isFaceDown) {
         long millisUntilNormalTimeout = 0;
         synchronized (mLock) {
+            if (!mBootCompleted) {
+                return;
+            }
+
             mIsFaceDown = isFaceDown;
             if (isFaceDown) {
                 final long currentTime = mClock.uptimeMillis();
@@ -1066,8 +1072,9 @@
                 final long sleepTimeout = getSleepTimeoutLocked(-1L);
                 final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout, -1L);
                 millisUntilNormalTimeout =
-                        mLastUserActivityTime + screenOffTimeout - mClock.uptimeMillis();
-                userActivityInternal(mClock.uptimeMillis(),
+                        mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(
+                                Display.DEFAULT_DISPLAY_GROUP) + screenOffTimeout - currentTime;
+                userActivityInternal(Display.DEFAULT_DISPLAY, currentTime,
                         PowerManager.USER_ACTIVITY_EVENT_FACE_DOWN, /* flags= */0,
                         Process.SYSTEM_UID);
             }
@@ -1645,12 +1652,28 @@
 
     // Called from native code.
     private void userActivityFromNative(long eventTime, int event, int displayId, int flags) {
-        userActivityInternal(eventTime, event, flags, Process.SYSTEM_UID);
+        userActivityInternal(displayId, eventTime, event, flags, Process.SYSTEM_UID);
     }
 
-    private void userActivityInternal(long eventTime, int event, int flags, int uid) {
+    private void userActivityInternal(int displayId, long eventTime, int event, int flags,
+            int uid) {
         synchronized (mLock) {
-            if (userActivityNoUpdateLocked(eventTime, event, flags, uid)) {
+            if (displayId == Display.INVALID_DISPLAY) {
+                if (userActivityNoUpdateLocked(eventTime, event, flags, uid)) {
+                    updatePowerStateLocked();
+                }
+                return;
+            }
+
+            final DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(displayId);
+            if (displayInfo == null) {
+                return;
+            }
+            final int groupId = displayInfo.displayGroupId;
+            if (groupId == Display.INVALID_DISPLAY_GROUP) {
+                return;
+            }
+            if (userActivityNoUpdateLocked(groupId, eventTime, event, flags, uid)) {
                 updatePowerStateLocked();
             }
         }
@@ -1658,7 +1681,7 @@
 
     private void onUserAttention() {
         synchronized (mLock) {
-            if (userActivityNoUpdateLocked(mClock.uptimeMillis(),
+            if (userActivityNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, mClock.uptimeMillis(),
                     PowerManager.USER_ACTIVITY_EVENT_ATTENTION, 0 /* flags */,
                     Process.SYSTEM_UID)) {
                 updatePowerStateLocked();
@@ -1667,10 +1690,22 @@
     }
 
     private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) {
+        boolean updatePowerState = false;
+        for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+            if (userActivityNoUpdateLocked(id, eventTime, event, flags, uid)) {
+                updatePowerState = true;
+            }
+        }
+
+        return updatePowerState;
+    }
+
+    private boolean userActivityNoUpdateLocked(int groupId, long eventTime, int event, int flags,
+            int uid) {
         if (DEBUG_SPEW) {
-            Slog.d(TAG, "userActivityNoUpdateLocked: eventTime=" + eventTime
-                    + ", event=" + event + ", flags=0x" + Integer.toHexString(flags)
-                    + ", uid=" + uid);
+            Slog.d(TAG, "userActivityNoUpdateLocked: groupId=" + groupId
+                    + ", eventTime=" + eventTime + ", event=" + event
+                    + ", flags=0x" + Integer.toHexString(flags) + ", uid=" + uid);
         }
 
         if (eventTime < mLastSleepTime || eventTime < mLastWakeTime || !mSystemReady) {
@@ -1692,8 +1727,9 @@
                 mOverriddenTimeout = -1;
             }
 
-            if (getWakefulnessLocked() == WAKEFULNESS_ASLEEP
-                    || getWakefulnessLocked() == WAKEFULNESS_DOZING
+            final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId);
+            if (wakefulness == WAKEFULNESS_ASLEEP
+                    || wakefulness == WAKEFULNESS_DOZING
                     || (flags & PowerManager.USER_ACTIVITY_FLAG_INDIRECT) != 0) {
                 return false;
             }
@@ -1701,9 +1737,13 @@
             maybeUpdateForegroundProfileLastActivityLocked(eventTime);
 
             if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) {
-                if (eventTime > mLastUserActivityTimeNoChangeLights
-                        && eventTime > mLastUserActivityTime) {
-                    mLastUserActivityTimeNoChangeLights = eventTime;
+                if (eventTime
+                        > mDisplayGroupPowerStateMapper.getLastUserActivityTimeNoChangeLightsLocked(
+                        groupId)
+                        && eventTime > mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(
+                        groupId)) {
+                    mDisplayGroupPowerStateMapper.setLastUserActivityTimeNoChangeLightsLocked(
+                            groupId, eventTime);
                     mDirty |= DIRTY_USER_ACTIVITY;
                     if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {
                         mDirty |= DIRTY_QUIESCENT;
@@ -1712,8 +1752,9 @@
                     return true;
                 }
             } else {
-                if (eventTime > mLastUserActivityTime) {
-                    mLastUserActivityTime = eventTime;
+                if (eventTime > mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(
+                        groupId)) {
+                    mDisplayGroupPowerStateMapper.setLastUserActivityTimeLocked(groupId, eventTime);
                     mDirty |= DIRTY_USER_ACTIVITY;
                     if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {
                         mDirty |= DIRTY_QUIESCENT;
@@ -1778,7 +1819,6 @@
             setWakefulnessLocked(groupId, WAKEFULNESS_AWAKE, eventTime, uid, reason, opUid,
                     opPackageName, details);
             mDisplayGroupPowerStateMapper.setLastPowerOnTimeLocked(groupId, eventTime);
-            mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
         }
@@ -1829,7 +1869,6 @@
             if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {
                 reallySleepDisplayGroupNoUpdateLocked(groupId, eventTime, uid);
             }
-            mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
         }
@@ -1862,7 +1901,6 @@
             mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, true);
             setWakefulnessLocked(groupId, WAKEFULNESS_DREAMING, eventTime, uid, /* reason= */
                     0, /* opUid= */ 0, /* opPackageName= */ null, /* details= */ null);
-            mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
 
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
@@ -1890,7 +1928,6 @@
             setWakefulnessLocked(groupId, WAKEFULNESS_ASLEEP, eventTime, uid,
                     PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,  /* opUid= */ 0,
                     /* opPackageName= */ null, /* details= */ null);
-            mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
         }
@@ -1901,8 +1938,14 @@
     void setWakefulnessLocked(int groupId, int wakefulness, long eventTime, int uid, int reason,
             int opUid, String opPackageName, String details) {
         if (mDisplayGroupPowerStateMapper.setWakefulnessLocked(groupId, wakefulness)) {
+            mDirty |= DIRTY_DISPLAY_GROUP_WAKEFULNESS;
             setGlobalWakefulnessLocked(mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked(),
                     eventTime, reason, uid, opUid, opPackageName, details);
+            if (wakefulness == WAKEFULNESS_AWAKE) {
+                // Kick user activity to prevent newly awake group from timing out instantly.
+                userActivityNoUpdateLocked(
+                        groupId, eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
+            }
         }
     }
 
@@ -1972,8 +2015,6 @@
             switch (wakefulness) {
                 case WAKEFULNESS_AWAKE:
                     mNotifier.onWakeUp(reason, details, uid, opPackageName, opUid);
-                    userActivityNoUpdateLocked(
-                            eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
                     if (sQuiescent) {
                         mDirty |= DIRTY_QUIESCENT;
                     }
@@ -2163,8 +2204,9 @@
                             "android.server.power:PLUGGED:" + mIsPowered, Process.SYSTEM_UID,
                             mContext.getOpPackageName(), Process.SYSTEM_UID);
                 }
-                userActivityNoUpdateLocked(
-                        now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
+
+                userActivityNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, now,
+                        PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
 
                 // only play charging sounds if boot is completed so charging sounds don't play
                 // with potential notification sounds
@@ -2407,106 +2449,123 @@
      */
     private void updateUserActivitySummaryLocked(long now, int dirty) {
         // Update the status of the user activity timeout timer.
-        if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY
-                | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0) {
-            mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);
+        if ((dirty & (DIRTY_DISPLAY_GROUP_WAKEFULNESS | DIRTY_WAKE_LOCKS
+                | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) == 0) {
+            return;
+        }
+        mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);
 
-            long nextTimeout = 0;
-            if (getWakefulnessLocked() == WAKEFULNESS_AWAKE
-                    || getWakefulnessLocked() == WAKEFULNESS_DREAMING
-                    || getWakefulnessLocked() == WAKEFULNESS_DOZING) {
-                final long attentiveTimeout = getAttentiveTimeoutLocked();
-                final long sleepTimeout = getSleepTimeoutLocked(attentiveTimeout);
-                long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout,
-                        attentiveTimeout);
-                final long screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
-                screenOffTimeout =
-                        getScreenOffTimeoutWithFaceDownLocked(screenOffTimeout, screenDimDuration);
-                final boolean userInactiveOverride = mUserInactiveOverrideFromWindowManager;
-                final long nextProfileTimeout = getNextProfileTimeoutLocked(now);
-
-                mUserActivitySummary = 0;
-                if (mLastUserActivityTime >= mLastWakeTime) {
-                    nextTimeout = mLastUserActivityTime
-                            + screenOffTimeout - screenDimDuration;
-                    if (now < nextTimeout) {
-                        mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
+        final long attentiveTimeout = getAttentiveTimeoutLocked();
+        final long sleepTimeout = getSleepTimeoutLocked(attentiveTimeout);
+        long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout,
+                attentiveTimeout);
+        final long screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
+        screenOffTimeout =
+                getScreenOffTimeoutWithFaceDownLocked(screenOffTimeout, screenDimDuration);
+        final boolean userInactiveOverride = mUserInactiveOverrideFromWindowManager;
+        long nextTimeout = -1;
+        boolean hasUserActivitySummary = false;
+        for (int groupId : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+            int groupUserActivitySummary = 0;
+            long groupNextTimeout = 0;
+            if (mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId) != WAKEFULNESS_ASLEEP) {
+                final long lastUserActivityTime =
+                        mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(groupId);
+                final long lastUserActivityTimeNoChangeLights =
+                        mDisplayGroupPowerStateMapper.getLastUserActivityTimeNoChangeLightsLocked(
+                                groupId);
+                if (lastUserActivityTime >= mLastWakeTime) {
+                    groupNextTimeout = lastUserActivityTime + screenOffTimeout - screenDimDuration;
+                    if (now < groupNextTimeout) {
+                        groupUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
                     } else {
-                        nextTimeout = mLastUserActivityTime + screenOffTimeout;
-                        if (now < nextTimeout) {
-                            mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
+                        groupNextTimeout = lastUserActivityTime + screenOffTimeout;
+                        if (now < groupNextTimeout) {
+                            groupUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
                         }
                     }
                 }
-                if (mUserActivitySummary == 0
-                        && mLastUserActivityTimeNoChangeLights >= mLastWakeTime) {
-                    nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;
-                    if (now < nextTimeout) {
+                if (groupUserActivitySummary == 0
+                        && lastUserActivityTimeNoChangeLights >= mLastWakeTime) {
+                    groupNextTimeout = lastUserActivityTimeNoChangeLights + screenOffTimeout;
+                    if (now < groupNextTimeout) {
                         final DisplayPowerRequest displayPowerRequest =
-                                mDisplayGroupPowerStateMapper.getPowerRequestLocked(
-                                        Display.DEFAULT_DISPLAY_GROUP);
+                                mDisplayGroupPowerStateMapper.getPowerRequestLocked(groupId);
                         if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT
                                 || displayPowerRequest.policy == DisplayPowerRequest.POLICY_VR) {
-                            mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
+                            groupUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
                         } else if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
-                            mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
+                            groupUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
                         }
                     }
                 }
 
-                if (mUserActivitySummary == 0) {
+                if (groupUserActivitySummary == 0) {
                     if (sleepTimeout >= 0) {
-                        final long anyUserActivity = Math.max(mLastUserActivityTime,
-                                mLastUserActivityTimeNoChangeLights);
+                        final long anyUserActivity = Math.max(lastUserActivityTime,
+                                lastUserActivityTimeNoChangeLights);
                         if (anyUserActivity >= mLastWakeTime) {
-                            nextTimeout = anyUserActivity + sleepTimeout;
-                            if (now < nextTimeout) {
-                                mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
+                            groupNextTimeout = anyUserActivity + sleepTimeout;
+                            if (now < groupNextTimeout) {
+                                groupUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
                             }
                         }
                     } else {
-                        mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
-                        nextTimeout = -1;
+                        groupUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
+                        groupNextTimeout = -1;
                     }
                 }
 
-                if (mUserActivitySummary != USER_ACTIVITY_SCREEN_DREAM && userInactiveOverride) {
-                    if ((mUserActivitySummary &
+                if (groupUserActivitySummary != USER_ACTIVITY_SCREEN_DREAM
+                        && userInactiveOverride) {
+                    if ((groupUserActivitySummary &
                             (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0) {
                         // Device is being kept awake by recent user activity
-                        if (nextTimeout >= now && mOverriddenTimeout == -1) {
+                        if (mOverriddenTimeout == -1) {
                             // Save when the next timeout would have occurred
-                            mOverriddenTimeout = nextTimeout;
+                            mOverriddenTimeout = groupNextTimeout;
                         }
                     }
-                    mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
-                    nextTimeout = -1;
+                    groupUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
+                    groupNextTimeout = -1;
                 }
 
-                if ((mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
+                if ((groupUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
                         && (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) == 0) {
-                    nextTimeout = mAttentionDetector.updateUserActivity(nextTimeout,
+                    groupNextTimeout = mAttentionDetector.updateUserActivity(groupNextTimeout,
                             screenDimDuration);
                 }
 
-                if (nextProfileTimeout > 0) {
-                    nextTimeout = Math.min(nextTimeout, nextProfileTimeout);
-                }
+                hasUserActivitySummary |= groupUserActivitySummary != 0;
 
-                if (mUserActivitySummary != 0 && nextTimeout >= 0) {
-                    scheduleUserInactivityTimeout(nextTimeout);
+                if (nextTimeout == -1) {
+                    nextTimeout = groupNextTimeout;
+                } else if (groupNextTimeout != -1) {
+                    nextTimeout = Math.min(nextTimeout, groupNextTimeout);
                 }
-            } else {
-                mUserActivitySummary = 0;
             }
 
+            mDisplayGroupPowerStateMapper.setUserActivitySummaryLocked(groupId,
+                    groupUserActivitySummary);
+
             if (DEBUG_SPEW) {
-                Slog.d(TAG, "updateUserActivitySummaryLocked: mWakefulness="
-                        + PowerManagerInternal.wakefulnessToString(getWakefulnessLocked())
-                        + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
-                        + ", nextTimeout=" + TimeUtils.formatUptime(nextTimeout));
+                Slog.d(TAG, "updateUserActivitySummaryLocked: groupId=" + groupId
+                        + ", mWakefulness=" + wakefulnessToString(
+                        mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId))
+                        + ", mUserActivitySummary=0x" + Integer.toHexString(
+                        groupUserActivitySummary)
+                        + ", nextTimeout=" + TimeUtils.formatUptime(groupNextTimeout));
             }
         }
+
+        final long nextProfileTimeout = getNextProfileTimeoutLocked(now);
+        if (nextProfileTimeout > 0) {
+            nextTimeout = Math.min(nextTimeout, nextProfileTimeout);
+        }
+
+        if (hasUserActivitySummary && nextTimeout >= 0) {
+            scheduleUserInactivityTimeout(nextTimeout);
+        }
     }
 
     private void scheduleUserInactivityTimeout(long timeMs) {
@@ -2539,15 +2598,20 @@
 
     private void updateAttentiveStateLocked(long now, int dirty) {
         long attentiveTimeout = getAttentiveTimeoutLocked();
-        long goToSleepTime = mLastUserActivityTime + attentiveTimeout;
+        if (attentiveTimeout < 0) {
+            return;
+        }
+        // Attentive state only applies to the default display group.
+        long goToSleepTime = mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(
+                Display.DEFAULT_DISPLAY_GROUP) + attentiveTimeout;
         long showWarningTime = goToSleepTime - mAttentiveWarningDurationConfig;
 
         boolean warningDismissed = maybeHideInattentiveSleepWarningLocked(now, showWarningTime);
 
-        if (attentiveTimeout >= 0 && (warningDismissed
-                || (dirty & (DIRTY_ATTENTIVE | DIRTY_STAY_ON | DIRTY_SCREEN_BRIGHTNESS_BOOST
-                | DIRTY_PROXIMITY_POSITIVE | DIRTY_WAKEFULNESS | DIRTY_BOOT_COMPLETED
-                | DIRTY_SETTINGS)) != 0)) {
+        if (warningDismissed ||
+                (dirty & (DIRTY_ATTENTIVE | DIRTY_STAY_ON | DIRTY_SCREEN_BRIGHTNESS_BOOST
+                        | DIRTY_PROXIMITY_POSITIVE | DIRTY_WAKEFULNESS | DIRTY_BOOT_COMPLETED
+                        | DIRTY_SETTINGS)) != 0) {
             if (DEBUG_SPEW) {
                 Slog.d(TAG, "Updating attentive state");
             }
@@ -2601,9 +2665,12 @@
         return false;
     }
 
-    private boolean isAttentiveTimeoutExpired(long now) {
+    private boolean isAttentiveTimeoutExpired(int groupId, long now) {
         long attentiveTimeout = getAttentiveTimeoutLocked();
-        return attentiveTimeout >= 0 && now >= mLastUserActivityTime + attentiveTimeout;
+        // Attentive state only applies to the default display group.
+        return groupId == Display.DEFAULT_DISPLAY_GROUP && attentiveTimeout >= 0
+                && now >= mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(groupId)
+                + attentiveTimeout;
     }
 
     /**
@@ -2703,26 +2770,20 @@
                 | DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE
                 | DIRTY_DOCK_STATE | DIRTY_ATTENTIVE | DIRTY_SETTINGS
                 | DIRTY_SCREEN_BRIGHTNESS_BOOST)) != 0) {
-            if (getWakefulnessLocked() == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {
-                if (DEBUG_SPEW) {
-                    Slog.d(TAG, "updateWakefulnessLocked: Bed time...");
-                }
-                final long time = mClock.uptimeMillis();
-                if (isAttentiveTimeoutExpired(time)) {
-                    // TODO (b/175764389): Support per-display timeouts.
-                    for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+            final long time = mClock.uptimeMillis();
+            for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+                if (mDisplayGroupPowerStateMapper.getWakefulnessLocked(id) == WAKEFULNESS_AWAKE
+                        && isItBedTimeYetLocked(id)) {
+                    if (DEBUG_SPEW) {
+                        Slog.d(TAG, "updateWakefulnessLocked: Bed time for group " + id);
+                    }
+                    if (isAttentiveTimeoutExpired(id, time)) {
                         changed = sleepDisplayGroupNoUpdateLocked(id, time,
                                 PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
                                 PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
-                    }
-                } else if (shouldNapAtBedTimeLocked()) {
-                    // TODO (b/175764389): Support per-display timeouts.
-                    for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+                    } else if (shouldNapAtBedTimeLocked()) {
                         changed = dreamDisplayGroupNoUpdateLocked(id, time, Process.SYSTEM_UID);
-                    }
-                } else {
-                    // TODO (b/175764389): Support per-display timeouts.
-                    for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+                    } else {
                         changed = sleepDisplayGroupNoUpdateLocked(id, time,
                                 PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
                     }
@@ -2743,51 +2804,50 @@
     }
 
     /**
-     * Returns true if the device should go to sleep now.
-     * Also used when exiting a dream to determine whether we should go back
-     * to being fully awake or else go to sleep for good.
+     * Returns true if the DisplayGroup with the provided {@code groupId} should go to sleep now.
+     * Also used when exiting a dream to determine whether we should go back to being fully awake or
+     * else go to sleep for good.
      */
-    private boolean isItBedTimeYetLocked() {
+    private boolean isItBedTimeYetLocked(int groupId) {
         if (!mBootCompleted) {
             return false;
         }
 
         long now = mClock.uptimeMillis();
-        if (isAttentiveTimeoutExpired(now)) {
-            return !isBeingKeptFromInattentiveSleepLocked();
+        if (isAttentiveTimeoutExpired(groupId, now)) {
+            return !isBeingKeptFromInattentiveSleepLocked(groupId);
         } else {
-            return !isBeingKeptAwakeLocked();
+            return !isBeingKeptAwakeLocked(groupId);
         }
     }
 
     /**
-     * Returns true if the device is being kept awake by a wake lock, user activity
-     * or the stay on while powered setting.  We also keep the phone awake when
-     * the proximity sensor returns a positive result so that the device does not
-     * lock while in a phone call.  This function only controls whether the device
-     * will go to sleep or dream which is independent of whether it will be allowed
-     * to suspend.
+     * Returns true if the DisplayGroup with the provided {@code groupId} is being kept awake by a
+     * wake lock, user activity or the stay on while powered setting.  We also keep the phone awake
+     * when the proximity sensor returns a positive result so that the device does not lock while in
+     * a phone call.  This function only controls whether the device will go to sleep or dream which
+     * is independent of whether it will be allowed to suspend.
      */
-    private boolean isBeingKeptAwakeLocked() {
+    private boolean isBeingKeptAwakeLocked(int groupId) {
         return mStayOn
                 || mProximityPositive
                 || (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0
-                || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
-                        | USER_ACTIVITY_SCREEN_DIM)) != 0
+                || (mDisplayGroupPowerStateMapper.getUserActivitySummaryLocked(groupId) & (
+                USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0
                 || mScreenBrightnessBoostInProgress;
     }
 
     /**
-     * Returns true if the device is prevented from going into inattentive sleep by the stay on
-     * while powered setting. We also keep the device awake when the proximity sensor returns a
-     * positive result so that the device does not lock while in a phone call. This function only
-     * controls whether the device will go to sleep which is independent of whether it will be
-     * allowed to suspend.
+     * Returns true if the DisplayGroup with the provided {@code groupId} is prevented from going
+     * into inattentive sleep by the stay on while powered setting. We also keep the device awake
+     * when the proximity sensor returns a positive result so that the device does not lock while in
+     * a phone call. This function only controls whether the device will go to sleep which is
+     * independent of whether it will be allowed to suspend.
      */
-    private boolean isBeingKeptFromInattentiveSleepLocked() {
+    private boolean isBeingKeptFromInattentiveSleepLocked(int groupId) {
         return mStayOn || mScreenBrightnessBoostInProgress || mProximityPositive
-                || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
-                | USER_ACTIVITY_SCREEN_DIM)) != 0;
+                || (mDisplayGroupPowerStateMapper.getUserActivitySummaryLocked(groupId) & (
+                USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0;
     }
 
     private boolean isBeingKeptFromShowingInattentiveSleepWarningLocked() {
@@ -2902,7 +2962,7 @@
                     if (mDreamsBatteryLevelDrainCutoffConfig >= 0
                             && mBatteryLevel < mBatteryLevelWhenDreamStarted
                                     - mDreamsBatteryLevelDrainCutoffConfig
-                            && !isBeingKeptAwakeLocked()) {
+                            && !isBeingKeptAwakeLocked(groupId)) {
                         // If the user activity timeout expired and the battery appears
                         // to be draining faster than it is charging then stop dreaming
                         // and go to sleep.
@@ -2917,18 +2977,17 @@
                 }
 
                 // Dream has ended or will be stopped.  Update the power state.
-                if (isItBedTimeYetLocked()) {
-                    final int flags = isAttentiveTimeoutExpired(now)
+                if (isItBedTimeYetLocked(groupId)) {
+                    final int flags = isAttentiveTimeoutExpired(groupId, now)
                             ? PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE : 0;
                     sleepDisplayGroupNoUpdateLocked(groupId, now,
                             PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, flags, Process.SYSTEM_UID);
-                    updatePowerStateLocked();
                 } else {
                     wakeDisplayGroupNoUpdateLocked(groupId, now, PowerManager.WAKE_REASON_UNKNOWN,
                             "android.server.power:DREAM_FINISHED", Process.SYSTEM_UID,
                             mContext.getOpPackageName(), Process.SYSTEM_UID);
-                    updatePowerStateLocked();
                 }
+                updatePowerStateLocked();
             } else if (wakefulness == WAKEFULNESS_DOZING) {
                 if (isDreaming) {
                     return; // continue dozing
@@ -2952,17 +3011,18 @@
     private boolean canDreamLocked(int groupId) {
         final DisplayPowerRequest displayPowerRequest =
                 mDisplayGroupPowerStateMapper.getPowerRequestLocked(groupId);
-        if (getWakefulnessLocked() != WAKEFULNESS_DREAMING
+        if (!mBootCompleted
+                || getWakefulnessLocked() != WAKEFULNESS_DREAMING
                 || !mDreamsSupportedConfig
                 || !mDreamsEnabledSetting
                 || !displayPowerRequest.isBrightOrDim()
                 || displayPowerRequest.isVr()
-                || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
-                        | USER_ACTIVITY_SCREEN_DIM | USER_ACTIVITY_SCREEN_DREAM)) == 0
-                || !mBootCompleted) {
+                || (mDisplayGroupPowerStateMapper.getUserActivitySummaryLocked(groupId) & (
+                USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM
+                        | USER_ACTIVITY_SCREEN_DREAM)) == 0) {
             return false;
         }
-        if (!isBeingKeptAwakeLocked()) {
+        if (!isBeingKeptAwakeLocked(groupId)) {
             if (!mIsPowered && !mDreamsEnabledOnBatteryConfig) {
                 return false;
             }
@@ -2971,11 +3031,9 @@
                     && mBatteryLevel < mDreamsBatteryLevelMinimumWhenNotPoweredConfig) {
                 return false;
             }
-            if (mIsPowered
-                    && mDreamsBatteryLevelMinimumWhenPoweredConfig >= 0
-                    && mBatteryLevel < mDreamsBatteryLevelMinimumWhenPoweredConfig) {
-                return false;
-            }
+            return !mIsPowered
+                    || mDreamsBatteryLevelMinimumWhenPoweredConfig < 0
+                    || mBatteryLevel >= mDreamsBatteryLevelMinimumWhenPoweredConfig;
         }
         return true;
     }
@@ -3002,7 +3060,7 @@
         if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
                 | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
                 | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_VR_MODE_CHANGED |
-                DIRTY_QUIESCENT | DIRTY_DISPLAY_GROUP_POWER_UPDATED)) != 0) {
+                DIRTY_QUIESCENT | DIRTY_DISPLAY_GROUP_WAKEFULNESS)) != 0) {
             if ((dirty & DIRTY_QUIESCENT) != 0) {
                 if (mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) {
                     sQuiescent = false;
@@ -3072,7 +3130,7 @@
                             mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId))
                             + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)
                             + ", mUserActivitySummary=0x" + Integer.toHexString(
-                            mUserActivitySummary)
+                            mDisplayGroupPowerStateMapper.getUserActivitySummaryLocked(groupId))
                             + ", mBootCompleted=" + mBootCompleted
                             + ", screenBrightnessOverride="
                             + displayPowerRequest.screenBrightnessOverride
@@ -3156,8 +3214,9 @@
         }
 
         if ((mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
-                || (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
                 || !mBootCompleted
+                || (mDisplayGroupPowerStateMapper.getUserActivitySummaryLocked(groupId)
+                & USER_ACTIVITY_SCREEN_BRIGHT) != 0
                 || mScreenBrightnessBoostInProgress) {
             return DisplayPowerRequest.POLICY_BRIGHT;
         }
@@ -3190,7 +3249,7 @@
             synchronized (mLock) {
                 mProximityPositive = false;
                 mDirty |= DIRTY_PROXIMITY_POSITIVE;
-                userActivityNoUpdateLocked(mClock.uptimeMillis(),
+                userActivityNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, mClock.uptimeMillis(),
                         PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
                 updatePowerStateLocked();
             }
@@ -3758,7 +3817,7 @@
             mScreenBrightnessBoostInProgress = true;
             mDirty |= DIRTY_SCREEN_BRIGHTNESS_BOOST;
 
-            userActivityNoUpdateLocked(eventTime,
+            userActivityNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, eventTime,
                     PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
             updatePowerStateLocked();
         }
@@ -3863,14 +3922,16 @@
     @VisibleForTesting
     boolean wasDeviceIdleForInternal(long ms) {
         synchronized (mLock) {
-            return mLastUserActivityTime + ms < mClock.uptimeMillis();
+            return mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(
+                    Display.DEFAULT_DISPLAY_GROUP) + ms < mClock.uptimeMillis();
         }
     }
 
     @VisibleForTesting
     void onUserActivity() {
         synchronized (mLock) {
-            mLastUserActivityTime = mClock.uptimeMillis();
+            mDisplayGroupPowerStateMapper.setLastUserActivityTimeLocked(
+                    Display.DEFAULT_DISPLAY_GROUP, mClock.uptimeMillis());
         }
     }
 
@@ -4024,7 +4085,6 @@
                 TimeUtils.formatDuration(mNotifyLongNextCheck, mClock.uptimeMillis(), pw);
             }
             pw.println();
-            pw.println("  mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary));
             pw.println("  mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity);
             pw.println("  mSandmanScheduled=" + mSandmanScheduled);
             pw.println("  mBatteryLevelLow=" + mBatteryLevelLow);
@@ -4035,9 +4095,6 @@
             pw.println("  mLastWakeTime=" + TimeUtils.formatUptime(mLastWakeTime));
             pw.println("  mLastSleepTime=" + TimeUtils.formatUptime(mLastSleepTime));
             pw.println("  mLastSleepReason=" + PowerManager.sleepReasonToString(mLastSleepReason));
-            pw.println("  mLastUserActivityTime=" + TimeUtils.formatUptime(mLastUserActivityTime));
-            pw.println("  mLastUserActivityTimeNoChangeLights="
-                    + TimeUtils.formatUptime(mLastUserActivityTimeNoChangeLights));
             pw.println("  mLastInteractivePowerHintTime="
                     + TimeUtils.formatUptime(mLastInteractivePowerHintTime));
             pw.println("  mLastScreenBrightnessBoostTime="
@@ -4181,6 +4238,18 @@
                 pw.println(profile.mLockingNotified);
             }
 
+            pw.println("Display Group User Activity:");
+            for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+                pw.println("  displayGroupId=" + id);
+                pw.println("  userActivitySummary=0x" + Integer.toHexString(
+                        mDisplayGroupPowerStateMapper.getUserActivitySummaryLocked(id)));
+                pw.println("  lastUserActivityTime=" + TimeUtils.formatUptime(
+                        mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(id)));
+                pw.println("  lastUserActivityTimeNoChangeLights=" + TimeUtils.formatUptime(
+                        mDisplayGroupPowerStateMapper.getLastUserActivityTimeNoChangeLightsLocked(
+                                id)));
+            }
+
             wcd = mWirelessChargerDetector;
         }
 
@@ -4266,17 +4335,27 @@
             proto.write(PowerManagerServiceDumpProto.NOTIFY_LONG_DISPATCHED_MS, mNotifyLongDispatched);
             proto.write(PowerManagerServiceDumpProto.NOTIFY_LONG_NEXT_CHECK_MS, mNotifyLongNextCheck);
 
-            final long userActivityToken = proto.start(PowerManagerServiceDumpProto.USER_ACTIVITY);
-            proto.write(
-                    PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_BRIGHT,
-                    (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0);
-            proto.write(
-                    PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_DIM,
-                    (mUserActivitySummary & USER_ACTIVITY_SCREEN_DIM) != 0);
-            proto.write(
-                    PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_DREAM,
-                    (mUserActivitySummary & USER_ACTIVITY_SCREEN_DREAM) != 0);
-            proto.end(userActivityToken);
+            for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+                final long userActivityToken = proto.start(
+                        PowerManagerServiceDumpProto.USER_ACTIVITY);
+                proto.write(PowerManagerServiceDumpProto.UserActivityProto.DISPLAY_GROUP_ID, id);
+                final long userActivitySummary =
+                        mDisplayGroupPowerStateMapper.getUserActivitySummaryLocked(id);
+                proto.write(PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_BRIGHT,
+                        (userActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0);
+                proto.write(PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_DIM,
+                        (userActivitySummary & USER_ACTIVITY_SCREEN_DIM) != 0);
+                proto.write(PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_DREAM,
+                        (userActivitySummary & USER_ACTIVITY_SCREEN_DREAM) != 0);
+                proto.write(
+                        PowerManagerServiceDumpProto.UserActivityProto.LAST_USER_ACTIVITY_TIME_MS,
+                        mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(id));
+                proto.write(
+                        PowerManagerServiceDumpProto.UserActivityProto.LAST_USER_ACTIVITY_TIME_NO_CHANGE_LIGHTS_MS,
+                        mDisplayGroupPowerStateMapper.getLastUserActivityTimeNoChangeLightsLocked(
+                                id));
+                proto.end(userActivityToken);
+            }
 
             proto.write(
                     PowerManagerServiceDumpProto.IS_REQUEST_WAIT_FOR_NEGATIVE_PROXIMITY,
@@ -4295,10 +4374,6 @@
 
             proto.write(PowerManagerServiceDumpProto.LAST_WAKE_TIME_MS, mLastWakeTime);
             proto.write(PowerManagerServiceDumpProto.LAST_SLEEP_TIME_MS, mLastSleepTime);
-            proto.write(PowerManagerServiceDumpProto.LAST_USER_ACTIVITY_TIME_MS, mLastUserActivityTime);
-            proto.write(
-                    PowerManagerServiceDumpProto.LAST_USER_ACTIVITY_TIME_NO_CHANGE_LIGHTS_MS,
-                    mLastUserActivityTimeNoChangeLights);
             proto.write(
                     PowerManagerServiceDumpProto.LAST_INTERACTIVE_POWER_HINT_TIME_MS,
                     mLastInteractivePowerHintTime);
@@ -5102,7 +5177,7 @@
             final int uid = Binder.getCallingUid();
             final long ident = Binder.clearCallingIdentity();
             try {
-                userActivityInternal(eventTime, event, flags, uid);
+                userActivityInternal(displayId, eventTime, event, flags, uid);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 38ad4f0..7b0fb01 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -30,7 +30,6 @@
 import static com.android.server.wm.InsetsSourceProviderProto.CONTROL_TARGET;
 import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL;
 import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL_TARGET;
-import static com.android.server.wm.InsetsSourceProviderProto.FINISH_SEAMLESS_ROTATE_FRAME_NUMBER;
 import static com.android.server.wm.InsetsSourceProviderProto.FRAME;
 import static com.android.server.wm.InsetsSourceProviderProto.IME_OVERRIDDEN_FRAME;
 import static com.android.server.wm.InsetsSourceProviderProto.IS_LEASH_READY_FOR_DISPATCHING;
@@ -60,6 +59,7 @@
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
 import java.io.PrintWriter;
+import java.util.function.Consumer;
 
 /**
  * Controller for a specific inset source on the server. It's called provider as it provides the
@@ -85,6 +85,16 @@
     private final Rect mImeOverrideFrame = new Rect();
     private boolean mIsLeashReadyForDispatching;
 
+    private final Consumer<Transaction> mSetLeashPositionConsumer = t -> {
+        if (mControl != null) {
+            final SurfaceControl leash = mControl.getLeash();
+            if (leash != null) {
+                final Point position = mControl.getSurfacePosition();
+                t.setPosition(leash, position.x, position.y);
+            }
+        }
+    };
+
     /** The visibility override from the current controlling window. */
     private boolean mClientVisible;
 
@@ -151,7 +161,6 @@
             // TODO: Ideally, we should wait for the animation to finish so previous window can
             // animate-out as new one animates-in.
             mWin.cancelAnimation();
-            mWin.mPendingPositionChanged = null;
             mWin.mProvidedInsetsSources.remove(mSource.getType());
         }
         ProtoLog.d(WM_DEBUG_IME, "InsetsSource setWin %s", win);
@@ -252,12 +261,11 @@
             final Point position = getWindowFrameSurfacePosition();
             if (mControl.setSurfacePosition(position.x, position.y) && mControlTarget != null) {
                 changed = true;
-                if (!mWin.getWindowFrames().didFrameSizeChange()) {
-                    updateLeashPosition(-1 /* frameNumber */);
-                } else if (mWin.mInRelayout) {
-                    updateLeashPosition(mWin.getFrameNumber());
+                if (mWin.getWindowFrames().didFrameSizeChange() && mWin.mWinAnimator.getShown()
+                        && mWin.okToDisplay()) {
+                    mWin.applyWithNextDraw(mSetLeashPositionConsumer);
                 } else {
-                    mWin.mPendingPositionChanged = this;
+                    mSetLeashPositionConsumer.accept(mWin.getPendingTransaction());
                 }
             }
             final Insets insetsHint = mSource.calculateInsets(
@@ -272,19 +280,6 @@
         }
     }
 
-    void updateLeashPosition(long frameNumber) {
-        if (mControl == null) {
-            return;
-        }
-        final SurfaceControl leash = mControl.getLeash();
-        if (leash != null) {
-            final Transaction t = mDisplayContent.getPendingTransaction();
-            final Point position = mControl.getSurfacePosition();
-            t.setPosition(leash, position.x, position.y);
-            deferTransactionUntil(t, leash, frameNumber);
-        }
-    }
-
     private Point getWindowFrameSurfacePosition() {
         final Rect frame = mWin.getFrame();
         final Point position = new Point();
@@ -292,14 +287,6 @@
         return position;
     }
 
-    private void deferTransactionUntil(Transaction t, SurfaceControl leash, long frameNumber) {
-        if (frameNumber >= 0) {
-            final SurfaceControl barrier = mWin.getClientViewRootSurface();
-            t.deferTransactionUntil(mWin.getSurfaceControl(), barrier, frameNumber);
-            t.deferTransactionUntil(leash, barrier, frameNumber);
-        }
-    }
-
     /**
      * @see InsetsStateController#onControlFakeTargetChanged(int, InsetsControlTarget)
      */
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 7c5afa8..7644cf2 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -43,7 +43,6 @@
 import android.util.ArraySet;
 import android.util.MathUtils;
 import android.util.Slog;
-import android.view.DisplayInfo;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.view.animation.Animation;
@@ -296,9 +295,9 @@
     }
 
     boolean updateWallpaperOffset(WindowState wallpaperWin, boolean sync) {
-        final DisplayInfo displayInfo = wallpaperWin.getDisplayInfo();
-        final int dw = displayInfo.logicalWidth;
-        final int dh = displayInfo.logicalHeight;
+        final Rect parentFrame = wallpaperWin.getParentFrame();
+        final int dw = parentFrame.width();
+        final int dh = parentFrame.height();
 
         int xOffset = 0;
         int yOffset = 0;
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index 9245f8c..ffd6d21 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -113,7 +113,7 @@
     }
 
     /**
-     * @return true if the width or height has changed since last reported to the client.
+     * @return true if the width or height has changed since last updating resizing window.
      */
     boolean didFrameSizeChange() {
         return (mLastFrame.width() != mFrame.width()) || (mLastFrame.height() != mFrame.height());
@@ -135,6 +135,13 @@
     }
 
     /**
+     * @return true if the width or height has changed since last reported to the client.
+     */
+    boolean isFrameSizeChangeReported() {
+        return mFrameSizeChanged || didFrameSizeChange();
+    }
+
+    /**
      * Resets the size changed flags so they're all set to false again. This should be called
      * after the frames are reported to client.
      */
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a8ca5b6..d494b75 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2228,13 +2228,6 @@
 
             win.setFrameNumber(frameNumber);
 
-            final DisplayContent dc = win.getDisplayContent();
-
-            if (win.mPendingPositionChanged != null) {
-                win.mPendingPositionChanged.updateLeashPosition(frameNumber);
-                win.mPendingPositionChanged = null;
-            }
-
             int attrChanges = 0;
             int flagChanges = 0;
             int privateFlagChanges = 0;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 48d4fc5..eb83152 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -726,8 +726,6 @@
      */
     private InsetsState mFrozenInsetsState;
 
-    @Nullable InsetsSourceProvider mPendingPositionChanged;
-
     private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
     private KeyInterceptionInfo mKeyInterceptionInfo;
 
@@ -824,6 +822,12 @@
         updateSurfacePosition(t);
     };
 
+    private final Consumer<SurfaceControl.Transaction> mSetSurfacePositionConsumer = t -> {
+        if (mSurfaceControl != null && mSurfaceControl.isValid()) {
+            t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
+        }
+    };
+
     /**
      * @see #setSurfaceTranslationY(int)
      */
@@ -2180,18 +2184,7 @@
         final int left = mWindowFrames.mFrame.left;
         final int top = mWindowFrames.mFrame.top;
 
-        // During the transition from pip to fullscreen, the activity windowing mode is set to
-        // fullscreen at the beginning while the task is kept in pinned mode. Skip the move
-        // animation in such case since the transition is handled in SysUI.
-        final boolean hasMovementAnimation = getTask() == null
-                ? getWindowConfiguration().hasMovementAnimations()
-                : getTask().getWindowConfiguration().hasMovementAnimations();
-        if (mToken.okToAnimate()
-                && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
-                && !isDragResizing()
-                && hasMovementAnimation
-                && !mWinAnimator.mLastHidden
-                && !mSeamlesslyRotated) {
+        if (canPlayMoveAnimation()) {
             startMoveAnimation(left, top);
         }
 
@@ -2207,6 +2200,22 @@
         mMovedByResize = false;
     }
 
+    private boolean canPlayMoveAnimation() {
+
+        // During the transition from pip to fullscreen, the activity windowing mode is set to
+        // fullscreen at the beginning while the task is kept in pinned mode. Skip the move
+        // animation in such case since the transition is handled in SysUI.
+        final boolean hasMovementAnimation = getTask() == null
+                ? getWindowConfiguration().hasMovementAnimations()
+                : getTask().getWindowConfiguration().hasMovementAnimations();
+        return mToken.okToAnimate()
+                && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
+                && !isDragResizing()
+                && hasMovementAnimation
+                && !mWinAnimator.mLastHidden
+                && !mSeamlesslyRotated;
+    }
+
     /**
      * Return whether this window has moved. (Only makes
      * sense to call from performLayoutAndPlaceSurfacesLockedInner().)
@@ -5377,13 +5386,18 @@
         // prior to the rotation.
         if (!mSurfaceAnimator.hasLeash() && mPendingSeamlessRotate == null
                 && !mLastSurfacePosition.equals(mSurfacePosition)) {
-            t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
+            final boolean frameSizeChanged = mWindowFrames.isFrameSizeChangeReported();
+            final boolean surfaceInsetsChanged = surfaceInsetsChanging();
+            final boolean surfaceSizeChanged = frameSizeChanged || surfaceInsetsChanged;
             mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
-            if (surfaceInsetsChanging() && mWinAnimator.hasSurface()) {
+            if (surfaceInsetsChanged) {
                 mLastSurfaceInsets.set(mAttrs.surfaceInsets);
-                t.deferTransactionUntil(mSurfaceControl,
-                        mWinAnimator.mSurfaceController.mSurfaceControl,
-                        getFrameNumber());
+            }
+            if (surfaceSizeChanged && mWinAnimator.getShown() && !canPlayMoveAnimation()
+                    && okToDisplay()) {
+                applyWithNextDraw(mSetSurfacePositionConsumer);
+            } else {
+                mSetSurfacePositionConsumer.accept(t);
             }
         }
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index cdd5a92b..55ab8c3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -109,6 +109,14 @@
         return 0;
     }
 
+    @Override
+    public void acknowledgeDeviceCompliant() {}
+
+    @Override
+    public boolean isComplianceAcknowledgementRequired() {
+        return false;
+    }
+
     public boolean canProfileOwnerResetPasswordWhenLocked(int userId) {
         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 44791b0..8739a01 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -22,6 +22,7 @@
 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.admin.DeviceAdminReceiver.ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED;
 import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE;
 import static android.app.admin.DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE;
 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
@@ -687,12 +688,19 @@
 
     private static final boolean ENABLE_LOCK_GUARD = true;
 
-    /** Profile off deadline is not set or more than MANAGED_PROFILE_OFF_WARNING_PERIOD away. */
-    private static final int PROFILE_OFF_DEADLINE_DEFAULT = 0;
-    /** Profile off deadline is closer than MANAGED_PROFILE_OFF_WARNING_PERIOD. */
-    private static final int PROFILE_OFF_DEADLINE_WARNING = 1;
-    /** Profile off deadline reached, notify the user that personal apps blocked. */
-    private static final int PROFILE_OFF_DEADLINE_REACHED = 2;
+    /**
+     * Profile off deadline is not set or more than MANAGED_PROFILE_OFF_WARNING_PERIOD away, or the
+     * user is running unlocked, no need for notification.
+     */
+    private static final int PROFILE_OFF_NOTIFICATION_NONE = 0;
+    /**
+     * Profile off deadline is closer than MANAGED_PROFILE_OFF_WARNING_PERIOD.
+     */
+    private static final int PROFILE_OFF_NOTIFICATION_WARNING = 1;
+    /**
+     * Profile off deadline reached, notify the user that personal apps blocked.
+     */
+    private static final int PROFILE_OFF_NOTIFICATION_SUSPENDED = 2;
 
     interface Stats {
         int LOCK_GUARD_GUARD = 0;
@@ -889,10 +897,9 @@
                 }
                 if (isManagedProfile(userHandle)) {
                     Slog.d(LOG_TAG, "Managed profile became unlocked");
-                    if (updatePersonalAppsSuspension(userHandle, true /* unlocked */)
-                            == PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT) {
-                        triggerPolicyComplianceCheck(userHandle);
-                    }
+                    final boolean suspended =
+                            updatePersonalAppsSuspension(userHandle, true /* unlocked */);
+                    triggerPolicyComplianceCheckIfNeeded(userHandle, suspended);
                 }
             } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                 handlePackagesChanged(null /* check all admins */, userHandle);
@@ -16111,7 +16118,6 @@
         Objects.requireNonNull(who, "ComponentName is null");
 
         final CallerIdentity caller = getCallerIdentity(who);
-        // DO shouldn't be able to use this method.
         Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
         Preconditions.checkState(canHandleCheckPolicyComplianceIntent(caller));
 
@@ -16142,18 +16148,26 @@
                 .write();
     }
 
-    /** Starts an activity to check policy compliance in the DPC. */
-    private void triggerPolicyComplianceCheck(int profileUserId) {
-        final Intent intent = new Intent(ACTION_CHECK_POLICY_COMPLIANCE);
+    /** Starts an activity to check policy compliance or request compliance acknowledgement. */
+    private void triggerPolicyComplianceCheckIfNeeded(int profileUserId, boolean suspended) {
         synchronized (getLockObject()) {
             final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(profileUserId);
             if (profileOwner == null) {
                 Slog.wtf(LOG_TAG, "Profile owner not found for compliance check");
                 return;
             }
-            intent.setPackage(profileOwner.info.getPackageName());
+            if (suspended) {
+                // If suspended, DPC will need to show an activity.
+                final Intent intent = new Intent(ACTION_CHECK_POLICY_COMPLIANCE);
+                intent.setPackage(profileOwner.info.getPackageName());
+                mContext.startActivityAsUser(intent, UserHandle.of(profileUserId));
+            } else if (profileOwner.mProfileOffDeadline > 0) {
+                // If not suspended, but deadline set, DPC needs to acknowledge compliance so that
+                // the deadline can be reset.
+                sendAdminCommandLocked(profileOwner, ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED,
+                        /* adminExtras= */ null, /* receiver= */ null, /* inForeground = */ true);
+            }
         }
-        mContext.startActivityAsUser(intent, UserHandle.of(profileUserId));
     }
 
     /**
@@ -16162,39 +16176,35 @@
      *
      * @param unlocked whether the profile is currently running unlocked.
      */
-    private @PersonalAppsSuspensionReason int updatePersonalAppsSuspension(
-            int profileUserId, boolean unlocked) {
-        final boolean suspendedExplicitly;
-        final boolean suspendedByTimeout;
+    private boolean updatePersonalAppsSuspension(int profileUserId, boolean unlocked) {
+        final boolean shouldSuspend;
         synchronized (getLockObject()) {
             final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(profileUserId);
             if (profileOwner != null) {
-                final int deadlineState =
-                        updateProfileOffDeadlineLocked(profileUserId, profileOwner, unlocked);
-                suspendedExplicitly = profileOwner.mSuspendPersonalApps;
-                suspendedByTimeout = deadlineState == PROFILE_OFF_DEADLINE_REACHED;
-                Slog.d(LOG_TAG, "Personal apps suspended explicitly: %b, deadline state: %d",
-                        suspendedExplicitly, deadlineState);
                 final int notificationState =
-                        unlocked ? PROFILE_OFF_DEADLINE_DEFAULT : deadlineState;
+                        updateProfileOffDeadlineLocked(profileUserId, profileOwner, unlocked);
+                final boolean suspendedExplicitly = profileOwner.mSuspendPersonalApps;
+                final boolean suspendedByTimeout = profileOwner.mProfileOffDeadline == -1;
+                Slog.d(LOG_TAG,
+                        "Personal apps suspended explicitly: %b, by timeout: %b, notification: %d",
+                        suspendedExplicitly, suspendedByTimeout, notificationState);
                 updateProfileOffDeadlineNotificationLocked(
                         profileUserId, profileOwner, notificationState);
+                shouldSuspend = suspendedExplicitly || suspendedByTimeout;
             } else {
-                suspendedExplicitly = false;
-                suspendedByTimeout = false;
+                shouldSuspend = false;
             }
         }
 
         final int parentUserId = getProfileParentId(profileUserId);
-        suspendPersonalAppsInternal(parentUserId, suspendedExplicitly || suspendedByTimeout);
-
-        return makeSuspensionReasons(suspendedExplicitly, suspendedByTimeout);
+        suspendPersonalAppsInternal(parentUserId, shouldSuspend);
+        return shouldSuspend;
     }
 
     /**
      * Checks work profile time off policy, scheduling personal apps suspension via alarm if
      * necessary.
-     * @return profile deadline state
+     * @return notification state
      */
     private int updateProfileOffDeadlineLocked(
             int profileUserId, ActiveAdmin profileOwner, boolean unlocked) {
@@ -16206,7 +16216,7 @@
                 profileOwner.mProfileOffDeadline = -1;
                 saveSettingsLocked(profileUserId);
             }
-            return PROFILE_OFF_DEADLINE_REACHED;
+            return unlocked ? PROFILE_OFF_NOTIFICATION_NONE : PROFILE_OFF_NOTIFICATION_SUSPENDED;
         }
         boolean shouldSaveSettings = false;
         if (profileOwner.mSuspendPersonalApps) {
@@ -16216,8 +16226,8 @@
                 shouldSaveSettings = true;
             }
         } else if (profileOwner.mProfileOffDeadline != 0
-                && (profileOwner.mProfileMaximumTimeOffMillis == 0 || unlocked)) {
-            // There is a deadline but either there is no policy or the profile is unlocked -> clear
+                && (profileOwner.mProfileMaximumTimeOffMillis == 0)) {
+            // There is a deadline but either there is no policy -> clear
             // the deadline.
             Slog.i(LOG_TAG, "Profile off deadline is reset to zero");
             profileOwner.mProfileOffDeadline = 0;
@@ -16236,19 +16246,19 @@
         }
 
         final long alarmTime;
-        final int deadlineState;
-        if (profileOwner.mProfileOffDeadline == 0) {
+        final int notificationState;
+        if (unlocked || profileOwner.mProfileOffDeadline == 0) {
             alarmTime = 0;
-            deadlineState = PROFILE_OFF_DEADLINE_DEFAULT;
+            notificationState = PROFILE_OFF_NOTIFICATION_NONE;
         } else if (profileOwner.mProfileOffDeadline - now < MANAGED_PROFILE_OFF_WARNING_PERIOD) {
             // The deadline is close, upon the alarm personal apps should be suspended.
             alarmTime = profileOwner.mProfileOffDeadline;
-            deadlineState = PROFILE_OFF_DEADLINE_WARNING;
+            notificationState = PROFILE_OFF_NOTIFICATION_WARNING;
         } else {
             // The deadline is quite far, upon the alarm we should warn the user first, so the
             // alarm is scheduled earlier than the actual deadline.
             alarmTime = profileOwner.mProfileOffDeadline - MANAGED_PROFILE_OFF_WARNING_PERIOD;
-            deadlineState = PROFILE_OFF_DEADLINE_DEFAULT;
+            notificationState = PROFILE_OFF_NOTIFICATION_NONE;
         }
 
         final AlarmManager am = mInjector.getAlarmManager();
@@ -16268,7 +16278,7 @@
             am.set(AlarmManager.RTC, alarmTime, pi);
         }
 
-        return deadlineState;
+        return notificationState;
     }
 
     private void suspendPersonalAppsInternal(int userId, boolean suspended) {
@@ -16310,7 +16320,7 @@
     @GuardedBy("getLockObject()")
     private void updateProfileOffDeadlineNotificationLocked(
             int profileUserId, ActiveAdmin profileOwner, int notificationState) {
-        if (notificationState == PROFILE_OFF_DEADLINE_DEFAULT) {
+        if (notificationState == PROFILE_OFF_NOTIFICATION_NONE) {
             mInjector.getNotificationManager().cancel(SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED);
             return;
         }
@@ -16331,7 +16341,7 @@
 
         final String text;
         final boolean ongoing;
-        if (notificationState == PROFILE_OFF_DEADLINE_WARNING) {
+        if (notificationState == PROFILE_OFF_NOTIFICATION_WARNING) {
             // Round to the closest integer number of days.
             final int maxDays = (int)
                     ((profileOwner.mProfileMaximumTimeOffMillis + MS_PER_DAY / 2) / MS_PER_DAY);
@@ -16422,7 +16432,6 @@
         Objects.requireNonNull(who, "ComponentName is null");
 
         final CallerIdentity caller = getCallerIdentity(who);
-        // DO shouldn't be able to use this method.
         Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
 
         synchronized (getLockObject()) {
@@ -16432,6 +16441,33 @@
     }
 
     @Override
+    public void acknowledgeDeviceCompliant() {
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
+        enforceUserUnlocked(caller.getUserId());
+
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getProfileOwnerLocked(caller);
+            if (admin.mProfileOffDeadline > 0) {
+                admin.mProfileOffDeadline = 0;
+                saveSettingsLocked(caller.getUserId());
+            }
+        }
+    }
+
+    @Override
+    public boolean isComplianceAcknowledgementRequired() {
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
+        enforceUserUnlocked(caller.getUserId());
+
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getProfileOwnerLocked(caller);
+            return admin.mProfileOffDeadline != 0;
+        }
+    }
+
+    @Override
     public boolean canProfileOwnerResetPasswordWhenLocked(int userId) {
         enforceSystemCaller("call canProfileOwnerResetPasswordWhenLocked");
         synchronized (getLockObject()) {
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index a262939..29aedce 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -295,10 +295,30 @@
             return;
         }
 
-        try {
-            mIProfcollect.report();
-        } catch (RemoteException e) {
-            Log.e(LOG_TAG, e.getMessage());
-        }
+        final boolean uploadReport =
+                DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
+                                        "upload_report", false);
+
+        new Thread(() -> {
+            try {
+                String reportPath = mIProfcollect.report();
+                if (!uploadReport) {
+                    return;
+                }
+                Intent uploadIntent =
+                        new Intent("com.google.android.apps.betterbug.intent.action.UPLOAD_PROFILE")
+                        .setPackage("com.google.android.apps.internal.betterbug")
+                        .putExtra("EXTRA_DESTINATION", "PROFCOLLECT")
+                        .putExtra("EXTRA_PACKAGE_NAME", getContext().getPackageName())
+                        .putExtra("EXTRA_PROFILE_PATH", reportPath)
+                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                Context context = getContext();
+                if (context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0) != null) {
+                    context.sendBroadcast(uploadIntent);
+                }
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, e.getMessage());
+            }
+        }).start();
     }
 }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index 1b0a305..537a49e 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -417,7 +417,7 @@
 
         allowQueryAll.set(true)
 
-        runMethod(target, NON_VERIFIER_UID)
+        assertFails { runMethod(target, NON_VERIFIER_UID) }
     }
 
     private fun approvedVerifier() {
@@ -816,7 +816,7 @@
         // System/shell only
         INTERNAL,
 
-        // INTERNAL || domain verification agent || user setting permission holder
+        // INTERNAL || non-legacy domain verification agent
         QUERENT,
 
         // INTERNAL || domain verification agent
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 28940b3..8481961 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -21,6 +21,7 @@
 import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_COMPAT;
 import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
 import static android.app.AlarmManager.FLAG_IDLE_UNTIL;
+import static android.app.AlarmManager.FLAG_PRIORITIZE;
 import static android.app.AlarmManager.FLAG_STANDALONE;
 import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE;
 import static android.app.AlarmManager.RTC;
@@ -62,6 +63,7 @@
 import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_FUTURITY;
 import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_INTERVAL;
 import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_WINDOW;
+import static com.android.server.alarm.AlarmManagerService.Constants.KEY_PRIORITY_ALARM_DELAY;
 import static com.android.server.alarm.AlarmManagerService.FREQUENT_INDEX;
 import static com.android.server.alarm.AlarmManagerService.INDEFINITE_DELAY;
 import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK;
@@ -443,6 +445,12 @@
                 TEST_CALLING_UID);
     }
 
+    private void setPrioritizedAlarm(int type, long triggerTime, IAlarmListener listener) {
+        mService.setImpl(type, triggerTime, WINDOW_EXACT, 0, null, listener, "test",
+                FLAG_STANDALONE | FLAG_PRIORITIZE, null, null, TEST_CALLING_UID,
+                TEST_CALLING_PACKAGE, null);
+    }
+
     private void setAllowWhileIdleAlarm(int type, long triggerTime, PendingIntent pi,
             boolean unrestricted) {
         final int flags = unrestricted ? FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED : FLAG_ALLOW_WHILE_IDLE;
@@ -579,6 +587,7 @@
         setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION, 40);
         setDeviceConfigLong(KEY_LISTENER_TIMEOUT, 45);
         setDeviceConfigLong(KEY_MIN_WINDOW, 50);
+        setDeviceConfigLong(KEY_PRIORITY_ALARM_DELAY, 55);
         assertEquals(5, mService.mConstants.MIN_FUTURITY);
         assertEquals(10, mService.mConstants.MIN_INTERVAL);
         assertEquals(15, mService.mConstants.MAX_INTERVAL);
@@ -589,6 +598,7 @@
         assertEquals(40, mService.mConstants.ALLOW_WHILE_IDLE_WHITELIST_DURATION);
         assertEquals(45, mService.mConstants.LISTENER_TIMEOUT);
         assertEquals(50, mService.mConstants.MIN_WINDOW);
+        assertEquals(55, mService.mConstants.PRIORITY_ALARM_DELAY);
     }
 
     @Test
@@ -1586,6 +1596,89 @@
     }
 
     @Test
+    public void prioritizedAlarmsInBatterySaver() throws Exception {
+        when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
+                TEST_CALLING_PACKAGE)).thenReturn(true);
+        when(mAppStateTracker.isForceAllAppsStandbyEnabled()).thenReturn(true);
+        final long minDelay = 5;
+        setDeviceConfigLong(KEY_PRIORITY_ALARM_DELAY, minDelay);
+
+        final long firstTrigger = mNowElapsedTest + 4;
+        final AtomicInteger alarmsFired = new AtomicInteger(0);
+        final int numAlarms = 10;
+        for (int i = 0; i < numAlarms; i++) {
+            setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
+                    new IAlarmListener.Stub() {
+                        @Override
+                        public void doAlarm(IAlarmCompleteListener callback)
+                                throws RemoteException {
+                            alarmsFired.incrementAndGet();
+                        }
+                    });
+        }
+        assertEquals(firstTrigger, mTestTimer.getElapsed());
+        mNowElapsedTest = firstTrigger;
+        mTestTimer.expire();
+        while (alarmsFired.get() < numAlarms) {
+            assertEquals(mNowElapsedTest + minDelay, mTestTimer.getElapsed());
+            mNowElapsedTest = mTestTimer.getElapsed();
+            mTestTimer.expire();
+        }
+        assertEquals(numAlarms, alarmsFired.get());
+    }
+
+    @Test
+    public void prioritizedAlarmsInDeviceIdle() throws Exception {
+        doReturn(0).when(mService).fuzzForDuration(anyLong());
+
+        final long minDelay = 5;
+        setDeviceConfigLong(KEY_PRIORITY_ALARM_DELAY, minDelay);
+
+        final long idleUntil = mNowElapsedTest + 1000;
+        setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil, getNewMockPendingIntent());
+        assertNotNull(mService.mPendingIdleUntil);
+
+        final long firstTrigger = mNowElapsedTest + 4;
+        final AtomicInteger alarmsFired = new AtomicInteger(0);
+        final int numAlarms = 10;
+        for (int i = 0; i < numAlarms; i++) {
+            setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
+                    new IAlarmListener.Stub() {
+                        @Override
+                        public void doAlarm(IAlarmCompleteListener callback)
+                                throws RemoteException {
+                            alarmsFired.incrementAndGet();
+                        }
+                    });
+        }
+        assertEquals(firstTrigger, mTestTimer.getElapsed());
+        mNowElapsedTest = firstTrigger;
+        mTestTimer.expire();
+        while (alarmsFired.get() < numAlarms) {
+            assertEquals(mNowElapsedTest + minDelay, mTestTimer.getElapsed());
+            mNowElapsedTest = mTestTimer.getElapsed();
+            mTestTimer.expire();
+        }
+        assertEquals(numAlarms, alarmsFired.get());
+
+        setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil - 3, new IAlarmListener.Stub() {
+            @Override
+            public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
+            }
+        });
+        setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil - 2, new IAlarmListener.Stub() {
+            @Override
+            public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
+            }
+        });
+        assertEquals(idleUntil - 3, mTestTimer.getElapsed());
+        mNowElapsedTest = mTestTimer.getElapsed();
+        mTestTimer.expire();
+
+        assertEquals(idleUntil, mTestTimer.getElapsed());
+    }
+
+    @Test
     public void dispatchOrder() throws Exception {
         doReturn(0).when(mService).fuzzForDuration(anyLong());
 
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 5761958..9513c6e 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -70,6 +70,7 @@
     <uses-permission android:name="android.permission.CONTROL_KEYGUARD"/>
     <uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/>
     <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"/>
+    <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS"/>
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
     <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/>
     <uses-permission android:name="android.permission.HARDWARE_TEST"/>
diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
index 73a2feb..4a67ec7 100644
--- a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
@@ -20,6 +20,7 @@
 import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_BT;
 import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_CPU;
 import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_DISPLAY;
+import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO;
 import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -93,6 +94,16 @@
         tempAllIds.add(btId);
         mPowerStatsInternal.incrementEnergyConsumption(btId, 34567);
 
+        final int gnssId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.GNSS, 0,
+                "gnss");
+        tempAllIds.add(gnssId);
+        mPowerStatsInternal.incrementEnergyConsumption(gnssId, 787878);
+
+        final int mobileRadioId = mPowerStatsInternal.addEnergyConsumer(
+                EnergyConsumerType.MOBILE_RADIO, 0, "mobile_radio");
+        tempAllIds.add(mobileRadioId);
+        mPowerStatsInternal.incrementEnergyConsumption(mobileRadioId, 62626);
+
         final int[] cpuClusterIds = new int[numCpuClusters];
         for (int i = 0; i < numCpuClusters; i++) {
             cpuClusterIds[i] = mPowerStatsInternal.addEnergyConsumer(
@@ -135,6 +146,12 @@
         assertEquals(1, bluetoothResults.length);
         assertEquals(btId, bluetoothResults[0].id);
 
+        final EnergyConsumerResult[] mobileRadioResults =
+                mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_RADIO).getNow(null);
+        // Results should only have the mobile radio energy consumer
+        assertEquals(1, mobileRadioResults.length);
+        assertEquals(mobileRadioId, mobileRadioResults[0].id);
+
         final EnergyConsumerResult[] cpuResults =
                 mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_CPU).getNow(null);
         // Results should only have the cpu cluster energy consumers
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 9ffb5017..5c8a7d2 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -80,6 +80,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.widget.LockPatternUtils;
 import com.android.server.FgThread;
 import com.android.server.am.UserState.KeyEvictedCallback;
 import com.android.server.pm.UserManagerInternal;
@@ -123,6 +124,7 @@
     private static final long HANDLER_WAIT_TIME_MS = 100;
 
     private UserController mUserController;
+    private LockPatternUtils mLockPatternUtils;
     private TestInjector mInjector;
     private final HashMap<Integer, UserState> mUserStates = new HashMap<>();
 
@@ -161,6 +163,13 @@
             doNothing().when(mInjector).activityManagerOnUserStopped(anyInt());
             doNothing().when(mInjector).clearBroadcastQueueForUser(anyInt());
             doNothing().when(mInjector).taskSupervisorRemoveUser(anyInt());
+
+            // Make it appear that calling unlockUserKey() is needed.
+            doReturn(true).when(mInjector).isFileEncryptedNativeOnly();
+            mLockPatternUtils = mock(LockPatternUtils.class);
+            when(mLockPatternUtils.isSecure(anyInt())).thenReturn(false);
+            doReturn(mLockPatternUtils).when(mInjector).getLockPatternUtils();
+
             // All UserController params are set to default.
             mUserController = new UserController(mInjector);
             setUpUser(TEST_USER_ID, NO_USERINFO_FLAGS);
@@ -552,6 +561,20 @@
                 /* keyEvictedCallback= */ mKeyEvictedCallback, /* expectLocking= */ true);
     }
 
+    /**
+     * Test that if a user has a lock screen credential set, then UserController
+     * doesn't bother trying to unlock their storage key without a credential
+     * token, as it will never work.
+     */
+    @Test
+    public void testSecureUserUnlockNotAttempted() throws Exception {
+        when(mLockPatternUtils.isSecure(eq(TEST_USER_ID1))).thenReturn(true);
+        setUpUser(TEST_USER_ID1, 0);
+        mUserController.startUser(TEST_USER_ID1, /* foreground= */ false);
+        verify(mInjector.mStorageManagerMock, times(0))
+                .unlockUserKey(eq(TEST_USER_ID1), anyInt(), any(), any());
+    }
+
     @Test
     public void testStartProfile_fullUserFails() {
         setUpUser(TEST_USER_ID1, 0);
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
index f3ee233..4240581 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
@@ -104,12 +104,12 @@
 
         // Insert package1 document
         GenericDocument document1 =
-                new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
+                new GenericDocument.Builder<>("namespace", "uri", "schema1").build();
         mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
 
         // Insert package2 document
         GenericDocument document2 =
-                new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
+                new GenericDocument.Builder<>("namespace", "uri", "schema2").build();
         mAppSearchImpl.putDocument("package2", "database2", document2, /*logger=*/ null);
 
         // No query filters specified, global query can retrieve all documents.
@@ -122,8 +122,8 @@
 
         // Document2 will be first since it got indexed later and has a "better", aka more recent
         // score.
-        assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document2);
-        assertThat(searchResultPage.getResults().get(1).getDocument()).isEqualTo(document1);
+        assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
+        assertThat(searchResultPage.getResults().get(1).getGenericDocument()).isEqualTo(document1);
     }
 
     /**
@@ -158,12 +158,12 @@
 
         // Insert package1 document
         GenericDocument document1 =
-                new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
+                new GenericDocument.Builder<>("namespace", "uri", "schema1").build();
         mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
 
         // Insert package2 document
         GenericDocument document2 =
-                new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
+                new GenericDocument.Builder<>("namespace", "uri", "schema2").build();
         mAppSearchImpl.putDocument("package2", "database2", document2, /*logger=*/ null);
 
         // "package1" filter specified
@@ -176,7 +176,7 @@
                 mAppSearchImpl.globalQuery(
                         "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
         assertThat(searchResultPage.getResults()).hasSize(1);
-        assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document1);
+        assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
 
         // "package2" filter specified
         searchSpec =
@@ -188,7 +188,7 @@
                 mAppSearchImpl.globalQuery(
                         "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
         assertThat(searchResultPage.getResults()).hasSize(1);
-        assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document2);
+        assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
index fcd6b84..7bdc87e 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
@@ -116,7 +116,7 @@
     }
 
     CompatConfigBuilder addOverridableChangeWithId(long id) {
-        mChanges.add(new CompatChange(id, "", -1, -1, false, true, "", true));
+        mChanges.add(new CompatChange(id, "", -1, -1, true, false, "", true));
         return this;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index bd77405..a866363 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -260,6 +260,36 @@
     }
 
     @Test
+    public void testInstallerCanSetOverrides() throws Exception {
+        final long changeId = 1234L;
+        final int installerUid = 23;
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addOverridableChangeWithId(1234L)
+                .build();
+        ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+                .withPackageName("com.some.package")
+                .build();
+        PackageManager packageManager = mock(PackageManager.class);
+        when(mContext.getPackageManager()).thenReturn(packageManager);
+        when(packageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
+                .thenReturn(applicationInfo);
+
+        // Force the validator to prevent overriding the change by using a user build.
+        when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+        when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+
+        CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(
+                Collections.singletonMap(1234L,
+                        new PackageOverride.Builder()
+                                .setMaxVersionCode(99L)
+                                .setEnabled(true)
+                                .build()));
+
+        compatConfig.addOverrides(config, "com.some.package");
+        assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
+    }
+
+    @Test
     public void testApplyDeferredOverridesAfterInstallingApp() throws Exception {
         ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
                 .withPackageName("com.notinstalled.foo")
@@ -639,9 +669,18 @@
                         .build());
         when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
                 .thenThrow(new NameNotFoundException());
-
-        compatConfig.addOverride(1L, "foo.bar", true);
-        compatConfig.addOverride(2L, "bar.baz", false);
+        compatConfig.addOverrides(
+                new CompatibilityOverrideConfig(
+                        Collections.singletonMap(
+                                1L,
+                                new PackageOverride.Builder().setEnabled(true).build())),
+                "foo.bar");
+        compatConfig.addOverrides(
+                new CompatibilityOverrideConfig(
+                        Collections.singletonMap(
+                                2L,
+                                new PackageOverride.Builder().setEnabled(false).build())),
+                "bar.baz");
 
         assertThat(readFile(overridesFile)).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                 + "<overrides>\n"
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 3fc6e99..a2664e5 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -113,7 +113,7 @@
                 new CompatibilityChangeInfo(
                         6L, "", Build.VERSION_CODES.R, -1, false, false, "", false),
                 new CompatibilityChangeInfo(7L, "", -1, -1, false, true, "", false),
-                new CompatibilityChangeInfo(8L, "", -1, -1, false, true, "", true));
+                new CompatibilityChangeInfo(8L, "", -1, -1, true, false, "", true));
     }
 
     @Test
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 15ada89..c8099e2 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -464,6 +464,40 @@
     }
 
     @Test
+    public void testStaleAppRequestSize() {
+        DisplayModeDirector director =
+                new DisplayModeDirector(mContext, mHandler, mInjector);
+        Display.Mode[] modes = new Display.Mode[] {
+                new Display.Mode(1, 1280, 720, 60),
+        };
+        Display.Mode defaultMode = modes[0];
+
+        // Inject supported modes
+        SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
+        supportedModesByDisplay.put(DISPLAY_ID, modes);
+        director.injectSupportedModesByDisplay(supportedModesByDisplay);
+
+        // Inject default mode
+        SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<>();
+        defaultModesByDisplay.put(DISPLAY_ID, defaultMode);
+        director.injectDefaultModeByDisplay(defaultModesByDisplay);
+
+        // Inject votes
+        SparseArray<Vote> votes = new SparseArray<>();
+        votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(1920, 1080));
+        votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(50, 50));
+        SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+        votesByDisplay.put(DISPLAY_ID, votes);
+        director.injectVotesByDisplay(votesByDisplay);
+
+        director.setShouldAlwaysRespectAppRequestedMode(true);
+
+        // We should return the only available mode
+        DesiredDisplayModeSpecs specs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+        assertThat(specs.baseModeId).isEqualTo(defaultMode.getModeId());
+    }
+
+    @Test
     public void testBrightnessObserverGetsUpdatedRefreshRatesForZone() {
         DisplayModeDirector director =
                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
index eebc25a..6f1268e 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
@@ -23,6 +23,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
@@ -34,6 +35,7 @@
 import android.os.Build;
 import android.os.LocaleList;
 import android.os.Parcel;
+import android.util.ArrayMap;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
@@ -48,6 +50,7 @@
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
@@ -793,6 +796,97 @@
         }
     }
 
+    @Test
+    public void testChooseSystemVoiceIme() throws Exception {
+        final InputMethodInfo systemIme = createFakeInputMethodInfo("SystemIme", "fake.voice0",
+                true /* isSystem */);
+
+        // Returns null when the config value is null.
+        {
+            final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+            methodMap.put(systemIme.getId(), systemIme);
+            assertNull(InputMethodUtils.chooseSystemVoiceIme(methodMap, null, ""));
+        }
+
+        // Returns null when the config value is empty.
+        {
+            final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+            methodMap.put(systemIme.getId(), systemIme);
+            assertNull(InputMethodUtils.chooseSystemVoiceIme(methodMap, "", ""));
+        }
+
+        // Returns null when the configured package doesn't have an IME.
+        {
+            assertNull(InputMethodUtils.chooseSystemVoiceIme(new ArrayMap<>(),
+                    systemIme.getPackageName(), ""));
+        }
+
+        // Returns the right one when the current default is null.
+        {
+            final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+            methodMap.put(systemIme.getId(), systemIme);
+            assertEquals(systemIme, InputMethodUtils.chooseSystemVoiceIme(methodMap,
+                    systemIme.getPackageName(), null));
+        }
+
+        // Returns the right one when the current default is empty.
+        {
+            final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+            methodMap.put(systemIme.getId(), systemIme);
+            assertEquals(systemIme, InputMethodUtils.chooseSystemVoiceIme(methodMap,
+                    systemIme.getPackageName(), ""));
+        }
+
+        // Returns null when the current default isn't found.
+        {
+            assertNull(InputMethodUtils.chooseSystemVoiceIme(new ArrayMap<>(),
+                    systemIme.getPackageName(), systemIme.getId()));
+        }
+
+        // Returns null when there are multiple IMEs defined by the config package.
+        {
+            final InputMethodInfo secondIme = createFakeInputMethodInfo(systemIme.getPackageName(),
+                    "fake.voice1", true /* isSystem */);
+            final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+            methodMap.put(systemIme.getId(), systemIme);
+            methodMap.put(secondIme.getId(), secondIme);
+            assertNull(InputMethodUtils.chooseSystemVoiceIme(methodMap, systemIme.getPackageName(),
+                    ""));
+        }
+
+        // Returns the current one when the current default and config point to the same package.
+        {
+            final InputMethodInfo secondIme = createFakeInputMethodInfo("SystemIme", "fake.voice1",
+                    true /* isSystem */);
+            final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+            methodMap.put(systemIme.getId(), systemIme);
+            methodMap.put(secondIme.getId(), secondIme);
+            assertEquals(systemIme, InputMethodUtils.chooseSystemVoiceIme(methodMap,
+                    systemIme.getPackageName(), systemIme.getId()));
+        }
+
+        // Doesn't return the current default if it isn't a system app.
+        {
+            final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+            final InputMethodInfo nonSystemIme = createFakeInputMethodInfo("NonSystemIme",
+                    "fake.voice0", false /* isSystem */);
+            methodMap.put(nonSystemIme.getId(), nonSystemIme);
+            assertNull(InputMethodUtils.chooseSystemVoiceIme(methodMap,
+                    nonSystemIme.getPackageName(), nonSystemIme.getId()));
+        }
+
+        // Returns null if the configured one isn't a system app.
+        {
+            final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+            final InputMethodInfo nonSystemIme = createFakeInputMethodInfo(
+                    "FakeDefaultAutoVoiceIme", "fake.voice0", false /* isSystem */);
+            methodMap.put(systemIme.getId(), systemIme);
+            methodMap.put(nonSystemIme.getId(), nonSystemIme);
+            assertNull(InputMethodUtils.chooseSystemVoiceIme(methodMap,
+                    nonSystemIme.getPackageName(), ""));
+        }
+    }
+
     private void assertDefaultEnabledImes(final ArrayList<InputMethodInfo> preinstalledImes,
             final Locale systemLocale, String... expectedImeNames) {
         final Context context = createTargetContextWithLocales(new LocaleList(systemLocale));
@@ -866,6 +960,25 @@
     }
 
     private static InputMethodInfo createFakeInputMethodInfo(String packageName, String name,
+            boolean isSystem) {
+        final ResolveInfo ri = new ResolveInfo();
+        final ServiceInfo si = new ServiceInfo();
+        final ApplicationInfo ai = new ApplicationInfo();
+        ai.packageName = packageName;
+        ai.enabled = true;
+        if (isSystem) {
+            ai.flags |= ApplicationInfo.FLAG_SYSTEM;
+        }
+        si.applicationInfo = ai;
+        si.enabled = true;
+        si.packageName = packageName;
+        si.name = name;
+        si.exported = true;
+        ri.serviceInfo = si;
+        return new InputMethodInfo(ri, false, "", Collections.emptyList(), 1, true);
+    }
+
+    private static InputMethodInfo createFakeInputMethodInfo(String packageName, String name,
             CharSequence label, boolean isAuxIme, boolean isDefault,
             List<InputMethodSubtype> subtypes) {
         final ResolveInfo ri = new ResolveInfo();
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index d405113..100d3ea 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -1773,57 +1773,75 @@
                 true);
     }
 
-    /**
-     * Test that when StatsProvider triggers limit reached, new limit will be calculated and
-     * re-armed.
-     */
-    @Test
-    public void testStatsProviderLimitReached() throws Exception {
-        final int CYCLE_DAY = 15;
-
-        final NetworkStats stats = new NetworkStats(0L, 1);
+    private void increaseMockedTotalBytes(NetworkStats stats, long rxBytes, long txBytes) {
         stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
-                2999, 1, 2000, 1, 0);
+                rxBytes, 1, txBytes, 1, 0);
         when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
                 .thenReturn(stats.getTotalBytes());
         when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
                 .thenReturn(stats);
+    }
+
+    private void triggerOnStatsProviderWarningOrLimitReached() throws InterruptedException {
+        final NetworkPolicyManagerInternal npmi = LocalServices
+                .getService(NetworkPolicyManagerInternal.class);
+        npmi.onStatsProviderWarningOrLimitReached("TEST");
+        // Wait for processing of MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED.
+        postMsgAndWaitForCompletion();
+        verify(mStatsService).forceUpdate();
+        // Wait for processing of MSG_*_INTERFACE_QUOTAS.
+        postMsgAndWaitForCompletion();
+    }
+
+    /**
+     * Test that when StatsProvider triggers warning and limit reached, new quotas will be
+     * calculated and re-armed.
+     */
+    @Test
+    public void testStatsProviderWarningAndLimitReached() throws Exception {
+        final int CYCLE_DAY = 15;
+
+        final NetworkStats stats = new NetworkStats(0L, 1);
+        increaseMockedTotalBytes(stats, 2999, 2000);
 
         // Get active mobile network in place
         expectMobileDefaults();
         mService.updateNetworks();
-        verify(mStatsService).setStatsProviderLimitAsync(TEST_IFACE, Long.MAX_VALUE);
+        verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE,
+                Long.MAX_VALUE);
 
-        // Set limit to 10KB.
+        // Set warning to 7KB and limit to 10KB.
         setNetworkPolicies(new NetworkPolicy(
-                sTemplateMobileAll, CYCLE_DAY, TIMEZONE_UTC, WARNING_DISABLED, 10000L,
-                true));
+                sTemplateMobileAll, CYCLE_DAY, TIMEZONE_UTC, 7000L, 10000L, true));
         postMsgAndWaitForCompletion();
 
-        // Verifies that remaining quota is set to providers.
-        verify(mStatsService).setStatsProviderLimitAsync(TEST_IFACE, 10000L - 4999L);
-
+        // Verifies that remaining quotas are set to providers.
+        verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2001L, 5001L);
         reset(mStatsService);
 
-        // Increase the usage.
-        stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
-                1000, 1, 999, 1, 0);
-        when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
-                .thenReturn(stats.getTotalBytes());
-        when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
-                .thenReturn(stats);
+        // Increase the usage and simulates that limit reached fires earlier by provider,
+        // but actually the quota is not yet reached. Verifies that the limit reached leads to
+        // a force update and new quotas should be set.
+        increaseMockedTotalBytes(stats, 1000, 999);
+        triggerOnStatsProviderWarningOrLimitReached();
+        verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2L, 3002L);
+        reset(mStatsService);
 
-        // Simulates that limit reached fires earlier by provider, but actually the quota is not
-        // yet reached.
-        final NetworkPolicyManagerInternal npmi = LocalServices
-                .getService(NetworkPolicyManagerInternal.class);
-        npmi.onStatsProviderWarningOrLimitReached("TEST");
+        // Increase the usage and simulate warning reached, the new warning should be unlimited
+        // since service will disable warning quota to stop lower layer from keep triggering
+        // warning reached event.
+        increaseMockedTotalBytes(stats, 1000L, 1000);
+        triggerOnStatsProviderWarningOrLimitReached();
+        verify(mStatsService).setStatsProviderWarningAndLimitAsync(
+                TEST_IFACE, Long.MAX_VALUE, 1002L);
+        reset(mStatsService);
 
-        // Verifies that the limit reached leads to a force update and new limit should be set.
-        postMsgAndWaitForCompletion();
-        verify(mStatsService).forceUpdate();
-        postMsgAndWaitForCompletion();
-        verify(mStatsService).setStatsProviderLimitAsync(TEST_IFACE, 10000L - 4999L - 1999L);
+        // Increase the usage that over the warning and limit, the new limit should set to 1 to
+        // block the network traffic.
+        increaseMockedTotalBytes(stats, 1000L, 1000);
+        triggerOnStatsProviderWarningOrLimitReached();
+        verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE, 1L);
+        reset(mStatsService);
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 212a9c6..8e2b207 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -89,6 +89,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
@@ -769,6 +770,21 @@
         }
 
         @Override
+        public void writeQueryResultsToFile(String packageName, String databaseName,
+                ParcelFileDescriptor fileDescriptor, String queryExpression,
+                Bundle searchSpecBundle, int userId, IAppSearchResultCallback callback)
+                throws RemoteException {
+            ignore(callback);
+        }
+
+        @Override
+        public void putDocumentsFromFile(String packageName, String databaseName,
+                ParcelFileDescriptor fileDescriptor, int userId, IAppSearchResultCallback callback)
+                throws RemoteException {
+            ignore(callback);
+        }
+
+        @Override
         public void reportUsage(String packageName, String databaseName, String namespace,
                 String uri, long usageTimeMillis, boolean systemUsage, int userId,
                 IAppSearchResultCallback callback)
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index d63a467..a231169 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -287,7 +287,7 @@
 
         final SuspendDialogInfo dialogInfo1 = new SuspendDialogInfo.Builder()
                 .setIcon(0x11220001)
-                .setTitle(0x11220002)
+                .setTitle("String Title")
                 .setMessage("1st message")
                 .setNeutralButtonText(0x11220003)
                 .setNeutralButtonAction(BUTTON_ACTION_MORE_DETAILS)
@@ -296,7 +296,7 @@
                 .setIcon(0x22220001)
                 .setTitle(0x22220002)
                 .setMessage("2nd message")
-                .setNeutralButtonText(0x22220003)
+                .setNeutralButtonText("String button text")
                 .setNeutralButtonAction(BUTTON_ACTION_UNSUSPEND)
                 .build();
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java
index 322e448..826a8d4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java
@@ -57,7 +57,7 @@
     }
 
     @Test
-    public void equalsComparesTitle() {
+    public void equalsComparesTitleIds() {
         final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
         final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
         assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
@@ -67,7 +67,39 @@
     }
 
     @Test
-    public void equalsComparesButtonText() {
+    public void equalsIgnoresTitleStringsWhenIdsSet() {
+        final SuspendDialogInfo.Builder dialogBuilder1 = new SuspendDialogInfo.Builder()
+                .setTitle(VALID_TEST_RES_ID_1)
+                .setTitle("1st title");
+        final SuspendDialogInfo.Builder dialogBuilder2 = new SuspendDialogInfo.Builder()
+                .setTitle(VALID_TEST_RES_ID_1)
+                .setTitle("2nd title");
+        // String titles different but should get be ignored when resource ids are set
+        assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
+    }
+
+    @Test
+    public void equalsComparesTitleStringsWhenNoIdsSet() {
+        final SuspendDialogInfo.Builder dialogBuilder1 = new SuspendDialogInfo.Builder()
+                .setTitle("1st title");
+        final SuspendDialogInfo.Builder dialogBuilder2 = new SuspendDialogInfo.Builder()
+                .setTitle("2nd title");
+        // Both have different titles, which are not ignored as resource ids aren't set
+        assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
+    }
+
+    @Test
+    public void titleStringClearedWhenResIdSet() {
+        final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder()
+                .setTitle(VALID_TEST_RES_ID_2)
+                .setTitle("Should be cleared on build")
+                .build();
+        assertNull(dialogInfo.getTitle());
+        assertEquals(VALID_TEST_RES_ID_2, dialogInfo.getTitleResId());
+    }
+
+    @Test
+    public void equalsComparesButtonTextIds() {
         final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
         final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
         assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
@@ -77,6 +109,38 @@
     }
 
     @Test
+    public void equalsIgnoresButtonStringsWhenIdsSet() {
+        final SuspendDialogInfo.Builder dialogBuilder1 = new SuspendDialogInfo.Builder()
+                .setNeutralButtonText(VALID_TEST_RES_ID_1)
+                .setNeutralButtonText("1st button text");
+        final SuspendDialogInfo.Builder dialogBuilder2 = new SuspendDialogInfo.Builder()
+                .setNeutralButtonText(VALID_TEST_RES_ID_1)
+                .setNeutralButtonText("2nd button text");
+        // Button strings different but should get be ignored when resource ids are set
+        assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
+    }
+
+    @Test
+    public void equalsComparesButtonStringsWhenNoIdsSet() {
+        final SuspendDialogInfo.Builder dialogBuilder1 = new SuspendDialogInfo.Builder()
+                .setNeutralButtonText("1st button text");
+        final SuspendDialogInfo.Builder dialogBuilder2 = new SuspendDialogInfo.Builder()
+                .setNeutralButtonText("2nd button text");
+        // Both have different button texts, which are not ignored as resource ids aren't set
+        assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
+    }
+
+    @Test
+    public void buttonStringClearedWhenResIdSet() {
+        final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder()
+                .setNeutralButtonText(VALID_TEST_RES_ID_2)
+                .setNeutralButtonText("Should be cleared on build")
+                .build();
+        assertNull(dialogInfo.getNeutralButtonText());
+        assertEquals(VALID_TEST_RES_ID_2, dialogInfo.getNeutralButtonTextResId());
+    }
+
+    @Test
     public void equalsComparesButtonAction() {
         final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
         final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 6e5fbd0..2df6c5a 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -71,6 +71,7 @@
 import android.service.dreams.DreamManagerInternal;
 import android.test.mock.MockContentResolver;
 import android.view.Display;
+import android.view.DisplayInfo;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -594,6 +595,8 @@
     public void testWasDeviceIdleFor_true() {
         int interval = 1000;
         createService();
+        mService.systemReady(null);
+        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
         mService.onUserActivity();
         advanceTime(interval + 1 /* just a little more */);
         assertThat(mService.wasDeviceIdleForInternal(interval)).isTrue();
@@ -603,6 +606,8 @@
     public void testWasDeviceIdleFor_false() {
         int interval = 1000;
         createService();
+        mService.systemReady(null);
+        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
         mService.onUserActivity();
         assertThat(mService.wasDeviceIdleForInternal(interval)).isFalse();
     }
@@ -757,6 +762,9 @@
 
     @Test
     public void testInattentiveSleep_userActivityDismissesWarning() throws Exception {
+        final DisplayInfo info = new DisplayInfo();
+        info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
+        when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
         setMinimumScreenOffTimeoutConfig(5);
         setAttentiveWarningDuration(1900);
         setAttentiveTimeout(2000);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
deleted file mode 100644
index 1700707..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.WindowContainer.POSITION_TOP;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link DisplayContent} class.
- *
- * Build/Install/Run:
- *  atest WmTests:ActivityDisplayTests
- */
-@SmallTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-// TODO(b/144248496): Merge to DisplayContentTests
-public class ActivityDisplayTests extends WindowTestsBase {
-
-    @Test
-    public void testLastFocusedStackIsUpdatedWhenMovingStack() {
-        // Create a stack at bottom.
-        final TaskDisplayArea taskDisplayAreas =
-                mRootWindowContainer.getDefaultDisplay().getDefaultTaskDisplayArea();
-        final Task stack =
-                new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build();
-        final Task prevFocusedStack = taskDisplayAreas.getFocusedRootTask();
-
-        stack.moveToFront("moveStackToFront");
-        // After moving the stack to front, the previous focused should be the last focused.
-        assertTrue(stack.isFocusedRootTaskOnDisplay());
-        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.getLastFocusedRootTask());
-    }
-
-    /**
-     * This test simulates the picture-in-picture menu activity launches an activity to fullscreen
-     * stack. The fullscreen stack should be the top focused for resuming correctly.
-     */
-    @Test
-    public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() {
-        // Create a pinned stack and move to front.
-        final Task pinnedStack = mRootWindowContainer.getDefaultTaskDisplayArea()
-                .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)
-                .setTask(pinnedTask).build();
-        pinnedStack.moveToFront("movePinnedStackToFront");
-
-        // The focused stack should be the pinned stack.
-        assertTrue(pinnedStack.isFocusedRootTaskOnDisplay());
-
-        // Create a fullscreen stack and move to front.
-        final Task fullscreenStack = createFullscreenStackWithSimpleActivityAt(
-                mRootWindowContainer.getDefaultDisplay());
-        fullscreenStack.moveToFront("moveFullscreenStackToFront");
-
-        // The focused stack should be the fullscreen stack.
-        assertTrue(fullscreenStack.isFocusedRootTaskOnDisplay());
-    }
-
-    /**
-     * 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
-    public void testStackShouldNotBeFocusedAfterMovingToBackOrRemoving() {
-        // Create a display which only contains 2 stacks.
-        final DisplayContent display = addNewDisplayContentAt(POSITION_TOP);
-        final Task stack1 = createFullscreenStackWithSimpleActivityAt(display);
-        final Task stack2 = createFullscreenStackWithSimpleActivityAt(display);
-
-        // Put stack1 and stack2 on top.
-        stack1.moveToFront("moveStack1ToFront");
-        stack2.moveToFront("moveStack2ToFront");
-        assertTrue(stack2.isFocusedRootTaskOnDisplay());
-
-        // Stack1 should be focused after moving stack2 to back.
-        stack2.moveToBack("moveStack2ToBack", null /* task */);
-        assertTrue(stack1.isFocusedRootTaskOnDisplay());
-
-        // Stack2 should be focused after removing stack1.
-        stack1.getDisplayArea().removeRootTask(stack1);
-        assertTrue(stack2.isFocusedRootTaskOnDisplay());
-    }
-
-    /**
-     * Verifies {@link DisplayContent#remove} should not resume home stack on the removing display.
-     */
-    @Test
-    public void testNotResumeHomeStackOnRemovingDisplay() {
-        // Create a display which supports system decoration and allows reparenting stacks to
-        // another display when the display is removed.
-        final DisplayContent display = new TestDisplayContent.Builder(
-                mAtm, 1000, 1500).setSystemDecorations(true).build();
-        doReturn(false).when(display).shouldDestroyContentOnRemove();
-
-        // Put home stack on the display.
-        final Task homeStack = new TaskBuilder(mSupervisor)
-                .setDisplay(display).setActivityType(ACTIVITY_TYPE_HOME).build();
-
-        // Put a finishing standard activity which will be reparented.
-        final Task stack = createFullscreenStackWithSimpleActivityAt(display);
-        stack.topRunningActivity().makeFinishingLocked();
-
-        clearInvocations(homeStack);
-        display.remove();
-
-        // The removed display should have no focused stack and its home stack should never resume.
-        assertNull(display.getFocusedRootTask());
-        verify(homeStack, never()).resumeTopActivityUncheckedLocked(any(), any());
-    }
-
-    private Task createFullscreenStackWithSimpleActivityAt(DisplayContent display) {
-        final Task fullscreenStack = display.getDefaultTaskDisplayArea().createRootTask(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP);
-        final Task fullscreenTask = new TaskBuilder(mAtm.mTaskSupervisor)
-                .setParentTask(fullscreenStack).build();
-        new ActivityBuilder(mAtm).setTask(fullscreenTask).build();
-        return fullscreenStack;
-    }
-
-    /**
-     * Verifies the correct activity is returned when querying the top running activity.
-     */
-    @Test
-    public void testTopRunningActivity() {
-        final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
-        final KeyguardController keyguard = mSupervisor.getKeyguardController();
-        final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
-        final ActivityRecord activity = stack.getTopNonFinishingActivity();
-
-        // Create empty stack on top.
-        final Task emptyStack = new TaskBuilder(mSupervisor).build();
-
-        // Make sure the top running activity is not affected when keyguard is not locked.
-        assertTopRunningActivity(activity, display);
-
-        // Check to make sure activity not reported when it cannot show on lock and lock is on.
-        doReturn(true).when(keyguard).isKeyguardLocked();
-        assertEquals(activity, display.topRunningActivity());
-        assertNull(display.topRunningActivity(true /* considerKeyguardState */));
-
-        // Move stack with activity to top.
-        stack.moveToFront("testStackToFront");
-        assertEquals(stack, display.getFocusedRootTask());
-        assertEquals(activity, display.topRunningActivity());
-        assertNull(display.topRunningActivity(true /* considerKeyguardState */));
-
-        // Add activity that should be shown on the keyguard.
-        final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mAtm)
-                .setTask(stack)
-                .setActivityFlags(FLAG_SHOW_WHEN_LOCKED)
-                .build();
-
-        // Ensure the show when locked activity is returned.
-        assertTopRunningActivity(showWhenLockedActivity, display);
-
-        // 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.getFocusedRootTask());
-        assertTopRunningActivity(showWhenLockedActivity, display);
-    }
-
-    private static void assertTopRunningActivity(ActivityRecord top, DisplayContent display) {
-        assertEquals(top, display.topRunningActivity());
-        assertEquals(top, display.topRunningActivity(true /* considerKeyguardState */));
-    }
-
-    /**
-     * This test enforces that alwaysOnTop stack is placed at proper position.
-     */
-    @Test
-    public void testAlwaysOnTopStackLocation() {
-        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        final Task alwaysOnTopStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM,
-                ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(alwaysOnTopStack).build();
-        alwaysOnTopStack.setAlwaysOnTop(true);
-        taskDisplayArea.positionChildAt(POSITION_TOP, alwaysOnTopStack,
-                false /* includingParents */);
-        assertTrue(alwaysOnTopStack.isAlwaysOnTop());
-        // Ensure always on top state is synced to the children of the stack.
-        assertTrue(alwaysOnTopStack.getTopNonFinishingActivity().isAlwaysOnTop());
-        assertEquals(alwaysOnTopStack, taskDisplayArea.getTopRootTask());
-
-        final Task pinnedStack = taskDisplayArea.createRootTask(
-                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        assertEquals(pinnedStack, taskDisplayArea.getRootPinnedTask());
-        assertEquals(pinnedStack, taskDisplayArea.getTopRootTask());
-
-        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.getRootTaskCount() - 1;
-        // Ensure the new alwaysOnTop stack is put below the pinned stack, but on top of the
-        // existing alwaysOnTop stack.
-        assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopStack));
-
-        final Task nonAlwaysOnTopStack = taskDisplayArea.createRootTask(
-                WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        assertEquals(taskDisplayArea, nonAlwaysOnTopStack.getDisplayArea());
-        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(topPosition - 3, getTaskIndexOf(taskDisplayArea, nonAlwaysOnTopStack));
-
-        anotherAlwaysOnTopStack.setAlwaysOnTop(false);
-        taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopStack,
-                false /* includingParents */);
-        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(topPosition - 2, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopStack));
-        anotherAlwaysOnTopStack.setAlwaysOnTop(true);
-
-        // Ensure always on top state changes properly when windowing mode changes.
-        anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
-        assertEquals(topPosition - 2, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopStack));
-        anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
-        assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopStack));
-
-        final Task dreamStack = taskDisplayArea.createRootTask(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM, true /* onTop */);
-        assertEquals(taskDisplayArea, dreamStack.getDisplayArea());
-        assertTrue(dreamStack.isAlwaysOnTop());
-        topPosition = taskDisplayArea.getRootTaskCount() - 1;
-        // Ensure dream shows above all activities, including PiP
-        assertEquals(dreamStack, taskDisplayArea.getTopRootTask());
-        assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, pinnedStack));
-
-        final Task assistStack = taskDisplayArea.createRootTask(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
-        assertEquals(taskDisplayArea, assistStack.getDisplayArea());
-        assertFalse(assistStack.isAlwaysOnTop());
-        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(isAssistantOnTop ? topPosition : topPosition - 4,
-                getTaskIndexOf(taskDisplayArea, assistStack));
-    }
-
-    @Test
-    public void testRemoveRootTaskInWindowingModes() {
-        removeStackTests(() -> mRootWindowContainer.removeRootTasksInWindowingModes(
-                WINDOWING_MODE_FULLSCREEN));
-    }
-
-    @Test
-    public void testRemoveStackWithActivityTypes() {
-        removeStackTests(() -> mRootWindowContainer.removeRootTasksWithActivityTypes(
-                ACTIVITY_TYPE_STANDARD));
-    }
-
-    private void removeStackTests(Runnable runnable) {
-        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        final Task stack1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, ON_TOP);
-        final Task stack2 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, ON_TOP);
-        final Task stack3 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, ON_TOP);
-        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();
-        final Task task3 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(stack3).build();
-        final Task task4 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(stack4).build();
-
-        // Reordering stacks while removing stacks.
-        doAnswer(invocation -> {
-            taskDisplayArea.positionChildAt(POSITION_TOP, stack3, false /*includingParents*/);
-            return true;
-        }).when(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any());
-
-        // Removing stacks from the display while removing stacks.
-        doAnswer(invocation -> {
-            taskDisplayArea.removeRootTask(stack2);
-            return true;
-        }).when(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any());
-
-        runnable.run();
-        verify(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any());
-        verify(mSupervisor).removeTask(eq(task3), anyBoolean(), anyBoolean(), any());
-        verify(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any());
-        verify(mSupervisor).removeTask(eq(task1), anyBoolean(), anyBoolean(), any());
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index ce5fc40..678defe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -405,7 +405,7 @@
     public void testGetAnimationTargets_taskContainsMultipleTasks() {
         // [DisplayContent] - [Task] -+- [Task1] - [ActivityRecord1] (opening, invisible)
         //                            +- [Task2] - [ActivityRecord2] (closing, visible)
-        final Task parentTask = createTaskStackOnDisplay(mDisplayContent);
+        final Task parentTask = createTask(mDisplayContent);
         final ActivityRecord activity1 = createActivityRecordWithParentTask(parentTask);
         activity1.setVisible(false);
         activity1.mVisibleRequested = true;
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 83aca5e..c3279bf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -177,13 +177,13 @@
     }
 
     @Test
-    public void testCleanAppTransitionWhenTaskStackReparent() {
+    public void testCleanAppTransitionWhenRootTaskReparent() {
         // Create 2 displays & presume both display the state is ON for ready to display & animate.
         final DisplayContent dc1 = createNewDisplay(Display.STATE_ON);
         final DisplayContent dc2 = createNewDisplay(Display.STATE_ON);
 
-        final Task stack1 = createTaskStackOnDisplay(dc1);
-        final Task task1 = createTaskInStack(stack1, 0 /* userId */);
+        final Task rootTask1 = createTask(dc1);
+        final Task task1 = createTaskInRootTask(rootTask1, 0 /* userId */);
         final ActivityRecord activity1 = createNonAttachedActivityRecord(dc1);
         task1.addChild(activity1, 0);
 
@@ -198,8 +198,8 @@
         dc1.mOpeningApps.add(activity1);
         assertTrue(dc1.mOpeningApps.size() > 0);
 
-        // Move stack to another display.
-        stack1.reparent(dc2.getDefaultTaskDisplayArea(), true);
+        // Move root task to another display.
+        rootTask1.reparent(dc2.getDefaultTaskDisplayArea(), true);
 
         // Verify if token are cleared from both pending transition list in former display.
         assertFalse(dc1.mOpeningApps.contains(activity1));
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 24b4f65..0afd39f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -16,10 +16,12 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -65,6 +67,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
 import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
@@ -350,29 +353,29 @@
     }
 
     /**
-     * This tests stack movement between displays and proper stack's, task's and app token's display
-     * container references updates.
+     * This tests root task movement between displays and proper root task's, task's and app token's
+     * display container references updates.
      */
     @Test
-    public void testMoveStackBetweenDisplays() {
+    public void testMoveRootTaskBetweenDisplays() {
         // Create a second display.
         final DisplayContent dc = createNewDisplay();
 
-        // Add stack with activity.
-        final Task stack = createTaskStackOnDisplay(dc);
-        assertEquals(dc.getDisplayId(), stack.getDisplayContent().getDisplayId());
-        assertEquals(dc, stack.getDisplayContent());
+        // Add root task with activity.
+        final Task rootTask = createTask(dc);
+        assertEquals(dc.getDisplayId(), rootTask.getDisplayContent().getDisplayId());
+        assertEquals(dc, rootTask.getDisplayContent());
 
-        final Task task = createTaskInStack(stack, 0 /* userId */);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
         final ActivityRecord activity = createNonAttachedActivityRecord(dc);
         task.addChild(activity, 0);
         assertEquals(dc, task.getDisplayContent());
         assertEquals(dc, activity.getDisplayContent());
 
-        // Move stack to first display.
-        stack.reparent(mDisplayContent.getDefaultTaskDisplayArea(), true /* onTop */);
-        assertEquals(mDisplayContent.getDisplayId(), stack.getDisplayContent().getDisplayId());
-        assertEquals(mDisplayContent, stack.getDisplayContent());
+        // Move root task to first display.
+        rootTask.reparent(mDisplayContent.getDefaultTaskDisplayArea(), true /* onTop */);
+        assertEquals(mDisplayContent.getDisplayId(), rootTask.getDisplayContent().getDisplayId());
+        assertEquals(mDisplayContent, rootTask.getDisplayContent());
         assertEquals(mDisplayContent, task.getDisplayContent());
         assertEquals(mDisplayContent, activity.getDisplayContent());
     }
@@ -424,7 +427,7 @@
     }
 
     /**
-     * Tests tapping on a stack in different display results in window gaining focus.
+     * Tests tapping on a root task in different display results in window gaining focus.
      */
     @Test
     public void testInputEventBringsCorrectDisplayInFocus() {
@@ -432,16 +435,16 @@
         // Create a second display
         final DisplayContent dc1 = createNewDisplay();
 
-        // Add stack with activity.
-        final Task stack0 = createTaskStackOnDisplay(dc0);
-        final Task task0 = createTaskInStack(stack0, 0 /* userId */);
+        // Add root task with activity.
+        final Task rootTask0 = createTask(dc0);
+        final Task task0 = createTaskInRootTask(rootTask0, 0 /* userId */);
         final ActivityRecord activity = createNonAttachedActivityRecord(dc0);
         task0.addChild(activity, 0);
         dc0.configureDisplayPolicy();
         assertNotNull(dc0.mTapDetector);
 
-        final Task stack1 = createTaskStackOnDisplay(dc1);
-        final Task task1 = createTaskInStack(stack1, 0 /* userId */);
+        final Task rootTask1 = createTask(dc1);
+        final Task task1 = createTaskInRootTask(rootTask1, 0 /* userId */);
         final ActivityRecord activity1 = createNonAttachedActivityRecord(dc0);
         task1.addChild(activity1, 0);
         dc1.configureDisplayPolicy();
@@ -889,13 +892,13 @@
         final DisplayContent newDisplay = createNewDisplay();
 
         final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin");
-        final Task stack = mDisplayContent.getTopRootTask();
-        final ActivityRecord activity = stack.topRunningActivity();
+        final Task rootTask = mDisplayContent.getTopRootTask();
+        final ActivityRecord activity = rootTask.topRunningActivity();
         doReturn(true).when(activity).shouldBeVisibleUnchecked();
 
         final WindowState appWin1 = createWindow(null, TYPE_APPLICATION, newDisplay, "appWin1");
-        final Task stack1 = newDisplay.getTopRootTask();
-        final ActivityRecord activity1 = stack1.topRunningActivity();
+        final Task rootTask1 = newDisplay.getTopRootTask();
+        final ActivityRecord activity1 = rootTask1.topRunningActivity();
         doReturn(true).when(activity1).shouldBeVisibleUnchecked();
         appWin.setHasSurface(true);
         appWin1.setHasSurface(true);
@@ -934,28 +937,28 @@
         dc.getDisplayRotation().setFixedToUserRotation(
                 IWindowManager.FIXED_TO_USER_ROTATION_DISABLED);
 
-        final Task stack = new TaskBuilder(mSupervisor)
+        final Task rootTask = new TaskBuilder(mSupervisor)
                 .setDisplay(dc)
                 .setCreateActivity(true)
                 .build();
-        doReturn(true).when(stack).isVisible();
+        doReturn(true).when(rootTask).isVisible();
 
-        final Task freeformStack = new TaskBuilder(mSupervisor)
+        final Task freeformRootTask = new TaskBuilder(mSupervisor)
                 .setDisplay(dc)
                 .setCreateActivity(true)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM)
                 .build();
-        doReturn(true).when(freeformStack).isVisible();
-        freeformStack.getTopChild().setBounds(100, 100, 300, 400);
+        doReturn(true).when(freeformRootTask).isVisible();
+        freeformRootTask.getTopChild().setBounds(100, 100, 300, 400);
 
         assertTrue(dc.getDefaultTaskDisplayArea().isRootTaskVisible(WINDOWING_MODE_FREEFORM));
 
-        freeformStack.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-        stack.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_PORTRAIT);
+        freeformRootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        rootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_PORTRAIT);
         assertEquals(SCREEN_ORIENTATION_PORTRAIT, dc.getOrientation());
 
-        stack.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-        freeformStack.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_PORTRAIT);
+        rootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        freeformRootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_PORTRAIT);
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
     }
 
@@ -1683,66 +1686,6 @@
     }
 
     @Test
-    public void testGetOrCreateRootHomeTask_defaultDisplay() {
-        TaskDisplayArea defaultTaskDisplayArea = mWm.mRoot.getDefaultTaskDisplayArea();
-
-        // Remove the current home stack if it exists so a new one can be created below.
-        Task homeTask = defaultTaskDisplayArea.getRootHomeTask();
-        if (homeTask != null) {
-            defaultTaskDisplayArea.removeChild(homeTask);
-        }
-        assertNull(defaultTaskDisplayArea.getRootHomeTask());
-
-        assertNotNull(defaultTaskDisplayArea.getOrCreateRootHomeTask());
-    }
-
-    @Test
-    public void testGetOrCreateRootHomeTask_supportedSecondaryDisplay() {
-        DisplayContent display = createNewDisplay();
-        doReturn(true).when(display).supportsSystemDecorations();
-
-        // Remove the current home stack if it exists so a new one can be created below.
-        TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
-        Task homeTask = taskDisplayArea.getRootHomeTask();
-        if (homeTask != null) {
-            taskDisplayArea.removeChild(homeTask);
-        }
-        assertNull(taskDisplayArea.getRootHomeTask());
-
-        assertNotNull(taskDisplayArea.getOrCreateRootHomeTask());
-    }
-
-    @Test
-    public void testGetOrCreateRootHomeTask_unsupportedSystemDecorations() {
-        DisplayContent display = createNewDisplay();
-        TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
-        doReturn(false).when(display).supportsSystemDecorations();
-
-        assertNull(taskDisplayArea.getRootHomeTask());
-        assertNull(taskDisplayArea.getOrCreateRootHomeTask());
-    }
-
-    @Test
-    public void testGetOrCreateRootHomeTask_untrustedDisplay() {
-        DisplayContent display = createNewDisplay();
-        TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
-        doReturn(false).when(display).isTrusted();
-
-        assertNull(taskDisplayArea.getRootHomeTask());
-        assertNull(taskDisplayArea.getOrCreateRootHomeTask());
-    }
-
-    @Test
-    public void testGetOrCreateRootHomeTask_dontMoveToTop() {
-        DisplayContent display = createNewDisplay();
-        display.mDontMoveToTop = true;
-        TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
-
-        assertNull(taskDisplayArea.getRootHomeTask());
-        assertNull(taskDisplayArea.getOrCreateRootHomeTask());
-    }
-
-    @Test
     public void testValidWindowingLayer() {
         final SurfaceControl windowingLayer = mDisplayContent.getWindowingLayer();
         assertNotNull(windowingLayer);
@@ -1761,8 +1704,8 @@
     @Test
     public void testFindScrollCaptureTargetWindow_behindWindow() {
         DisplayContent display = createNewDisplay();
-        Task stack = createTaskStackOnDisplay(display);
-        Task task = createTaskInStack(stack, 0 /* userId */);
+        Task rootTask = createTask(display);
+        Task task = createTaskInRootTask(rootTask, 0 /* userId */);
         WindowState activityWindow = createAppWindow(task, TYPE_APPLICATION, "App Window");
         WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot");
 
@@ -1774,8 +1717,8 @@
     @Test
     public void testFindScrollCaptureTargetWindow_cantReceiveKeys() {
         DisplayContent display = createNewDisplay();
-        Task stack = createTaskStackOnDisplay(display);
-        Task task = createTaskInStack(stack, 0 /* userId */);
+        Task rootTask = createTask(display);
+        Task task = createTaskInRootTask(rootTask, 0 /* userId */);
         WindowState activityWindow = createAppWindow(task, TYPE_APPLICATION, "App Window");
         WindowState invisible = createWindow(null, TYPE_APPLICATION, "invisible");
         invisible.mViewVisibility = View.INVISIBLE;  // make canReceiveKeys return false
@@ -1788,8 +1731,8 @@
     @Test
     public void testFindScrollCaptureTargetWindow_taskId() {
         DisplayContent display = createNewDisplay();
-        Task stack = createTaskStackOnDisplay(display);
-        Task task = createTaskInStack(stack, 0 /* userId */);
+        Task rootTask = createTask(display);
+        Task task = createTaskInRootTask(rootTask, 0 /* userId */);
         WindowState window = createAppWindow(task, TYPE_APPLICATION, "App Window");
         WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot");
 
@@ -1800,8 +1743,8 @@
     @Test
     public void testFindScrollCaptureTargetWindow_taskIdCantReceiveKeys() {
         DisplayContent display = createNewDisplay();
-        Task stack = createTaskStackOnDisplay(display);
-        Task task = createTaskInStack(stack, 0 /* userId */);
+        Task rootTask = createTask(display);
+        Task task = createTaskInRootTask(rootTask, 0 /* userId */);
         WindowState window = createAppWindow(task, TYPE_APPLICATION, "App Window");
         window.mViewVisibility = View.INVISIBLE;  // make canReceiveKeys return false
         WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot");
@@ -1828,7 +1771,7 @@
     @Test
     public void testSetWindowingModeAtomicallyUpdatesWindoingModeAndDisplayWindowingMode() {
         final DisplayContent dc = createNewDisplay();
-        final Task stack = new TaskBuilder(mSupervisor)
+        final Task rootTask = new TaskBuilder(mSupervisor)
                 .setDisplay(dc)
                 .build();
         doAnswer(invocation -> {
@@ -1837,7 +1780,7 @@
             assertEquals(config.windowConfiguration.getWindowingMode(),
                     config.windowConfiguration.getDisplayWindowingMode());
             return null;
-        }).when(stack).onConfigurationChanged(any());
+        }).when(rootTask).onConfigurationChanged(any());
         dc.setWindowingMode(WINDOWING_MODE_FREEFORM);
         dc.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
     }
@@ -2072,6 +2015,130 @@
                 0 /* delta */);
     }
 
+    /**
+     * Verifies {@link DisplayContent#remove} should not resume home root task on the removing
+     * display.
+     */
+    @Test
+    public void testNotResumeHomeRootTaskOnRemovingDisplay() {
+        // Create a display which supports system decoration and allows reparenting root tasks to
+        // another display when the display is removed.
+        final DisplayContent display = new TestDisplayContent.Builder(
+                mAtm, 1000, 1500).setSystemDecorations(true).build();
+        doReturn(false).when(display).shouldDestroyContentOnRemove();
+
+        // Put home root task on the display.
+        final Task homeRootTask = new TaskBuilder(mSupervisor)
+                .setDisplay(display).setActivityType(ACTIVITY_TYPE_HOME).build();
+
+        // Put a finishing standard activity which will be reparented.
+        final Task rootTask = createTaskWithActivity(display.getDefaultTaskDisplayArea(),
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP, true /* twoLevelTask */);
+        rootTask.topRunningActivity().makeFinishingLocked();
+
+        clearInvocations(homeRootTask);
+        display.remove();
+
+        // The removed display should have no focused root task and its home root task should never
+        // resume.
+        assertNull(display.getFocusedRootTask());
+        verify(homeRootTask, never()).resumeTopActivityUncheckedLocked(any(), any());
+    }
+
+    /**
+     * Verifies the correct activity is returned when querying the top running activity.
+     */
+    @Test
+    public void testTopRunningActivity() {
+        final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
+        final KeyguardController keyguard = mSupervisor.getKeyguardController();
+        final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
+        final ActivityRecord activity = rootTask.getTopNonFinishingActivity();
+
+        // Create empty root task on top.
+        final Task emptyRootTask = new TaskBuilder(mSupervisor).build();
+
+        // Make sure the top running activity is not affected when keyguard is not locked.
+        assertTopRunningActivity(activity, display);
+
+        // Check to make sure activity not reported when it cannot show on lock and lock is on.
+        doReturn(true).when(keyguard).isKeyguardLocked();
+        assertEquals(activity, display.topRunningActivity());
+        assertNull(display.topRunningActivity(true /* considerKeyguardState */));
+
+        // Move root task with activity to top.
+        rootTask.moveToFront("testRootTaskToFront");
+        assertEquals(rootTask, display.getFocusedRootTask());
+        assertEquals(activity, display.topRunningActivity());
+        assertNull(display.topRunningActivity(true /* considerKeyguardState */));
+
+        // Add activity that should be shown on the keyguard.
+        final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mAtm)
+                .setTask(rootTask)
+                .setActivityFlags(FLAG_SHOW_WHEN_LOCKED)
+                .build();
+
+        // Ensure the show when locked activity is returned.
+        assertTopRunningActivity(showWhenLockedActivity, display);
+
+        // Move empty root task to front. The running activity in focusable root task which below
+        // the empty root task should be returned.
+        emptyRootTask.moveToFront("emptyRootTaskToFront");
+        assertEquals(rootTask, display.getFocusedRootTask());
+        assertTopRunningActivity(showWhenLockedActivity, display);
+    }
+
+    private static void assertTopRunningActivity(ActivityRecord top, DisplayContent display) {
+        assertEquals(top, display.topRunningActivity());
+        assertEquals(top, display.topRunningActivity(true /* considerKeyguardState */));
+    }
+
+    @Test
+    public void testRemoveRootTaskInWindowingModes() {
+        removeRootTaskTests(() -> mRootWindowContainer.removeRootTasksInWindowingModes(
+                WINDOWING_MODE_FULLSCREEN));
+    }
+
+    @Test
+    public void testRemoveRootTaskWithActivityTypes() {
+        removeRootTaskTests(() -> mRootWindowContainer.removeRootTasksWithActivityTypes(
+                ACTIVITY_TYPE_STANDARD));
+    }
+
+    private void removeRootTaskTests(Runnable runnable) {
+        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+        final Task rootTask1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, ON_TOP);
+        final Task rootTask2 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, ON_TOP);
+        final Task rootTask3 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, ON_TOP);
+        final Task rootTask4 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, ON_TOP);
+        final Task task1 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask1).build();
+        final Task task2 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask2).build();
+        final Task task3 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask3).build();
+        final Task task4 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask4).build();
+
+        // Reordering root tasks while removing root tasks.
+        doAnswer(invocation -> {
+            taskDisplayArea.positionChildAt(POSITION_TOP, rootTask3, false /*includingParents*/);
+            return true;
+        }).when(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any());
+
+        // Removing root tasks from the display while removing root tasks.
+        doAnswer(invocation -> {
+            taskDisplayArea.removeRootTask(rootTask2);
+            return true;
+        }).when(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any());
+
+        runnable.run();
+        verify(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any());
+        verify(mSupervisor).removeTask(eq(task3), anyBoolean(), anyBoolean(), any());
+        verify(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any());
+        verify(mSupervisor).removeTask(eq(task1), anyBoolean(), anyBoolean(), any());
+    }
+
     private boolean isOptionsPanelAtRight(int displayId) {
         return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 4e2697a..1bddd7b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -17,8 +17,6 @@
 package com.android.server.wm;
 
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
 import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
 import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
@@ -124,9 +122,8 @@
      */
     private WindowState createDropTargetWindow(String name, int ownerId) {
         final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
-        final Task stack = createTaskStackOnDisplay(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
-        final Task task = createTaskInStack(stack, ownerId);
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask, ownerId);
         task.addChild(activity, 0);
 
         // Use a new TestIWindow so we don't collect events for other windows
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 f97e794..7d137bc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -488,9 +488,9 @@
     @Test
     public void testIsAnimatingByRecents() {
         final ActivityRecord homeActivity = createHomeActivity();
-        final Task rootTask = createTaskStackOnDisplay(mDefaultDisplay);
-        final Task childTask = createTaskInStack(rootTask, 0 /* userId */);
-        final Task leafTask = createTaskInStack(childTask, 0 /* userId */);
+        final Task rootTask = createTask(mDefaultDisplay);
+        final Task childTask = createTaskInRootTask(rootTask, 0 /* userId */);
+        final Task leafTask = createTaskInRootTask(childTask, 0 /* userId */);
         spyOn(leafTask);
         doReturn(true).when(leafTask).isVisible();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
deleted file mode 100644
index 8388f2a..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ /dev/null
@@ -1,953 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.TYPE_VIRTUAL;
-import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.contains;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.refEq;
-
-import android.app.ActivityOptions;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ResolveInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.platform.test.annotations.Presubmit;
-import android.util.MergedConfiguration;
-import android.util.Pair;
-
-import androidx.test.filters.MediumTest;
-
-import com.android.internal.app.ResolverActivity;
-import com.android.server.wm.Task.ActivityState;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * Tests for the {@link RootWindowContainer} class.
- *
- * Build/Install/Run:
- *  atest WmTests:RootActivityContainerTests
- */
-@MediumTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class RootActivityContainerTests extends WindowTestsBase {
-
-    @Before
-    public void setUp() throws Exception {
-        doNothing().when(mAtm).updateSleepIfNeededLocked();
-    }
-
-    /**
-     * This test ensures that we do not try to restore a task based off an invalid task id. We
-     * should expect {@code null} to be returned in this case.
-     */
-    @Test
-    public void testRestoringInvalidTask() {
-        mRootWindowContainer.getDefaultDisplay().removeAllTasks();
-        Task task = mRootWindowContainer.anyTaskForId(0 /*taskId*/,
-                MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
-        assertNull(task);
-    }
-
-    /**
-     * This test ensures that an existing task in the pinned stack is moved to the fullscreen
-     * activity stack when a new task is added.
-     */
-    @Test
-    public void testReplacingTaskInPinnedStack() {
-        Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
-                .setTask(fullscreenTask).build();
-        final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
-                .setTask(fullscreenTask).build();
-
-        fullscreenTask.moveToFront("testReplacingTaskInPinnedStack");
-
-        // Ensure full screen stack has both tasks.
-        ensureStackPlacement(fullscreenTask, firstActivity, secondActivity);
-
-        // Move first activity to pinned stack.
-        mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, "initialMove");
-
-        final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea();
-        Task pinnedStack = taskDisplayArea.getRootPinnedTask();
-        // Ensure a task has moved over.
-        ensureStackPlacement(pinnedStack, firstActivity);
-        ensureStackPlacement(fullscreenTask, secondActivity);
-
-        // Move second activity to pinned stack.
-        mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "secondMove");
-
-        // Need to get stacks again as a new instance might have been created.
-        pinnedStack = taskDisplayArea.getRootPinnedTask();
-        fullscreenTask = taskDisplayArea.getRootTask(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD);
-        // Ensure stacks have swapped tasks.
-        ensureStackPlacement(pinnedStack, secondActivity);
-        ensureStackPlacement(fullscreenTask, firstActivity);
-    }
-
-    @Test
-    public void testMovingBottomMostStackActivityToPinnedStack() {
-        final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
-                .setTask(fullscreenTask).build();
-        final Task task = firstActivity.getTask();
-
-        final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
-                .setTask(fullscreenTask).build();
-
-        fullscreenTask.moveTaskToBack(task);
-
-        // Ensure full screen stack has both tasks.
-        ensureStackPlacement(fullscreenTask, firstActivity, secondActivity);
-        assertEquals(task.getTopMostActivity(), secondActivity);
-        firstActivity.setState(STOPPED, "testMovingBottomMostStackActivityToPinnedStack");
-
-
-        // Move first activity to pinned stack.
-        mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "initialMove");
-
-        assertTrue(firstActivity.mRequestForceTransition);
-    }
-
-    private static void ensureStackPlacement(Task task, ActivityRecord... activities) {
-        final ArrayList<ActivityRecord> taskActivities = new ArrayList<>();
-
-        task.forAllActivities((Consumer<ActivityRecord>) taskActivities::add, false);
-
-        assertEquals("Expecting " + Arrays.deepToString(activities) + " got " + taskActivities,
-                taskActivities.size(), activities != null ? activities.length : 0);
-
-        if (activities == null) {
-            return;
-        }
-
-        for (ActivityRecord activity : activities) {
-            assertTrue(taskActivities.contains(activity));
-        }
-    }
-
-    @Test
-    public void testApplySleepTokens() {
-        final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
-        final KeyguardController keyguard = mSupervisor.getKeyguardController();
-        final Task stack = new TaskBuilder(mSupervisor)
-                .setDisplay(display)
-                .setOnTop(false)
-                .build();
-
-        // Make sure we wake and resume in the case the display is turning on and the keyguard is
-        // not showing.
-        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
-                false /* displayShouldSleep */, true /* isFocusedStack */,
-                false /* keyguardShowing */, true /* expectWakeFromSleep */,
-                true /* expectResumeTopActivity */);
-
-        // Make sure we wake and don't resume when the display is turning on and the keyguard is
-        // showing.
-        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
-                false /* displayShouldSleep */, true /* isFocusedStack */,
-                true /* keyguardShowing */, true /* expectWakeFromSleep */,
-                false /* expectResumeTopActivity */);
-
-        // Make sure we wake and don't resume when the display is turning on and the keyguard is
-        // not showing as unfocused.
-        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
-                false /* displayShouldSleep */, false /* isFocusedStack */,
-                false /* keyguardShowing */, true /* expectWakeFromSleep */,
-                false /* expectResumeTopActivity */);
-
-        // Should not do anything if the display state hasn't changed.
-        verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/,
-                false /* displayShouldSleep */, true /* isFocusedStack */,
-                false /* keyguardShowing */, false /* expectWakeFromSleep */,
-                false /* expectResumeTopActivity */);
-    }
-
-    private void verifySleepTokenBehavior(DisplayContent display, KeyguardController keyguard,
-            Task stack, boolean displaySleeping, boolean displayShouldSleep,
-            boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep,
-            boolean expectResumeTopActivity) {
-        reset(stack);
-
-        doReturn(displayShouldSleep).when(display).shouldSleep();
-        doReturn(displaySleeping).when(display).isSleeping();
-        doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
-
-        doReturn(isFocusedStack).when(stack).isFocusedRootTaskOnDisplay();
-        doReturn(isFocusedStack ? stack : null).when(display).getFocusedRootTask();
-        TaskDisplayArea defaultTaskDisplayArea = display.getDefaultTaskDisplayArea();
-        doReturn(isFocusedStack ? stack : null).when(defaultTaskDisplayArea).getFocusedRootTask();
-        mRootWindowContainer.applySleepTokens(true);
-        verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
-        verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
-                null /* target */, null /* targetOptions */);
-    }
-
-    @Test
-    public void testAwakeFromSleepingWithAppConfiguration() {
-        final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
-        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
-        activity.moveFocusableActivityToTop("test");
-        assertTrue(activity.getRootTask().isFocusedRootTaskOnDisplay());
-        ActivityRecordTests.setRotatedScreenOrientationSilently(activity);
-
-        final Configuration rotatedConfig = new Configuration();
-        display.computeScreenConfiguration(rotatedConfig, display.getDisplayRotation()
-                .rotationForOrientation(activity.getOrientation(), display.getRotation()));
-        assertNotEquals(activity.getConfiguration().orientation, rotatedConfig.orientation);
-        // Assume the activity was shown in different orientation. For example, the top activity is
-        // landscape and the portrait lockscreen is shown.
-        activity.setLastReportedConfiguration(
-                new MergedConfiguration(mAtm.getGlobalConfiguration(), rotatedConfig));
-        activity.setState(ActivityState.STOPPED, "sleep");
-
-        display.setIsSleeping(true);
-        doReturn(false).when(display).shouldSleep();
-        // Allow to resume when awaking.
-        setBooted(mAtm);
-        mRootWindowContainer.applySleepTokens(true);
-
-        // The display orientation should be changed by the activity so there is no relaunch.
-        verify(activity, never()).relaunchActivityLocked(anyBoolean());
-        assertEquals(rotatedConfig.orientation, display.getConfiguration().orientation);
-    }
-
-    /**
-     * Verifies that removal of activity with task and stack is done correctly.
-     */
-    @Test
-    public void testRemovingStackOnAppCrash() {
-        final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
-                .getDefaultTaskDisplayArea();
-        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.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.getRootTaskCount());
-    }
-
-    /**
-     * Verifies that removal of activities with task and stack is done correctly when there are
-     * several task display areas.
-     */
-    @Test
-    public void testRemovingStackOnAppCrash_multipleDisplayAreas() {
-        final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
-                .getDefaultTaskDisplayArea();
-        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.getRootTaskCount());
-
-        final DisplayContent dc = defaultTaskDisplayArea.getDisplayContent();
-        final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
-                dc, mRootWindowContainer.mWmService, "TestTaskDisplayArea", FEATURE_VENDOR_FIRST);
-        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.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.getRootTaskCount());
-        assertEquals(0, secondTaskDisplayArea.getRootTaskCount());
-    }
-
-    @Test
-    public void testFocusability() {
-        final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
-                .getDefaultTaskDisplayArea();
-        final Task stack = defaultTaskDisplayArea.createRootTask(
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build();
-
-        // Created stacks are focusable by default.
-        assertTrue(stack.isTopActivityFocusable());
-        assertTrue(activity.isFocusable());
-
-        // If the stack is made unfocusable, its activities should inherit that.
-        stack.setFocusable(false);
-        assertFalse(stack.isTopActivityFocusable());
-        assertFalse(activity.isFocusable());
-
-        final Task pinnedStack = defaultTaskDisplayArea.createRootTask(
-                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm)
-                .setTask(pinnedStack).build();
-
-        // We should not be focusable when in pinned mode
-        assertFalse(pinnedStack.isTopActivityFocusable());
-        assertFalse(pinnedActivity.isFocusable());
-
-        // Add flag forcing focusability.
-        pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE;
-
-        // Task with FLAG_ALWAYS_FOCUSABLE should be focusable.
-        assertTrue(pinnedStack.isTopActivityFocusable());
-        assertTrue(pinnedActivity.isFocusable());
-    }
-
-    /**
-     * Verify that split-screen primary stack will be chosen if activity is launched that targets
-     * split-screen secondary, but a matching existing instance is found on top of split-screen
-     * primary stack.
-     */
-    @Test
-    public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() {
-        // Create primary split-screen stack with a task and an activity.
-        final Task primaryStack = mRootWindowContainer.getDefaultTaskDisplayArea()
-                .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();
-
-        // Find a launch stack for the top activity in split-screen primary, while requesting
-        // split-screen secondary.
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
-        final Task result =
-                mRootWindowContainer.getLaunchRootTask(r, options, task, true /* onTop */);
-
-        // Assert that the primary stack is returned.
-        assertEquals(primaryStack, result);
-    }
-
-    /**
-     * Verify that home stack would be moved to front when the top activity is Recents.
-     */
-    @Test
-    public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
-        // Create stack/task on default display.
-        final Task targetStack = new TaskBuilder(mSupervisor)
-                .setCreateActivity(true)
-                .setOnTop(false)
-                .build();
-        final Task targetTask = targetStack.getBottomMostTask();
-
-        // Create Recents on top of the display.
-        final Task stack = new TaskBuilder(mSupervisor)
-                .setCreateActivity(true)
-                .setActivityType(ACTIVITY_TYPE_RECENTS)
-                .build();
-
-        final String reason = "findTaskToMoveToFront";
-        mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
-                false);
-
-        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        verify(taskDisplayArea).moveHomeRootTaskToFront(contains(reason));
-    }
-
-    /**
-     * Verify that home stack won't be moved to front if the top activity on other display is
-     * Recents.
-     */
-    @Test
-    public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() {
-        // Create tasks on default display.
-        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        final Task targetRootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final Task targetTask = new TaskBuilder(mSupervisor).setParentTask(targetRootTask).build();
-
-        // Create Recents on secondary display.
-        final TestDisplayContent secondDisplay = addNewDisplayContentAt(
-                DisplayContent.POSITION_TOP);
-        final Task rootTask = secondDisplay.getDefaultTaskDisplayArea()
-                .createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
-        new ActivityBuilder(mAtm).setTask(rootTask).build();
-
-        final String reason = "findTaskToMoveToFront";
-        mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
-                false);
-
-        verify(taskDisplayArea, never()).moveHomeRootTaskToFront(contains(reason));
-    }
-
-    /**
-     * Verify if a stack is not at the topmost position, it should be able to resume its activity if
-     * the stack is the top focused.
-     */
-    @Test
-    public void testResumeActivityWhenNonTopmostStackIsTopFocused() {
-        // Create a root task at bottom.
-        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, false /* onTop */));
-        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build();
-        taskDisplayArea.positionChildAt(POSITION_BOTTOM, rootTask, false /*includingParents*/);
-
-        // Assume the task is not at the topmost position (e.g. behind always-on-top stacks) but it
-        // is the current top focused task.
-        assertFalse(rootTask.isTopRootTaskInDisplayArea());
-        doReturn(rootTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
-
-        // Use the task as target to resume.
-        mRootWindowContainer.resumeFocusedTasksTopActivities(
-                rootTask, activity, null /* targetOptions */);
-
-        // Verify the target task should resume its activity.
-        verify(rootTask, times(1)).resumeTopActivityUncheckedLocked(
-                eq(activity), eq(null /* targetOptions */));
-    }
-
-    /**
-     * Verify that home activity will be started on a display even if another display has a
-     * focusable activity.
-     */
-    @Test
-    public void testResumeFocusedStacksStartsHomeActivity_NoActivities() {
-        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        taskDisplayArea.getRootHomeTask().removeIfPossible();
-        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.resumeFocusedTasksTopActivities();
-
-        // Verify that home activity was started on the default display
-        verify(mRootWindowContainer).resumeHomeActivity(any(), any(), eq(taskDisplayArea));
-    }
-
-    /**
-     * Verify that home activity will be started on a display even if another display has a
-     * focusable activity.
-     */
-    @Test
-    public void testResumeFocusedStacksStartsHomeActivity_ActivityOnSecondaryScreen() {
-        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        taskDisplayArea.getRootHomeTask().removeIfPossible();
-        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 rootTask = secondDisplay.getDefaultTaskDisplayArea().createRootTask(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        new ActivityBuilder(mAtm).setTask(rootTask).build();
-
-        doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any());
-
-        mAtm.setBooted(true);
-
-        // Trigger resume on all displays
-        mRootWindowContainer.resumeFocusedTasksTopActivities();
-
-        // Verify that home activity was started on the default display
-        verify(mRootWindowContainer).resumeHomeActivity(any(), any(), eq(taskDisplayArea));
-    }
-
-    /**
-     * Verify that a lingering transition is being executed in case the activity to be resumed is
-     * already resumed
-     */
-    @Test
-    public void testResumeActivityLingeringTransition() {
-        // Create a root task at top.
-        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, false /* onTop */));
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(rootTask).setOnTop(true).build();
-        activity.setState(ActivityState.RESUMED, "test");
-
-        // Assume the task is at the topmost position
-        assertTrue(rootTask.isTopRootTaskInDisplayArea());
-
-        // Use the task as target to resume.
-        mRootWindowContainer.resumeFocusedTasksTopActivities();
-
-        // Verify the lingering app transition is being executed because it's already resumed
-        verify(rootTask, times(1)).executeAppTransition(any());
-    }
-
-    @Test
-    public void testResumeActivityLingeringTransition_notExecuted() {
-        // Create a root task at bottom.
-        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, false /* onTop */));
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(rootTask).setOnTop(true).build();
-        activity.setState(ActivityState.RESUMED, "test");
-        taskDisplayArea.positionChildAt(POSITION_BOTTOM, rootTask, false /*includingParents*/);
-
-        // Assume the task is at the topmost position
-        assertFalse(rootTask.isTopRootTaskInDisplayArea());
-        doReturn(rootTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
-
-        // Use the task as target to resume.
-        mRootWindowContainer.resumeFocusedTasksTopActivities();
-
-        // Verify the lingering app transition is being executed because it's already resumed
-        verify(rootTask, never()).executeAppTransition(any());
-    }
-
-    /**
-     * Tests that home activities can be started on the displays that supports system decorations.
-     */
-    @Test
-    public void testStartHomeOnAllDisplays() {
-        mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
-        mockResolveSecondaryHomeActivity();
-
-        // Create secondary displays.
-        final TestDisplayContent secondDisplay =
-                new TestDisplayContent.Builder(mAtm, 1000, 1500)
-                        .setSystemDecorations(true).build();
-
-        doReturn(true).when(mRootWindowContainer)
-                .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
-        doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
-                anyBoolean());
-
-        mRootWindowContainer.startHomeOnAllDisplays(0, "testStartHome");
-
-        assertTrue(mRootWindowContainer.getDefaultDisplay().getTopRootTask().isActivityTypeHome());
-        assertNotNull(secondDisplay.getTopRootTask());
-        assertTrue(secondDisplay.getTopRootTask().isActivityTypeHome());
-    }
-
-    /**
-     * Tests that home activities won't be started before booting when display added.
-     */
-    @Test
-    public void testNotStartHomeBeforeBoot() {
-        final int displayId = 1;
-        final boolean isBooting = mAtm.mAmInternal.isBooting();
-        final boolean isBooted = mAtm.mAmInternal.isBooted();
-        try {
-            mAtm.mAmInternal.setBooting(false);
-            mAtm.mAmInternal.setBooted(false);
-            mRootWindowContainer.onDisplayAdded(displayId);
-            verify(mRootWindowContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt());
-        } finally {
-            mAtm.mAmInternal.setBooting(isBooting);
-            mAtm.mAmInternal.setBooted(isBooted);
-        }
-    }
-
-    /**
-     * Tests whether home can be started if being instrumented.
-     */
-    @Test
-    public void testCanStartHomeWhenInstrumented() {
-        final ActivityInfo info = new ActivityInfo();
-        info.applicationInfo = new ApplicationInfo();
-        final WindowProcessController app = mock(WindowProcessController.class);
-        doReturn(app).when(mAtm).getProcessController(any(), anyInt());
-
-        // Can not start home if we don't want to start home while home is being instrumented.
-        doReturn(true).when(app).isInstrumenting();
-        final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
-                .getDefaultTaskDisplayArea();
-        assertFalse(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
-                false /* allowInstrumenting*/));
-
-        // Can start home for other cases.
-        assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
-                true /* allowInstrumenting*/));
-
-        doReturn(false).when(app).isInstrumenting();
-        assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
-                false /* allowInstrumenting*/));
-        assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
-                true /* allowInstrumenting*/));
-    }
-
-    /**
-     * Tests that secondary home activity should not be resolved if device is still locked.
-     */
-    @Test
-    public void testStartSecondaryHomeOnDisplayWithUserKeyLocked() {
-        // Create secondary displays.
-        final TestDisplayContent secondDisplay =
-                new TestDisplayContent.Builder(mAtm, 1000, 1500)
-                        .setSystemDecorations(true).build();
-
-        // Use invalid user id to let StorageManager.isUserKeyUnlocked() return false.
-        final int currentUser = mRootWindowContainer.mCurrentUser;
-        mRootWindowContainer.mCurrentUser = -1;
-
-        mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "testStartSecondaryHome",
-                secondDisplay.mDisplayId, true /* allowInstrumenting */, true /* fromHomeKey */);
-
-        try {
-            verify(mRootWindowContainer, never()).resolveSecondaryHomeActivity(anyInt(), any());
-        } finally {
-            mRootWindowContainer.mCurrentUser = currentUser;
-        }
-    }
-
-    /**
-     * Tests that secondary home activity should not be resolved if display does not support system
-     * decorations.
-     */
-    @Test
-    public void testStartSecondaryHomeOnDisplayWithoutSysDecorations() {
-        // Create secondary displays.
-        final TestDisplayContent secondDisplay =
-                new TestDisplayContent.Builder(mAtm, 1000, 1500)
-                        .setSystemDecorations(false).build();
-
-        mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "testStartSecondaryHome",
-                secondDisplay.mDisplayId, true /* allowInstrumenting */, true /* fromHomeKey */);
-
-        verify(mRootWindowContainer, never()).resolveSecondaryHomeActivity(anyInt(), any());
-    }
-
-    /**
-     * Tests that when starting {@link #ResolverActivity} for home, it should use the standard
-     * activity type (in a new stack) so the order of back stack won't be broken.
-     */
-    @Test
-    public void testStartResolverActivityForHome() {
-        final ActivityInfo info = new ActivityInfo();
-        info.applicationInfo = new ApplicationInfo();
-        info.applicationInfo.packageName = "android";
-        info.name = ResolverActivity.class.getName();
-        doReturn(info).when(mRootWindowContainer).resolveHomeActivity(anyInt(), any());
-
-        mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "test", DEFAULT_DISPLAY);
-        final ActivityRecord resolverActivity = mRootWindowContainer.topRunningActivity();
-
-        assertEquals(info, resolverActivity.info);
-        assertEquals(ACTIVITY_TYPE_STANDARD, resolverActivity.getRootTask().getActivityType());
-    }
-
-    /**
-     * Tests that secondary home should be selected if primary home not set.
-     */
-    @Test
-    public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSet() {
-        // Setup: primary home not set.
-        final Intent primaryHomeIntent = mAtm.getHomeIntent();
-        final ActivityInfo aInfoPrimary = new ActivityInfo();
-        aInfoPrimary.name = ResolverActivity.class.getName();
-        doReturn(aInfoPrimary).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
-                refEq(primaryHomeIntent));
-        // Setup: set secondary home.
-        mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */);
-
-        // Run the test.
-        final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
-                .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
-        final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
-        assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
-        assertEquals(aInfoSecondary.applicationInfo.packageName,
-                resolvedInfo.first.applicationInfo.packageName);
-    }
-
-    /**
-     * Tests that the default secondary home activity is always picked when it is in forced by
-     * config_useSystemProvidedLauncherForSecondary.
-     */
-    @Test
-    public void testResolveSecondaryHomeActivityForced() {
-        // SetUp: set primary home.
-        mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
-        // SetUp: set secondary home and force it.
-        mockResolveHomeActivity(false /* primaryHome */, true /* forceSystemProvided */);
-        final Intent secondaryHomeIntent =
-                mAtm.getSecondaryHomeIntent(null /* preferredPackage */);
-        final List<ResolveInfo> resolutions = new ArrayList<>();
-        final ResolveInfo resolveInfo = new ResolveInfo();
-        final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
-        resolveInfo.activityInfo = aInfoSecondary;
-        resolutions.add(resolveInfo);
-        doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(),
-                refEq(secondaryHomeIntent));
-        doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
-                anyBoolean());
-
-        // Run the test.
-        final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
-                .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
-        assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
-        assertEquals(aInfoSecondary.applicationInfo.packageName,
-                resolvedInfo.first.applicationInfo.packageName);
-    }
-
-    /**
-     * Tests that secondary home should be selected if primary home not support secondary displays
-     * or there is no matched activity in the same package as selected primary home.
-     */
-    @Test
-    public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSupportMultiDisplay() {
-        // Setup: there is no matched activity in the same package as selected primary home.
-        mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
-        final List<ResolveInfo> resolutions = new ArrayList<>();
-        doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
-        // Setup: set secondary home.
-        mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */);
-
-        // Run the test.
-        final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
-                .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
-        final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
-        assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
-        assertEquals(aInfoSecondary.applicationInfo.packageName,
-                resolvedInfo.first.applicationInfo.packageName);
-    }
-    /**
-     * Tests that primary home activity should be selected if it already support secondary displays.
-     */
-    @Test
-    public void testResolveSecondaryHomeActivityWhenPrimaryHomeSupportMultiDisplay() {
-        // SetUp: set primary home.
-        mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
-        // SetUp: put primary home info on 2nd item
-        final List<ResolveInfo> resolutions = new ArrayList<>();
-        final ResolveInfo infoFake1 = new ResolveInfo();
-        infoFake1.activityInfo = new ActivityInfo();
-        infoFake1.activityInfo.name = "fakeActivity1";
-        infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
-        infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
-        final ResolveInfo infoFake2 = new ResolveInfo();
-        final ActivityInfo aInfoPrimary = getFakeHomeActivityInfo(true /* primaryHome */);
-        infoFake2.activityInfo = aInfoPrimary;
-        resolutions.add(infoFake1);
-        resolutions.add(infoFake2);
-        doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
-
-        doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
-                anyBoolean());
-
-        // Run the test.
-        final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
-                .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
-        assertEquals(aInfoPrimary.name, resolvedInfo.first.name);
-        assertEquals(aInfoPrimary.applicationInfo.packageName,
-                resolvedInfo.first.applicationInfo.packageName);
-    }
-
-    /**
-     * Tests that the first one that matches should be selected if there are multiple activities.
-     */
-    @Test
-    public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() {
-        // SetUp: set primary home.
-        mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
-        // Setup: prepare two eligible activity info.
-        final List<ResolveInfo> resolutions = new ArrayList<>();
-        final ResolveInfo infoFake1 = new ResolveInfo();
-        infoFake1.activityInfo = new ActivityInfo();
-        infoFake1.activityInfo.name = "fakeActivity1";
-        infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
-        infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
-        final ResolveInfo infoFake2 = new ResolveInfo();
-        infoFake2.activityInfo = new ActivityInfo();
-        infoFake2.activityInfo.name = "fakeActivity2";
-        infoFake2.activityInfo.applicationInfo = new ApplicationInfo();
-        infoFake2.activityInfo.applicationInfo.packageName = "fakePackage2";
-        resolutions.add(infoFake1);
-        resolutions.add(infoFake2);
-        doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
-
-        doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
-                anyBoolean());
-
-        // Use the first one of matched activities in the same package as selected primary home.
-        final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
-                .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
-
-        assertEquals(infoFake1.activityInfo.applicationInfo.packageName,
-                resolvedInfo.first.applicationInfo.packageName);
-        assertEquals(infoFake1.activityInfo.name, resolvedInfo.first.name);
-    }
-
-    /**
-     * 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
-    public void testGetLaunchStackWithRealCallerId() {
-        // Create a non-system owned virtual display.
-        final TestDisplayContent secondaryDisplay =
-                new TestDisplayContent.Builder(mAtm, 1000, 1500)
-                        .setType(TYPE_VIRTUAL).setOwnerUid(100).build();
-
-        // Create an activity with specify the original launch pid / uid.
-        final ActivityRecord r = new ActivityBuilder(mAtm).setLaunchedFromPid(200)
-                .setLaunchedFromUid(200).build();
-
-        // Simulate ActivityStarter to find a launch stack for requesting the activity to launch
-        // on the secondary display with realCallerId.
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(secondaryDisplay.mDisplayId);
-        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        doReturn(true).when(mSupervisor).canPlaceEntityOnDisplay(secondaryDisplay.mDisplayId,
-                300 /* test realCallerPid */, 300 /* test realCallerUid */, r.info);
-        final Task result = mRootWindowContainer.getLaunchRootTask(r, options,
-                null /* task */, true /* onTop */, null, 300 /* test realCallerPid */,
-                300 /* test realCallerUid */);
-
-        // Assert that the stack is returned as expected.
-        assertNotNull(result);
-        assertEquals("The display ID of the stack should same as secondary display ",
-                secondaryDisplay.mDisplayId, result.getDisplayId());
-    }
-
-    @Test
-    public void testGetValidLaunchStackOnDisplayWithCandidateRootTask() {
-        // Create a root task with an activity on secondary display.
-        final TestDisplayContent secondaryDisplay = new TestDisplayContent.Builder(mAtm, 300,
-                600).build();
-        final Task task = new TaskBuilder(mSupervisor)
-                .setDisplay(secondaryDisplay).build();
-        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.getValidLaunchRootTaskInTaskDisplayArea(
-                mRootWindowContainer.getDefaultTaskDisplayArea(), activity, task,
-                null /* options */, null /* launchParams */);
-        assertEquals(task, stack);
-    }
-
-    @Test
-    public void testSwitchUser_missingHomeRootTask() {
-        final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        doReturn(fullscreenTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
-
-        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        Task homeStack = taskDisplayArea.getRootHomeTask();
-        if (homeStack != null) {
-            homeStack.removeImmediately();
-        }
-        assertNull(taskDisplayArea.getRootHomeTask());
-
-        int currentUser = mRootWindowContainer.mCurrentUser;
-        int otherUser = currentUser + 1;
-
-        mRootWindowContainer.switchUser(otherUser, null);
-
-        assertNotNull(taskDisplayArea.getRootHomeTask());
-        assertEquals(taskDisplayArea.getTopRootTask(), taskDisplayArea.getRootHomeTask());
-    }
-
-    /**
-     * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity
-     * info for test cases.
-     *
-     * @param primaryHome Indicate to use primary home intent as parameter, otherwise, use
-     *                    secondary home intent.
-     * @param forceSystemProvided Indicate to force using system provided home activity.
-     */
-    private void mockResolveHomeActivity(boolean primaryHome, boolean forceSystemProvided) {
-        ActivityInfo targetActivityInfo = getFakeHomeActivityInfo(primaryHome);
-        Intent targetIntent;
-        if (primaryHome) {
-            targetIntent = mAtm.getHomeIntent();
-        } else {
-            Resources resources = mContext.getResources();
-            spyOn(resources);
-            doReturn(targetActivityInfo.applicationInfo.packageName).when(resources).getString(
-                    com.android.internal.R.string.config_secondaryHomePackage);
-            doReturn(forceSystemProvided).when(resources).getBoolean(
-                    com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
-            targetIntent = mAtm.getSecondaryHomeIntent(null /* preferredPackage */);
-        }
-        doReturn(targetActivityInfo).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
-                refEq(targetIntent));
-    }
-
-    /**
-     * Mock {@link RootWindowContainer#resolveSecondaryHomeActivity} for returning consistent
-     * activity info for test cases.
-     */
-    private void mockResolveSecondaryHomeActivity() {
-        final Intent secondaryHomeIntent = mAtm
-                .getSecondaryHomeIntent(null /* preferredPackage */);
-        final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false);
-        doReturn(Pair.create(aInfoSecondary, secondaryHomeIntent)).when(mRootWindowContainer)
-                .resolveSecondaryHomeActivity(anyInt(), any());
-    }
-
-    private ActivityInfo getFakeHomeActivityInfo(boolean primaryHome) {
-        final ActivityInfo aInfo = new ActivityInfo();
-        aInfo.name = primaryHome ? "fakeHomeActivity" : "fakeSecondaryHomeActivity";
-        aInfo.applicationInfo = new ApplicationInfo();
-        aInfo.applicationInfo.packageName =
-                primaryHome ? "fakeHomePackage" : "fakeSecondaryHomePackage";
-        return  aInfo;
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
similarity index 60%
rename from services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
rename to services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index 3a7954b..748622b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -11,7 +11,7 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -27,12 +27,14 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
@@ -49,6 +51,8 @@
 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.getRootTaskAbove;
+import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
@@ -56,26 +60,30 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
 
 import android.app.ActivityManager;
 import android.app.IApplicationThread;
+import android.app.WindowConfiguration;
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
 import android.os.Binder;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 
 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;
@@ -84,15 +92,16 @@
 import java.util.function.Consumer;
 
 /**
- * Tests for the {@link ActivityStack} class.
+ * Tests for the root {@link Task} behavior.
  *
  * Build/Install/Run:
- *  atest WmTests:ActivityStackTests
+ *  atest WmTests:RootTaskTests
  */
 @SmallTest
 @Presubmit
 @RunWith(WindowTestRunner.class)
-public class ActivityStackTests extends WindowTestsBase {
+public class RootTaskTests extends WindowTestsBase {
+
     private TaskDisplayArea mDefaultTaskDisplayArea;
 
     @Before
@@ -101,6 +110,172 @@
     }
 
     @Test
+    public void testRootTaskPositionChildAt() {
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task1 = createTaskInRootTask(rootTask, 0 /* userId */);
+        final Task task2 = createTaskInRootTask(rootTask, 1 /* userId */);
+
+        // Current user root task should be moved to top.
+        rootTask.positionChildAt(WindowContainer.POSITION_TOP, task1, false /* includingParents */);
+        assertEquals(rootTask.mChildren.get(0), task2);
+        assertEquals(rootTask.mChildren.get(1), task1);
+
+        // Non-current user won't be moved to top.
+        rootTask.positionChildAt(WindowContainer.POSITION_TOP, task2, false /* includingParents */);
+        assertEquals(rootTask.mChildren.get(0), task2);
+        assertEquals(rootTask.mChildren.get(1), task1);
+
+        // Non-leaf task should be moved to top regardless of the user id.
+        createTaskInRootTask(task2, 0 /* userId */);
+        createTaskInRootTask(task2, 1 /* userId */);
+        rootTask.positionChildAt(WindowContainer.POSITION_TOP, task2, false /* includingParents */);
+        assertEquals(rootTask.mChildren.get(0), task1);
+        assertEquals(rootTask.mChildren.get(1), task2);
+    }
+
+    @Test
+    public void testClosingAppDifferentTaskOrientation() {
+        final ActivityRecord activity1 = createActivityRecord(mDisplayContent);
+        activity1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+        final ActivityRecord activity2 = createActivityRecord(mDisplayContent);
+        activity2.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
+
+        final WindowContainer parent = activity1.getTask().getParent();
+        assertEquals(SCREEN_ORIENTATION_PORTRAIT, parent.getOrientation());
+        mDisplayContent.mClosingApps.add(activity2);
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, parent.getOrientation());
+    }
+
+    @Test
+    public void testMoveTaskToBackDifferentTaskOrientation() {
+        final ActivityRecord activity1 = createActivityRecord(mDisplayContent);
+        activity1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+        final ActivityRecord activity2 = createActivityRecord(mDisplayContent);
+        activity2.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
+
+        final WindowContainer parent = activity1.getTask().getParent();
+        assertEquals(SCREEN_ORIENTATION_PORTRAIT, parent.getOrientation());
+    }
+
+    @Test
+    public void testRootTaskRemoveImmediately() {
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+        assertEquals(rootTask, task.getRootTask());
+
+        // Remove root task and check if its child is also removed.
+        rootTask.removeImmediately();
+        assertNull(rootTask.getDisplayContent());
+        assertNull(task.getParent());
+    }
+
+    @Test
+    public void testRemoveContainer() {
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+
+        assertNotNull(rootTask);
+        assertNotNull(task);
+        rootTask.removeIfPossible();
+        // Assert that the container was removed.
+        assertNull(rootTask.getParent());
+        assertEquals(0, rootTask.getChildCount());
+        assertNull(rootTask.getDisplayContent());
+        assertNull(task.getDisplayContent());
+        assertNull(task.getParent());
+    }
+
+    @Test
+    public void testRemoveContainer_deferRemoval() {
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+
+        // Root task removal is deferred if one of its child is animating.
+        doReturn(true).when(rootTask).hasWindowsAlive();
+        doReturn(rootTask).when(task).getAnimatingContainer(
+                eq(TRANSITION | CHILDREN), anyInt());
+
+        rootTask.removeIfPossible();
+        // For the case of deferred removal the task controller will still be connected to its
+        // container until the root task window container is removed.
+        assertNotNull(rootTask.getParent());
+        assertNotEquals(0, rootTask.getChildCount());
+        assertNotNull(task);
+
+        rootTask.removeImmediately();
+        // After removing, the task will be isolated.
+        assertNull(task.getParent());
+        assertEquals(0, task.getChildCount());
+    }
+
+    @Test
+    public void testReparent() {
+        // Create first root task on primary display.
+        final Task rootTask1 = createTask(mDisplayContent);
+        final Task task1 = createTaskInRootTask(rootTask1, 0 /* userId */);
+
+        // Create second display and put second root task on it.
+        final DisplayContent dc = createNewDisplay();
+        final Task rootTask2 = createTask(dc);
+
+        // Reparent
+        clearInvocations(task1); // reset the number of onDisplayChanged for task.
+        rootTask1.reparent(dc.getDefaultTaskDisplayArea(), true /* onTop */);
+        assertEquals(dc, rootTask1.getDisplayContent());
+        final int stack1PositionInParent = rootTask1.getParent().mChildren.indexOf(rootTask1);
+        final int stack2PositionInParent = rootTask1.getParent().mChildren.indexOf(rootTask2);
+        assertEquals(stack1PositionInParent, stack2PositionInParent + 1);
+        verify(task1, times(1)).onDisplayChanged(any());
+    }
+
+    @Test
+    public void testTaskOutset() {
+        final Task task = createTask(mDisplayContent);
+        final int taskOutset = 10;
+        spyOn(task);
+        doReturn(taskOutset).when(task).getTaskOutset();
+        doReturn(true).when(task).inMultiWindowMode();
+
+        // Mock the resolved override windowing mode to non-fullscreen
+        final WindowConfiguration windowConfiguration =
+                task.getResolvedOverrideConfiguration().windowConfiguration;
+        spyOn(windowConfiguration);
+        doReturn(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)
+                .when(windowConfiguration).getWindowingMode();
+
+        // Prevent adjust task dimensions
+        doNothing().when(task).adjustForMinimalTaskDimensions(any(), any(), any());
+
+        final Rect taskBounds = new Rect(200, 200, 800, 1000);
+        // Update surface position and size by the given bounds.
+        task.setBounds(taskBounds);
+
+        assertEquals(taskBounds.width() + 2 * taskOutset, task.getLastSurfaceSize().x);
+        assertEquals(taskBounds.height() + 2 * taskOutset, task.getLastSurfaceSize().y);
+        assertEquals(taskBounds.left - taskOutset, task.getLastSurfacePosition().x);
+        assertEquals(taskBounds.top - taskOutset, task.getLastSurfacePosition().y);
+    }
+
+    @Test
+    public void testActivityAndTaskGetsProperType() {
+        final Task task1 = new TaskBuilder(mSupervisor).build();
+        ActivityRecord activity1 = createNonAttachedActivityRecord(mDisplayContent);
+
+        // First activity should become standard
+        task1.addChild(activity1, 0);
+        assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, activity1.getActivityType());
+        assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, task1.getActivityType());
+
+        // Second activity should also become standard
+        ActivityRecord activity2 = createNonAttachedActivityRecord(mDisplayContent);
+        task1.addChild(activity2, WindowContainer.POSITION_TOP);
+        assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, activity2.getActivityType());
+        assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, task1.getActivityType());
+    }
+
+    @Test
     public void testResumedActivity() {
         final ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build();
         final Task task = r.getTask();
@@ -113,40 +288,40 @@
 
     @Test
     public void testResumedActivityFromTaskReparenting() {
-        final Task parentTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
+        final Task rootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
         final ActivityRecord r = new ActivityBuilder(mAtm)
-                .setCreateTask(true).setParentTask(parentTask).build();
+                .setCreateTask(true).setParentTask(rootTask).build();
         final Task task = r.getTask();
-        // Ensure moving task between two stacks updates resumed activity
+        // Ensure moving task between two root tasks updates resumed activity
         r.setState(RESUMED, "testResumedActivityFromTaskReparenting");
-        assertEquals(r, parentTask.getResumedActivity());
+        assertEquals(r, rootTask.getResumedActivity());
 
-        final Task destStack = new TaskBuilder(mSupervisor).setOnTop(true).build();
-        task.reparent(destStack, true /* toTop */, REPARENT_KEEP_ROOT_TASK_AT_FRONT,
+        final Task destRootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
+        task.reparent(destRootTask, true /* toTop */, REPARENT_KEEP_ROOT_TASK_AT_FRONT,
                 false /* animate */, true /* deferResume*/,
                 "testResumedActivityFromTaskReparenting");
 
-        assertNull(parentTask.getResumedActivity());
-        assertEquals(r, destStack.getResumedActivity());
+        assertNull(rootTask.getResumedActivity());
+        assertEquals(r, destRootTask.getResumedActivity());
     }
 
     @Test
     public void testResumedActivityFromActivityReparenting() {
-        final Task parentTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
+        final Task rootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
         final ActivityRecord r = new ActivityBuilder(mAtm)
-                .setCreateTask(true).setParentTask(parentTask).build();
+                .setCreateTask(true).setParentTask(rootTask).build();
         final Task task = r.getTask();
-        // Ensure moving task between two stacks updates resumed activity
+        // Ensure moving task between two root tasks updates resumed activity
         r.setState(RESUMED, "testResumedActivityFromActivityReparenting");
-        assertEquals(r, parentTask.getResumedActivity());
+        assertEquals(r, rootTask.getResumedActivity());
 
-        final Task destStack = new TaskBuilder(mSupervisor).setOnTop(true).build();
-        task.reparent(destStack, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
+        final Task destRootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
+        task.reparent(destRootTask, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
                 false /* animate */, false /* deferResume*/,
                 "testResumedActivityFromActivityReparenting");
 
-        assertNull(parentTask.getResumedActivity());
-        assertEquals(r, destStack.getResumedActivity());
+        assertNull(rootTask.getResumedActivity());
+        assertEquals(r, destRootTask.getResumedActivity());
     }
 
     @Test
@@ -155,7 +330,7 @@
         // We're testing an edge case here where we have primary + fullscreen rather than secondary.
         organizer.setMoveToSecondaryOnEnter(false);
 
-        // Create primary splitscreen stack.
+        // Create primary splitscreen root task.
         final Task primarySplitScreen = new TaskBuilder(mAtm.mTaskSupervisor)
                 .setParentTask(organizer.mPrimary)
                 .setOnTop(true)
@@ -168,7 +343,7 @@
         primarySplitScreen.moveToBack("testPrimarySplitScreenToFullscreenWhenMovedToBack",
                 null /* task */);
 
-        // Assert that stack is at the bottom.
+        // Assert that root task is at the bottom.
         assertEquals(0, getTaskIndexOf(mDefaultTaskDisplayArea, primarySplitScreen));
 
         // Ensure no longer in splitscreen.
@@ -182,7 +357,7 @@
     @Test
     public void testMoveToPrimarySplitScreenThenMoveToBack() {
         TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm);
-        // This time, start with a fullscreen activitystack
+        // This time, start with a fullscreen activity root task.
         final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
@@ -196,7 +371,7 @@
         primarySplitScreen.moveToBack("testPrimarySplitScreenToFullscreenWhenMovedToBack",
                 null /* task */);
 
-        // Assert that stack is at the bottom.
+        // Assert that root task is at the bottom.
         assertEquals(primarySplitScreen, organizer.mSecondary.getChildAt(0));
 
         // Ensure that the override mode is restored to what it was (fullscreen)
@@ -208,7 +383,7 @@
     public void testSplitScreenMoveToBack() {
         TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm);
         // Explicitly reparent task to primary split root to enter split mode, in which implies
-        // primary on top and secondary containing the home task below another stack.
+        // primary on top and secondary containing the home task below another root task.
         final Task primaryTask = mDefaultTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         final Task secondaryTask = mDefaultTaskDisplayArea.createRootTask(
@@ -244,24 +419,24 @@
     }
 
     @Test
-    public void testRemoveOrganizedTask_UpdateStackReference() {
+    public void testRemoveOrganizedTask_UpdateRootTaskReference() {
         final Task rootHomeTask = mDefaultTaskDisplayArea.getRootHomeTask();
         final ActivityRecord homeActivity = new ActivityBuilder(mAtm)
                 .setTask(rootHomeTask)
                 .build();
-        final Task secondaryStack = mAtm.mTaskOrganizerController.createRootTask(
+        final Task secondaryRootTask = mAtm.mTaskOrganizerController.createRootTask(
                 rootHomeTask.getDisplayContent(), WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
 
-        rootHomeTask.reparent(secondaryStack, POSITION_TOP);
-        assertEquals(secondaryStack, rootHomeTask.getParent());
+        rootHomeTask.reparent(secondaryRootTask, POSITION_TOP);
+        assertEquals(secondaryRootTask, rootHomeTask.getParent());
 
-        // This should call to {@link TaskDisplayArea#removeStackReferenceIfNeeded}.
+        // This should call to {@link TaskDisplayArea#removeRootTaskReferenceIfNeeded}.
         homeActivity.removeImmediately();
         assertNull(mDefaultTaskDisplayArea.getRootHomeTask());
     }
 
     @Test
-    public void testStackInheritsDisplayWindowingMode() {
+    public void testRootTaskInheritsDisplayWindowingMode() {
         final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
@@ -276,7 +451,7 @@
     }
 
     @Test
-    public void testStackOverridesDisplayWindowingMode() {
+    public void testRootTaskOverridesDisplayWindowingMode() {
         final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
@@ -358,95 +533,92 @@
     }
 
     @Test
-    public void testMoveStackToBackIncludingParent() {
+    public void testMoveRootTaskToBackIncludingParent() {
         final TaskDisplayArea taskDisplayArea = addNewDisplayContentAt(DisplayContent.POSITION_TOP)
                 .getDefaultTaskDisplayArea();
-        final Task stack1 = createStackForShouldBeVisibleTest(taskDisplayArea,
+        final Task rootTask1 = createTaskForShouldBeVisibleTest(taskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */,
                 true /* twoLevelTask */);
-        final Task stack2 = createStackForShouldBeVisibleTest(taskDisplayArea,
+        final Task rootTask2 = createTaskForShouldBeVisibleTest(taskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */,
                 true /* twoLevelTask */);
 
-        // Do not move display to back because there is still another stack.
-        stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.getTopMostTask());
-        verify(stack2).positionChildAtBottom(any(), eq(false) /* includingParents */);
+        // Do not move display to back because there is still another root task.
+        rootTask2.moveToBack("testMoveRootTaskToBackIncludingParent", rootTask2.getTopMostTask());
+        verify(rootTask2).positionChildAtBottom(any(), eq(false) /* includingParents */);
 
-        // Also move display to back because there is only one stack left.
-        taskDisplayArea.removeRootTask(stack1);
-        stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.getTopMostTask());
-        verify(stack2).positionChildAtBottom(any(), eq(true) /* includingParents */);
+        // Also move display to back because there is only one root task left.
+        taskDisplayArea.removeRootTask(rootTask1);
+        rootTask2.moveToBack("testMoveRootTaskToBackIncludingParent", rootTask2.getTopMostTask());
+        verify(rootTask2).positionChildAtBottom(any(), eq(true) /* includingParents */);
     }
 
     @Test
     public void testShouldBeVisible_Fullscreen() {
-        final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+        final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final Task pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+        final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        // Add an activity to the pinned stack so it isn't considered empty for visibility check.
+        // Add an activity to the pinned root task so it isn't considered empty for visibility
+        // check.
         final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm)
-                .setTask(pinnedStack)
+                .setTask(pinnedRootTask)
                 .build();
 
-        assertTrue(homeStack.shouldBeVisible(null /* starting */));
-        assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
+        assertTrue(homeRootTask.shouldBeVisible(null /* starting */));
+        assertTrue(pinnedRootTask.shouldBeVisible(null /* starting */));
 
-        final Task fullscreenStack = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        // Home stack shouldn't be visible behind an opaque fullscreen stack, but pinned stack
-        // should be visible since it is always on-top.
-        doReturn(false).when(fullscreenStack).isTranslucent(any());
-        assertFalse(homeStack.shouldBeVisible(null /* starting */));
-        assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
-        assertTrue(fullscreenStack.shouldBeVisible(null /* starting */));
+        final Task fullscreenRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        // Home root task shouldn't be visible behind an opaque fullscreen root task, but pinned
+        // root task should be visible since it is always on-top.
+        doReturn(false).when(fullscreenRootTask).isTranslucent(any());
+        assertFalse(homeRootTask.shouldBeVisible(null /* starting */));
+        assertTrue(pinnedRootTask.shouldBeVisible(null /* starting */));
+        assertTrue(fullscreenRootTask.shouldBeVisible(null /* starting */));
 
-        // Home stack should be visible behind a translucent fullscreen stack.
-        doReturn(true).when(fullscreenStack).isTranslucent(any());
-        assertTrue(homeStack.shouldBeVisible(null /* starting */));
-        assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
+        // Home root task should be visible behind a translucent fullscreen root task.
+        doReturn(true).when(fullscreenRootTask).isTranslucent(any());
+        assertTrue(homeRootTask.shouldBeVisible(null /* starting */));
+        assertTrue(pinnedRootTask.shouldBeVisible(null /* starting */));
     }
 
     @Test
     public void testShouldBeVisible_SplitScreen() {
-        final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+        final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        // Home stack should always be fullscreen for this test.
-        doReturn(false).when(homeStack).supportsSplitScreenWindowingMode();
-        final Task splitScreenPrimary =
-                createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+        // Home root task should always be fullscreen for this test.
+        doReturn(false).when(homeRootTask).supportsSplitScreenWindowingMode();
+        final Task splitScreenPrimary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final Task splitScreenSecondary =
-                createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+        final Task splitScreenSecondary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
-        // Home stack shouldn't be visible if both halves of split-screen are opaque.
+        // Home root task shouldn't be visible if both halves of split-screen are opaque.
         doReturn(false).when(splitScreenPrimary).isTranslucent(any());
         doReturn(false).when(splitScreenSecondary).isTranslucent(any());
-        assertFalse(homeStack.shouldBeVisible(null /* starting */));
+        assertFalse(homeRootTask.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
-        assertEquals(TASK_VISIBILITY_INVISIBLE, homeStack.getVisibility(null /* starting */));
+        assertEquals(TASK_VISIBILITY_INVISIBLE, homeRootTask.getVisibility(null /* starting */));
         assertEquals(TASK_VISIBILITY_VISIBLE,
                 splitScreenPrimary.getVisibility(null /* starting */));
         assertEquals(TASK_VISIBILITY_VISIBLE,
                 splitScreenSecondary.getVisibility(null /* starting */));
 
-        // Home stack should be visible if one of the halves of split-screen is translucent.
+        // Home root task should be visible if one of the halves of split-screen is translucent.
         doReturn(true).when(splitScreenPrimary).isTranslucent(any());
-        assertTrue(homeStack.shouldBeVisible(null /* starting */));
+        assertTrue(homeRootTask.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
         assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
-                homeStack.getVisibility(null /* starting */));
+                homeRootTask.getVisibility(null /* starting */));
         assertEquals(TASK_VISIBILITY_VISIBLE,
                 splitScreenPrimary.getVisibility(null /* starting */));
         assertEquals(TASK_VISIBILITY_VISIBLE,
                 splitScreenSecondary.getVisibility(null /* starting */));
 
-        final Task splitScreenSecondary2 =
-                createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+        final Task splitScreenSecondary2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         // First split-screen secondary shouldn't be visible behind another opaque split-split
         // secondary.
@@ -468,18 +640,17 @@
         assertEquals(TASK_VISIBILITY_VISIBLE,
                 splitScreenSecondary2.getVisibility(null /* starting */));
 
-        final Task assistantStack = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT,
-                true /* onTop */);
+        final Task assistantRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
 
-        // Split-screen stacks shouldn't be visible behind an opaque fullscreen stack.
-        doReturn(false).when(assistantStack).isTranslucent(any());
-        assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+        // Split-screen root tasks shouldn't be visible behind an opaque fullscreen root task.
+        doReturn(false).when(assistantRootTask).isTranslucent(any());
+        assertTrue(assistantRootTask.shouldBeVisible(null /* starting */));
         assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
         assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
         assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
         assertEquals(TASK_VISIBILITY_VISIBLE,
-                assistantStack.getVisibility(null /* starting */));
+                assistantRootTask.getVisibility(null /* starting */));
         assertEquals(TASK_VISIBILITY_INVISIBLE,
                 splitScreenPrimary.getVisibility(null /* starting */));
         assertEquals(TASK_VISIBILITY_INVISIBLE,
@@ -487,14 +658,14 @@
         assertEquals(TASK_VISIBILITY_INVISIBLE,
                 splitScreenSecondary2.getVisibility(null /* starting */));
 
-        // Split-screen stacks should be visible behind a translucent fullscreen stack.
-        doReturn(true).when(assistantStack).isTranslucent(any());
-        assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+        // Split-screen root tasks should be visible behind a translucent fullscreen root task.
+        doReturn(true).when(assistantRootTask).isTranslucent(any());
+        assertTrue(assistantRootTask.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
         assertEquals(TASK_VISIBILITY_VISIBLE,
-                assistantStack.getVisibility(null /* starting */));
+                assistantRootTask.getVisibility(null /* starting */));
         assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
                 splitScreenPrimary.getVisibility(null /* starting */));
         assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
@@ -502,20 +673,20 @@
         assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
                 splitScreenSecondary2.getVisibility(null /* starting */));
 
-        // Assistant stack shouldn't be visible behind translucent split-screen stack,
+        // Assistant root task shouldn't be visible behind translucent split-screen root task,
         // unless it is configured to show on top of everything.
-        doReturn(false).when(assistantStack).isTranslucent(any());
+        doReturn(false).when(assistantRootTask).isTranslucent(any());
         doReturn(true).when(splitScreenPrimary).isTranslucent(any());
         doReturn(true).when(splitScreenSecondary2).isTranslucent(any());
         splitScreenSecondary2.moveToFront("testShouldBeVisible_SplitScreen");
         splitScreenPrimary.moveToFront("testShouldBeVisible_SplitScreen");
 
         if (isAssistantOnTop()) {
-            assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+            assertTrue(assistantRootTask.shouldBeVisible(null /* starting */));
             assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
             assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
             assertEquals(TASK_VISIBILITY_VISIBLE,
-                    assistantStack.getVisibility(null /* starting */));
+                    assistantRootTask.getVisibility(null /* starting */));
             assertEquals(TASK_VISIBILITY_INVISIBLE,
                     splitScreenPrimary.getVisibility(null /* starting */));
             assertEquals(TASK_VISIBILITY_INVISIBLE,
@@ -523,11 +694,11 @@
             assertEquals(TASK_VISIBILITY_INVISIBLE,
                     splitScreenSecondary2.getVisibility(null /* starting */));
         } else {
-            assertFalse(assistantStack.shouldBeVisible(null /* starting */));
+            assertFalse(assistantRootTask.shouldBeVisible(null /* starting */));
             assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
             assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
             assertEquals(TASK_VISIBILITY_INVISIBLE,
-                    assistantStack.getVisibility(null /* starting */));
+                    assistantRootTask.getVisibility(null /* starting */));
             assertEquals(TASK_VISIBILITY_VISIBLE,
                     splitScreenPrimary.getVisibility(null /* starting */));
             assertEquals(TASK_VISIBILITY_INVISIBLE,
@@ -539,35 +710,33 @@
 
     @Test
     public void testGetVisibility_MultiLevel() {
-        final Task homeStack = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME,
-                true /* onTop */);
-        final Task splitPrimary = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
-                ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
-        final Task splitSecondary = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
-                ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
+        final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, true /* onTop */);
+        final Task splitPrimary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
+        final Task splitSecondary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
 
-        doReturn(false).when(homeStack).isTranslucent(any());
+        doReturn(false).when(homeRootTask).isTranslucent(any());
         doReturn(false).when(splitPrimary).isTranslucent(any());
         doReturn(false).when(splitSecondary).isTranslucent(any());
 
 
         // Re-parent home to split secondary.
-        homeStack.reparent(splitSecondary, POSITION_TOP);
+        homeRootTask.reparent(splitSecondary, POSITION_TOP);
         // Current tasks should be visible.
         assertEquals(TASK_VISIBILITY_VISIBLE, splitPrimary.getVisibility(null /* starting */));
         assertEquals(TASK_VISIBILITY_VISIBLE, splitSecondary.getVisibility(null /* starting */));
         // Home task should still be visible even though it is a child of another visible task.
-        assertEquals(TASK_VISIBILITY_VISIBLE, homeStack.getVisibility(null /* starting */));
+        assertEquals(TASK_VISIBILITY_VISIBLE, homeRootTask.getVisibility(null /* starting */));
 
 
         // Add fullscreen translucent task that partially occludes split tasks
-        final Task translucentStack = createStandardStackForVisibilityTest(
+        final Task translucentRootTask = createStandardRootTaskForVisibilityTest(
                 WINDOWING_MODE_FULLSCREEN, true /* translucent */);
         // Fullscreen translucent task should be visible
-        assertEquals(TASK_VISIBILITY_VISIBLE, translucentStack.getVisibility(null /* starting */));
+        assertEquals(TASK_VISIBILITY_VISIBLE,
+                translucentRootTask.getVisibility(null /* starting */));
         // Split tasks should be visible behind translucent
         assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
                 splitPrimary.getVisibility(null /* starting */));
@@ -576,415 +745,400 @@
         // Home task should be visible behind translucent since its parent is visible behind
         // translucent.
         assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
-                homeStack.getVisibility(null /* starting */));
+                homeRootTask.getVisibility(null /* starting */));
 
 
         // Hide split-secondary
         splitSecondary.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true /* set */);
         // Home split secondary and home task should be invisible.
         assertEquals(TASK_VISIBILITY_INVISIBLE, splitSecondary.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_INVISIBLE, homeStack.getVisibility(null /* starting */));
+        assertEquals(TASK_VISIBILITY_INVISIBLE, homeRootTask.getVisibility(null /* starting */));
     }
 
     @Test
     public void testGetVisibility_FullscreenBehindTranslucent() {
-        final Task bottomStack =
-                createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+        final Task bottomRootTask =
+                createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
-        final Task translucentStack =
-                createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+        final Task translucentRootTask =
+                createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
 
         assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
-                bottomStack.getVisibility(null /* starting */));
+                bottomRootTask.getVisibility(null /* starting */));
         assertEquals(TASK_VISIBILITY_VISIBLE,
-                translucentStack.getVisibility(null /* starting */));
+                translucentRootTask.getVisibility(null /* starting */));
     }
 
     @Test
     public void testGetVisibility_FullscreenBehindTranslucentAndOpaque() {
-        final Task bottomStack =
-                createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+        final Task bottomRootTask =
+                createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
-        final Task translucentStack =
-                createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+        final Task translucentRootTask =
+                createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
-        final Task opaqueStack =
-                createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+        final Task opaqueRootTask =
+                createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
 
-        assertEquals(TASK_VISIBILITY_INVISIBLE, bottomStack.getVisibility(null /* starting */));
+        assertEquals(TASK_VISIBILITY_INVISIBLE, bottomRootTask.getVisibility(null /* starting */));
         assertEquals(TASK_VISIBILITY_INVISIBLE,
-                translucentStack.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_VISIBLE, opaqueStack.getVisibility(null /* starting */));
+                translucentRootTask.getVisibility(null /* starting */));
+        assertEquals(TASK_VISIBILITY_VISIBLE, opaqueRootTask.getVisibility(null /* starting */));
     }
 
     @Test
     public void testGetVisibility_FullscreenBehindOpaqueAndTranslucent() {
-        final Task bottomStack =
-                createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+        final Task bottomRootTask =
+                createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
-        final Task opaqueStack =
-                createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+        final Task opaqueRootTask =
+                createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
-        final Task translucentStack =
-                createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+        final Task translucentRootTask =
+                createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
 
-        assertEquals(TASK_VISIBILITY_INVISIBLE, bottomStack.getVisibility(null /* starting */));
+        assertEquals(TASK_VISIBILITY_INVISIBLE, bottomRootTask.getVisibility(null /* starting */));
         assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
-                opaqueStack.getVisibility(null /* starting */));
+                opaqueRootTask.getVisibility(null /* starting */));
         assertEquals(TASK_VISIBILITY_VISIBLE,
-                translucentStack.getVisibility(null /* starting */));
+                translucentRootTask.getVisibility(null /* starting */));
     }
 
     @Test
     public void testGetVisibility_FullscreenTranslucentBehindTranslucent() {
-        final Task bottomTranslucentStack =
-                createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+        final Task bottomTranslucentRootTask =
+                createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
-        final Task translucentStack =
-                createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+        final Task translucentRootTask =
+                createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
 
         assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
-                bottomTranslucentStack.getVisibility(null /* starting */));
+                bottomTranslucentRootTask.getVisibility(null /* starting */));
         assertEquals(TASK_VISIBILITY_VISIBLE,
-                translucentStack.getVisibility(null /* starting */));
+                translucentRootTask.getVisibility(null /* starting */));
     }
 
     @Test
     public void testGetVisibility_FullscreenTranslucentBehindOpaque() {
-        final Task bottomTranslucentStack =
-                createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+        final Task bottomTranslucentRootTask =
+                createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
-        final Task opaqueStack =
-                createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+        final Task opaqueRootTask =
+                createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
 
         assertEquals(TASK_VISIBILITY_INVISIBLE,
-                bottomTranslucentStack.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_VISIBLE, opaqueStack.getVisibility(null /* starting */));
+                bottomTranslucentRootTask.getVisibility(null /* starting */));
+        assertEquals(TASK_VISIBILITY_VISIBLE, opaqueRootTask.getVisibility(null /* starting */));
     }
 
     @Test
     public void testGetVisibility_FullscreenBehindTranslucentAndPip() {
-        final Task bottomStack =
-                createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+        final Task bottomRootTask =
+                createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
-        final Task translucentStack =
-                createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+        final Task translucentRootTask =
+                createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
-        final Task pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+        final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
-                bottomStack.getVisibility(null /* starting */));
+                bottomRootTask.getVisibility(null /* starting */));
         assertEquals(TASK_VISIBILITY_VISIBLE,
-                translucentStack.getVisibility(null /* starting */));
-        // Add an activity to the pinned stack so it isn't considered empty for visibility check.
+                translucentRootTask.getVisibility(null /* starting */));
+        // Add an activity to the pinned root task so it isn't considered empty for visibility
+        // check.
         final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm)
-                .setTask(pinnedStack)
+                .setTask(pinnedRootTask)
                 .build();
-        assertEquals(TASK_VISIBILITY_VISIBLE, pinnedStack.getVisibility(null /* starting */));
+        assertEquals(TASK_VISIBILITY_VISIBLE, pinnedRootTask.getVisibility(null /* starting */));
     }
 
     @Test
     public void testShouldBeVisible_Finishing() {
-        final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+        final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        ActivityRecord topRunningHomeActivity = homeStack.topRunningActivity();
+        ActivityRecord topRunningHomeActivity = homeRootTask.topRunningActivity();
         if (topRunningHomeActivity == null) {
             topRunningHomeActivity = new ActivityBuilder(mAtm)
-                    .setTask(homeStack)
+                    .setTask(homeRootTask)
                     .build();
         }
 
-        final Task translucentStack = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        doReturn(true).when(translucentStack).isTranslucent(any());
+        final Task translucentRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        doReturn(true).when(translucentRootTask).isTranslucent(any());
 
-        assertTrue(homeStack.shouldBeVisible(null /* starting */));
-        assertTrue(translucentStack.shouldBeVisible(null /* starting */));
+        assertTrue(homeRootTask.shouldBeVisible(null /* starting */));
+        assertTrue(translucentRootTask.shouldBeVisible(null /* starting */));
 
         topRunningHomeActivity.finishing = true;
         final ActivityRecord topRunningTranslucentActivity =
-                translucentStack.topRunningActivity();
+                translucentRootTask.topRunningActivity();
         topRunningTranslucentActivity.finishing = true;
 
-        // Home stack should be visible even there are no running activities.
-        assertTrue(homeStack.shouldBeVisible(null /* starting */));
+        // Home root task should be visible even there are no running activities.
+        assertTrue(homeRootTask.shouldBeVisible(null /* starting */));
         // Home should be visible if we are starting an activity within it.
-        assertTrue(homeStack.shouldBeVisible(topRunningHomeActivity /* starting */));
-        // The translucent stack shouldn't be visible since its activity marked as finishing.
-        assertFalse(translucentStack.shouldBeVisible(null /* starting */));
+        assertTrue(homeRootTask.shouldBeVisible(topRunningHomeActivity /* starting */));
+        // The translucent root task shouldn't be visible since its activity marked as finishing.
+        assertFalse(translucentRootTask.shouldBeVisible(null /* starting */));
     }
 
     @Test
-    public void testShouldBeVisible_FullscreenBehindTranslucentInHomeStack() {
-        final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+    public void testShouldBeVisible_FullscreenBehindTranslucentInHomeRootTask() {
+        final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
 
         final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
-                    .setParentTask(homeStack)
-                    .setCreateTask(true)
-                    .build();
+                .setParentTask(homeRootTask)
+                .setCreateTask(true)
+                .build();
         final Task task = firstActivity.getTask();
         final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
                 .setTask(task)
                 .build();
 
         doReturn(false).when(secondActivity).occludesParent();
-        homeStack.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+        homeRootTask.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
                 false /* preserveWindows */);
 
         assertTrue(firstActivity.shouldBeVisible());
     }
 
     @Test
-    public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindFullscreen() {
-        final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+    public void testMoveHomeRootTaskBehindBottomMostVisible_NoMoveHomeBehindFullscreen() {
+        final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final Task fullscreenStack = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-
-        doReturn(false).when(homeStack).isTranslucent(any());
-        doReturn(false).when(fullscreenStack).isTranslucent(any());
-
-        // Ensure that we don't move the home stack if it is already behind the top fullscreen stack
-        int homeStackIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeStack);
-        assertEquals(fullscreenStack, getRootTaskAbove(homeStack));
-        mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack);
-        assertEquals(homeStackIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeStack));
-    }
-
-    @Test
-    public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindTranslucent() {
-        final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final Task fullscreenStack = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea,
+        final Task fullscreenRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
-        doReturn(false).when(homeStack).isTranslucent(any());
-        doReturn(true).when(fullscreenStack).isTranslucent(any());
+        doReturn(false).when(homeRootTask).isTranslucent(any());
+        doReturn(false).when(fullscreenRootTask).isTranslucent(any());
 
-        // Ensure that we don't move the home stack if it is already behind the top fullscreen stack
-        int homeStackIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeStack);
-        assertEquals(fullscreenStack, getRootTaskAbove(homeStack));
-        mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack);
-        assertEquals(homeStackIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeStack));
+        // Ensure that we don't move the home root task if it is already behind the top fullscreen
+        // root task.
+        int homeRootTaskIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask);
+        assertEquals(fullscreenRootTask, getRootTaskAbove(homeRootTask));
+        mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask);
+        assertEquals(homeRootTaskIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask));
     }
 
     @Test
-    public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeOnTop() {
-        final Task fullscreenStack = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+    public void testMoveHomeRootTaskBehindBottomMostVisible_NoMoveHomeBehindTranslucent() {
+        final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+        final Task fullscreenRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
-        doReturn(false).when(homeStack).isTranslucent(any());
-        doReturn(false).when(fullscreenStack).isTranslucent(any());
+        doReturn(false).when(homeRootTask).isTranslucent(any());
+        doReturn(true).when(fullscreenRootTask).isTranslucent(any());
 
-        // Ensure we don't move the home stack if it is already on top
-        int homeStackIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeStack);
-        assertNull(getRootTaskAbove(homeStack));
-        mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack);
-        assertEquals(homeStackIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeStack));
+        // Ensure that we don't move the home root task if it is already behind the top fullscreen
+        // root task.
+        int homeRootTaskIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask);
+        assertEquals(fullscreenRootTask, getRootTaskAbove(homeRootTask));
+        mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask);
+        assertEquals(homeRootTaskIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask));
     }
 
     @Test
-    public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreen() {
-        final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+    public void testMoveHomeRootTaskBehindBottomMostVisible_NoMoveHomeOnTop() {
+        final Task fullscreenRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final Task fullscreenStack2 = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final Task pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+
+        doReturn(false).when(homeRootTask).isTranslucent(any());
+        doReturn(false).when(fullscreenRootTask).isTranslucent(any());
+
+        // Ensure we don't move the home root task if it is already on top
+        int homeRootTaskIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask);
+        assertNull(getRootTaskAbove(homeRootTask));
+        mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask);
+        assertEquals(homeRootTaskIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask));
+    }
+
+    @Test
+    public void testMoveHomeRootTaskBehindBottomMostVisible_MoveHomeBehindFullscreen() {
+        final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+        final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final Task fullscreenRootTask2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
-        doReturn(false).when(homeStack).isTranslucent(any());
-        doReturn(false).when(fullscreenStack1).isTranslucent(any());
-        doReturn(false).when(fullscreenStack2).isTranslucent(any());
+        doReturn(false).when(homeRootTask).isTranslucent(any());
+        doReturn(false).when(fullscreenRootTask1).isTranslucent(any());
+        doReturn(false).when(fullscreenRootTask2).isTranslucent(any());
 
-        // Ensure that we move the home stack behind the bottom most fullscreen stack, ignoring the
-        // pinned stack
-        assertEquals(fullscreenStack1, getRootTaskAbove(homeStack));
-        mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack);
-        assertEquals(fullscreenStack2, getRootTaskAbove(homeStack));
+        // Ensure that we move the home root task behind the bottom most fullscreen root task,
+        // ignoring the pinned root task.
+        assertEquals(fullscreenRootTask1, getRootTaskAbove(homeRootTask));
+        mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask);
+        assertEquals(fullscreenRootTask2, getRootTaskAbove(homeRootTask));
     }
 
     @Test
     public void
-            testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreenAndTranslucent() {
-        final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+            testMoveHomeRootTaskBehindBottomMostVisible_MoveHomeBehindFullscreenAndTranslucent() {
+        final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final Task fullscreenStack2 = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
+        final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final Task fullscreenRootTask2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
-        doReturn(false).when(homeStack).isTranslucent(any());
-        doReturn(false).when(fullscreenStack1).isTranslucent(any());
-        doReturn(true).when(fullscreenStack2).isTranslucent(any());
+        doReturn(false).when(homeRootTask).isTranslucent(any());
+        doReturn(false).when(fullscreenRootTask1).isTranslucent(any());
+        doReturn(true).when(fullscreenRootTask2).isTranslucent(any());
 
-        // Ensure that we move the home stack behind the bottom most non-translucent fullscreen
-        // stack
-        assertEquals(fullscreenStack1, getRootTaskAbove(homeStack));
-        mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack);
-        assertEquals(fullscreenStack1, getRootTaskAbove(homeStack));
+        // Ensure that we move the home root task behind the bottom most non-translucent fullscreen
+        // root task.
+        assertEquals(fullscreenRootTask1, getRootTaskAbove(homeRootTask));
+        mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask);
+        assertEquals(fullscreenRootTask1, getRootTaskAbove(homeRootTask));
     }
 
     @Test
-    public void testMoveHomeStackBehindStack_BehindHomeStack() {
-        final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final Task fullscreenStack2 = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+    public void testMoveHomeRootTaskBehindRootTask_BehindHomeRootTask() {
+        final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final Task fullscreenRootTask2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
 
-        doReturn(false).when(homeStack).isTranslucent(any());
-        doReturn(false).when(fullscreenStack1).isTranslucent(any());
-        doReturn(false).when(fullscreenStack2).isTranslucent(any());
+        doReturn(false).when(homeRootTask).isTranslucent(any());
+        doReturn(false).when(fullscreenRootTask1).isTranslucent(any());
+        doReturn(false).when(fullscreenRootTask2).isTranslucent(any());
 
-        // Ensure we don't move the home stack behind itself
-        int homeStackIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeStack);
-        mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, homeStack);
-        assertEquals(homeStackIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeStack));
+        // Ensure we don't move the home root task behind itself
+        int homeRootTaskIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask);
+        mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeRootTask, homeRootTask);
+        assertEquals(homeRootTaskIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask));
     }
 
     @Test
-    public void testMoveHomeStackBehindStack() {
-        final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final Task fullscreenStack2 = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final Task fullscreenStack3 = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final Task fullscreenStack4 = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+    public void testMoveHomeRootTaskBehindRootTask() {
+        final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final Task fullscreenRootTask2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final Task fullscreenRootTask3 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final Task fullscreenRootTask4 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
 
-        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));
+        mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeRootTask, fullscreenRootTask1);
+        assertEquals(fullscreenRootTask1, getRootTaskAbove(homeRootTask));
+        mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeRootTask, fullscreenRootTask2);
+        assertEquals(fullscreenRootTask2, getRootTaskAbove(homeRootTask));
+        mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeRootTask, fullscreenRootTask4);
+        assertEquals(fullscreenRootTask4, getRootTaskAbove(homeRootTask));
+        mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeRootTask, fullscreenRootTask2);
+        assertEquals(fullscreenRootTask2, getRootTaskAbove(homeRootTask));
     }
 
     @Test
     public void testSetAlwaysOnTop() {
-        final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+        final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final Task pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+        final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        assertEquals(pinnedStack, getRootTaskAbove(homeStack));
+        assertEquals(pinnedRootTask, getRootTaskAbove(homeRootTask));
 
-        final Task alwaysOnTopStack = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        alwaysOnTopStack.setAlwaysOnTop(true);
-        assertTrue(alwaysOnTopStack.isAlwaysOnTop());
-        // Ensure (non-pinned) always on top stack is put below pinned stack.
-        assertEquals(pinnedStack, getRootTaskAbove(alwaysOnTopStack));
+        final Task alwaysOnTopRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        alwaysOnTopRootTask.setAlwaysOnTop(true);
+        assertTrue(alwaysOnTopRootTask.isAlwaysOnTop());
+        // Ensure (non-pinned) always on top root task is put below pinned root task.
+        assertEquals(pinnedRootTask, getRootTaskAbove(alwaysOnTopRootTask));
 
-        final Task nonAlwaysOnTopStack = createStackForShouldBeVisibleTest(
+        final Task nonAlwaysOnTopRootTask = createTaskForShouldBeVisibleTest(
                 mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        // Ensure non always on top stack is put below always on top stacks.
-        assertEquals(alwaysOnTopStack, getRootTaskAbove(nonAlwaysOnTopStack));
+        // Ensure non always on top root task is put below always on top root tasks.
+        assertEquals(alwaysOnTopRootTask, getRootTaskAbove(nonAlwaysOnTopRootTask));
 
-        final Task alwaysOnTopStack2 = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        alwaysOnTopStack2.setAlwaysOnTop(true);
-        assertTrue(alwaysOnTopStack2.isAlwaysOnTop());
-        // Ensure newly created always on top stack is placed above other all always on top stacks.
-        assertEquals(pinnedStack, getRootTaskAbove(alwaysOnTopStack2));
+        final Task alwaysOnTopRootTask2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        alwaysOnTopRootTask2.setAlwaysOnTop(true);
+        assertTrue(alwaysOnTopRootTask2.isAlwaysOnTop());
+        // Ensure newly created always on top root task is placed above other all always on top
+        // root tasks.
+        assertEquals(pinnedRootTask, getRootTaskAbove(alwaysOnTopRootTask2));
 
-        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, getRootTaskAbove(alwaysOnTopStack2));
-        alwaysOnTopStack2.setAlwaysOnTop(true);
+        alwaysOnTopRootTask2.setAlwaysOnTop(false);
+        // Ensure, when always on top is turned off for a root task, the root task is put just below
+        // all other always on top root tasks.
+        assertEquals(alwaysOnTopRootTask, getRootTaskAbove(alwaysOnTopRootTask2));
+        alwaysOnTopRootTask2.setAlwaysOnTop(true);
 
         // Ensure always on top state changes properly when windowing mode changes.
-        alwaysOnTopStack2.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        assertFalse(alwaysOnTopStack2.isAlwaysOnTop());
-        assertEquals(alwaysOnTopStack, getRootTaskAbove(alwaysOnTopStack2));
-        alwaysOnTopStack2.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        assertTrue(alwaysOnTopStack2.isAlwaysOnTop());
-        assertEquals(pinnedStack, getRootTaskAbove(alwaysOnTopStack2));
+        alwaysOnTopRootTask2.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        assertFalse(alwaysOnTopRootTask2.isAlwaysOnTop());
+        assertEquals(alwaysOnTopRootTask, getRootTaskAbove(alwaysOnTopRootTask2));
+        alwaysOnTopRootTask2.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertTrue(alwaysOnTopRootTask2.isAlwaysOnTop());
+        assertEquals(pinnedRootTask, getRootTaskAbove(alwaysOnTopRootTask2));
     }
 
     @Test
     public void testSplitScreenMoveToFront() {
-        final Task splitScreenPrimary = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
-                ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final Task splitScreenSecondary = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
-                ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final Task assistantStack = createStackForShouldBeVisibleTest(
-                mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT,
-                true /* onTop */);
+        final Task splitScreenPrimary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final Task splitScreenSecondary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final Task assistantRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
 
         doReturn(false).when(splitScreenPrimary).isTranslucent(any());
         doReturn(false).when(splitScreenSecondary).isTranslucent(any());
-        doReturn(false).when(assistantStack).isTranslucent(any());
+        doReturn(false).when(assistantRootTask).isTranslucent(any());
 
         assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
         assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
-        assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+        assertTrue(assistantRootTask.shouldBeVisible(null /* starting */));
 
         splitScreenSecondary.moveToFront("testSplitScreenMoveToFront");
 
         if (isAssistantOnTop()) {
             assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
             assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
-            assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+            assertTrue(assistantRootTask.shouldBeVisible(null /* starting */));
         } else {
             assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
             assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
-            assertFalse(assistantStack.shouldBeVisible(null /* starting */));
+            assertFalse(assistantRootTask.shouldBeVisible(null /* starting */));
         }
     }
 
-    private Task createStandardStackForVisibilityTest(int windowingMode,
+    private Task createStandardRootTaskForVisibilityTest(int windowingMode,
             boolean translucent) {
-        final Task stack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+        final Task rootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 windowingMode, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        doReturn(translucent).when(stack).isTranslucent(any());
-        return stack;
+        doReturn(translucent).when(rootTask).isTranslucent(any());
+        return rootTask;
     }
 
-    private Task createStackForShouldBeVisibleTest(
+    private Task createTaskForShouldBeVisibleTest(
             TaskDisplayArea taskDisplayArea, int windowingMode, int activityType, boolean onTop) {
-        return createStackForShouldBeVisibleTest(taskDisplayArea,
+        return createTaskForShouldBeVisibleTest(taskDisplayArea,
                 windowingMode, activityType, onTop, false /* twoLevelTask */);
     }
 
     @SuppressWarnings("TypeParameterUnusedInFormals")
-    private Task createStackForShouldBeVisibleTest(TaskDisplayArea taskDisplayArea,
+    private Task createTaskForShouldBeVisibleTest(TaskDisplayArea taskDisplayArea,
             int windowingMode, int activityType, boolean onTop, boolean twoLevelTask) {
         final Task task;
         if (activityType == ACTIVITY_TYPE_HOME) {
@@ -1157,20 +1311,20 @@
     }
 
     @Test
-    public void testWontFinishHomeStackImmediately() {
-        final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+    public void testWontFinishHomeRootTaskImmediately() {
+        final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
 
-        ActivityRecord activity = homeStack.topRunningActivity();
+        ActivityRecord activity = homeRootTask.topRunningActivity();
         if (activity == null) {
             activity = new ActivityBuilder(mAtm)
-                    .setParentTask(homeStack)
+                    .setParentTask(homeRootTask)
                     .setCreateTask(true)
                     .build();
         }
 
-        // Home stack should not be destroyed immediately.
-        final ActivityRecord activity1 = finishTopActivity(homeStack);
+        // Home root task should not be destroyed immediately.
+        final ActivityRecord activity1 = finishTopActivity(homeRootTask);
         assertEquals(FINISHING, activity1.getState());
     }
 
@@ -1178,30 +1332,28 @@
     public void testFinishCurrentActivity() {
         // Create 2 activities on a new display.
         final DisplayContent display = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
-        final Task stack1 = createStackForShouldBeVisibleTest(
-                display.getDefaultTaskDisplayArea(),
+        final Task rootTask1 = createTaskForShouldBeVisibleTest(display.getDefaultTaskDisplayArea(),
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final Task stack2 = createStackForShouldBeVisibleTest(
-                display.getDefaultTaskDisplayArea(),
+        final Task rootTask2 = createTaskForShouldBeVisibleTest(display.getDefaultTaskDisplayArea(),
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
-        // There is still an activity1 in stack1 so the activity2 should be added to finishing list
-        // that will be destroyed until idle.
-        stack2.getTopNonFinishingActivity().mVisibleRequested = true;
-        final ActivityRecord activity2 = finishTopActivity(stack2);
+        // There is still an activity1 in rootTask1 so the activity2 should be added to finishing
+        // list that will be destroyed until idle.
+        rootTask2.getTopNonFinishingActivity().mVisibleRequested = true;
+        final ActivityRecord activity2 = finishTopActivity(rootTask2);
         assertEquals(STOPPING, activity2.getState());
         assertThat(mSupervisor.mStoppingActivities).contains(activity2);
 
         // The display becomes empty. Since there is no next activity to be idle, the activity
         // should be destroyed immediately with updating configuration to restore original state.
-        final ActivityRecord activity1 = finishTopActivity(stack1);
+        final ActivityRecord activity1 = finishTopActivity(rootTask1);
         assertEquals(DESTROYING, activity1.getState());
         verify(mRootWindowContainer).ensureVisibilityAndConfig(eq(null) /* starting */,
                 eq(display.mDisplayId), anyBoolean(), anyBoolean());
     }
 
-    private ActivityRecord finishTopActivity(Task stack) {
-        final ActivityRecord activity = stack.topRunningActivity();
+    private ActivityRecord finishTopActivity(Task task) {
+        final ActivityRecord activity = task.topRunningActivity();
         assertNotNull(activity);
         activity.setState(STOPPED, "finishTopActivity");
         activity.makeFinishingLocked();
@@ -1214,24 +1366,24 @@
         // When focused activity and keyguard is going away, we should not sleep regardless
         // of the display state, but keyguard-going-away should only take effects on default
         // display since there is no keyguard on secondary displays (yet).
-        verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/,
+        verifyShouldSleepActivities(true /* focusedRootTask */, true /*keyguardGoingAway*/,
                 true /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */);
-        verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/,
+        verifyShouldSleepActivities(true /* focusedRootTask */, true /*keyguardGoingAway*/,
                 true /* displaySleeping */, false /* isDefaultDisplay */, true /* expected */);
 
-        // When not the focused stack, defer to display sleeping state.
-        verifyShouldSleepActivities(false /* focusedStack */, true /*keyguardGoingAway*/,
+        // When not the focused root task, defer to display sleeping state.
+        verifyShouldSleepActivities(false /* focusedRootTask */, true /*keyguardGoingAway*/,
                 true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */);
 
         // If keyguard is going away, defer to the display sleeping state.
-        verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/,
+        verifyShouldSleepActivities(true /* focusedRootTask */, false /*keyguardGoingAway*/,
                 true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */);
-        verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/,
+        verifyShouldSleepActivities(true /* focusedRootTask */, false /*keyguardGoingAway*/,
                 false /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */);
     }
 
     @Test
-    public void testStackOrderChangedOnRemoveStack() {
+    public void testRootTaskOrderChangedOnRemoveRootTask() {
         final Task task = new TaskBuilder(mSupervisor).build();
         RootTaskOrderChangedListener listener = new RootTaskOrderChangedListener();
         mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(listener);
@@ -1244,7 +1396,7 @@
     }
 
     @Test
-    public void testStackOrderChangedOnAddPositionStack() {
+    public void testRootTaskOrderChangedOnAddPositionRootTask() {
         final Task task = new TaskBuilder(mSupervisor).build();
         mDefaultTaskDisplayArea.removeRootTask(task);
 
@@ -1260,14 +1412,14 @@
     }
 
     @Test
-    public void testStackOrderChangedOnPositionStack() {
+    public void testRootTaskOrderChangedOnPositionRootTask() {
         RootTaskOrderChangedListener listener = new RootTaskOrderChangedListener();
         try {
-            final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
+            final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest(
                     mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                     true /* onTop */);
             mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(listener);
-            mDefaultTaskDisplayArea.positionChildAt(POSITION_BOTTOM, fullscreenStack1,
+            mDefaultTaskDisplayArea.positionChildAt(POSITION_BOTTOM, fullscreenRootTask1,
                     false /*includingParents*/);
         } finally {
             mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(listener);
@@ -1443,7 +1595,7 @@
                 com.android.internal.R.bool.config_assistantOnTopOfDream);
     }
 
-    private void verifyShouldSleepActivities(boolean focusedStack,
+    private void verifyShouldSleepActivities(boolean focusedRootTask,
             boolean keyguardGoingAway, boolean displaySleeping, boolean isDefaultDisplay,
             boolean expected) {
         final Task task = new TaskBuilder(mSupervisor).build();
@@ -1454,13 +1606,13 @@
         task.mDisplayContent = display;
         doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
         doReturn(displaySleeping).when(display).isSleeping();
-        doReturn(focusedStack).when(task).isFocusedRootTaskOnDisplay();
+        doReturn(focusedRootTask).when(task).isFocusedRootTaskOnDisplay();
 
         assertEquals(expected, task.shouldSleepActivities());
     }
 
     private static class RootTaskOrderChangedListener
-            implements OnRootTaskOrderChangedListener {
+            implements TaskDisplayArea.OnRootTaskOrderChangedListener {
         public boolean mChanged = false;
 
         @Override
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 1b114c1..20bced2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -16,42 +16,93 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.TYPE_VIRTUAL;
+import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
+import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE;
 import static com.android.server.wm.Task.ActivityState.FINISHING;
 import static com.android.server.wm.Task.ActivityState.PAUSED;
 import static com.android.server.wm.Task.ActivityState.PAUSING;
 import static com.android.server.wm.Task.ActivityState.STOPPED;
 import static com.android.server.wm.Task.ActivityState.STOPPING;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.contains;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.refEq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
+import android.app.ActivityOptions;
 import android.app.WindowConfiguration;
 import android.content.ComponentName;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.platform.test.annotations.Presubmit;
+import android.util.MergedConfiguration;
+import android.util.Pair;
 
-import androidx.test.filters.SmallTest;
+import androidx.test.filters.MediumTest;
 
+import com.android.internal.app.ResolverActivity;
+
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Consumer;
+
 /**
- * Tests for RootWindowContainer.
+ * Tests for {@link RootWindowContainer}.
  *
  * Build/Install/Run:
  *  atest WmTests:RootWindowContainerTests
  */
-@SmallTest
+@MediumTest
 @Presubmit
 @RunWith(WindowTestRunner.class)
 public class RootWindowContainerTests extends WindowTestsBase {
 
+    @Before
+    public void setUp() throws Exception {
+        doNothing().when(mAtm).updateSleepIfNeededLocked();
+    }
+
     @Test
     public void testUpdateDefaultDisplayWindowingModeOnSettingsRetrieved() {
         assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
@@ -166,5 +217,860 @@
         assertFalse(task.hasChild());
         assertFalse(wpc.hasActivities());
     }
+
+    /**
+     * This test ensures that we do not try to restore a task based off an invalid task id. We
+     * should expect {@code null} to be returned in this case.
+     */
+    @Test
+    public void testRestoringInvalidTask() {
+        mRootWindowContainer.getDefaultDisplay().removeAllTasks();
+        Task task = mRootWindowContainer.anyTaskForId(0 /*taskId*/,
+                MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
+        assertNull(task);
+    }
+
+    /**
+     * This test ensures that an existing task in the pinned root task is moved to the fullscreen
+     * activity root task when a new task is added.
+     */
+    @Test
+    public void testReplacingTaskInPinnedRootTask() {
+        Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
+                .setTask(fullscreenTask).build();
+        final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
+                .setTask(fullscreenTask).build();
+
+        fullscreenTask.moveToFront("testReplacingTaskInPinnedRootTask");
+
+        // Ensure full screen root task has both tasks.
+        ensureTaskPlacement(fullscreenTask, firstActivity, secondActivity);
+
+        // Move first activity to pinned root task.
+        mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, "initialMove");
+
+        final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea();
+        Task pinnedRootTask = taskDisplayArea.getRootPinnedTask();
+        // Ensure a task has moved over.
+        ensureTaskPlacement(pinnedRootTask, firstActivity);
+        ensureTaskPlacement(fullscreenTask, secondActivity);
+
+        // Move second activity to pinned root task.
+        mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "secondMove");
+
+        // Need to get root tasks again as a new instance might have been created.
+        pinnedRootTask = taskDisplayArea.getRootPinnedTask();
+        fullscreenTask = taskDisplayArea.getRootTask(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD);
+        // Ensure root tasks have swapped tasks.
+        ensureTaskPlacement(pinnedRootTask, secondActivity);
+        ensureTaskPlacement(fullscreenTask, firstActivity);
+    }
+
+    @Test
+    public void testMovingBottomMostRootTaskActivityToPinnedRootTask() {
+        final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
+                .setTask(fullscreenTask).build();
+        final Task task = firstActivity.getTask();
+
+        final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
+                .setTask(fullscreenTask).build();
+
+        fullscreenTask.moveTaskToBack(task);
+
+        // Ensure full screen task has both tasks.
+        ensureTaskPlacement(fullscreenTask, firstActivity, secondActivity);
+        assertEquals(task.getTopMostActivity(), secondActivity);
+        firstActivity.setState(STOPPED, "testMovingBottomMostRootTaskActivityToPinnedRootTask");
+
+
+        // Move first activity to pinned root task.
+        mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "initialMove");
+
+        assertTrue(firstActivity.mRequestForceTransition);
+    }
+
+    private static void ensureTaskPlacement(Task task, ActivityRecord... activities) {
+        final ArrayList<ActivityRecord> taskActivities = new ArrayList<>();
+
+        task.forAllActivities((Consumer<ActivityRecord>) taskActivities::add, false);
+
+        assertEquals("Expecting " + Arrays.deepToString(activities) + " got " + taskActivities,
+                taskActivities.size(), activities != null ? activities.length : 0);
+
+        if (activities == null) {
+            return;
+        }
+
+        for (ActivityRecord activity : activities) {
+            assertTrue(taskActivities.contains(activity));
+        }
+    }
+
+    @Test
+    public void testApplySleepTokens() {
+        final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
+        final KeyguardController keyguard = mSupervisor.getKeyguardController();
+        final Task task = new TaskBuilder(mSupervisor)
+                .setDisplay(display)
+                .setOnTop(false)
+                .build();
+
+        // Make sure we wake and resume in the case the display is turning on and the keyguard is
+        // not showing.
+        verifySleepTokenBehavior(display, keyguard, task, true /*displaySleeping*/,
+                false /* displayShouldSleep */, true /* isFocusedTask */,
+                false /* keyguardShowing */, true /* expectWakeFromSleep */,
+                true /* expectResumeTopActivity */);
+
+        // Make sure we wake and don't resume when the display is turning on and the keyguard is
+        // showing.
+        verifySleepTokenBehavior(display, keyguard, task, true /*displaySleeping*/,
+                false /* displayShouldSleep */, true /* isFocusedTask */,
+                true /* keyguardShowing */, true /* expectWakeFromSleep */,
+                false /* expectResumeTopActivity */);
+
+        // Make sure we wake and don't resume when the display is turning on and the keyguard is
+        // not showing as unfocused.
+        verifySleepTokenBehavior(display, keyguard, task, true /*displaySleeping*/,
+                false /* displayShouldSleep */, false /* isFocusedTask */,
+                false /* keyguardShowing */, true /* expectWakeFromSleep */,
+                false /* expectResumeTopActivity */);
+
+        // Should not do anything if the display state hasn't changed.
+        verifySleepTokenBehavior(display, keyguard, task, false /*displaySleeping*/,
+                false /* displayShouldSleep */, true /* isFocusedTask */,
+                false /* keyguardShowing */, false /* expectWakeFromSleep */,
+                false /* expectResumeTopActivity */);
+    }
+
+    private void verifySleepTokenBehavior(DisplayContent display, KeyguardController keyguard,
+            Task task, boolean displaySleeping, boolean displayShouldSleep,
+            boolean isFocusedTask, boolean keyguardShowing, boolean expectWakeFromSleep,
+            boolean expectResumeTopActivity) {
+        reset(task);
+
+        doReturn(displayShouldSleep).when(display).shouldSleep();
+        doReturn(displaySleeping).when(display).isSleeping();
+        doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
+
+        doReturn(isFocusedTask).when(task).isFocusedRootTaskOnDisplay();
+        doReturn(isFocusedTask ? task : null).when(display).getFocusedRootTask();
+        TaskDisplayArea defaultTaskDisplayArea = display.getDefaultTaskDisplayArea();
+        doReturn(isFocusedTask ? task : null).when(defaultTaskDisplayArea).getFocusedRootTask();
+        mRootWindowContainer.applySleepTokens(true);
+        verify(task, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
+        verify(task, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
+                null /* target */, null /* targetOptions */);
+    }
+
+    @Test
+    public void testAwakeFromSleepingWithAppConfiguration() {
+        final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        activity.moveFocusableActivityToTop("test");
+        assertTrue(activity.getRootTask().isFocusedRootTaskOnDisplay());
+        ActivityRecordTests.setRotatedScreenOrientationSilently(activity);
+
+        final Configuration rotatedConfig = new Configuration();
+        display.computeScreenConfiguration(rotatedConfig, display.getDisplayRotation()
+                .rotationForOrientation(activity.getOrientation(), display.getRotation()));
+        assertNotEquals(activity.getConfiguration().orientation, rotatedConfig.orientation);
+        // Assume the activity was shown in different orientation. For example, the top activity is
+        // landscape and the portrait lockscreen is shown.
+        activity.setLastReportedConfiguration(
+                new MergedConfiguration(mAtm.getGlobalConfiguration(), rotatedConfig));
+        activity.setState(Task.ActivityState.STOPPED, "sleep");
+
+        display.setIsSleeping(true);
+        doReturn(false).when(display).shouldSleep();
+        // Allow to resume when awaking.
+        setBooted(mAtm);
+        mRootWindowContainer.applySleepTokens(true);
+
+        // The display orientation should be changed by the activity so there is no relaunch.
+        verify(activity, never()).relaunchActivityLocked(anyBoolean());
+        assertEquals(rotatedConfig.orientation, display.getConfiguration().orientation);
+    }
+
+    /**
+     * Verifies that removal of activity with task and root task is done correctly.
+     */
+    @Test
+    public void testRemovingRootTaskOnAppCrash() {
+        final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
+                .getDefaultTaskDisplayArea();
+        final int originalRootTaskCount = defaultTaskDisplayArea.getRootTaskCount();
+        final Task rootTask = defaultTaskDisplayArea.createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+        final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(rootTask).build();
+
+        assertEquals(originalRootTaskCount + 1, defaultTaskDisplayArea.getRootTaskCount());
+
+        // Let's pretend that the app has crashed.
+        firstActivity.app.setThread(null);
+        mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test");
+
+        // Verify that the root task was removed.
+        assertEquals(originalRootTaskCount, defaultTaskDisplayArea.getRootTaskCount());
+    }
+
+    /**
+     * Verifies that removal of activities with task and root task is done correctly when there are
+     * several task display areas.
+     */
+    @Test
+    public void testRemovingRootTaskOnAppCrash_multipleDisplayAreas() {
+        final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
+                .getDefaultTaskDisplayArea();
+        final int originalRootTaskCount = defaultTaskDisplayArea.getRootTaskCount();
+        final Task rootTask = defaultTaskDisplayArea.createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+        final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(rootTask).build();
+        assertEquals(originalRootTaskCount + 1, defaultTaskDisplayArea.getRootTaskCount());
+
+        final DisplayContent dc = defaultTaskDisplayArea.getDisplayContent();
+        final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
+                dc, mRootWindowContainer.mWmService, "TestTaskDisplayArea", FEATURE_VENDOR_FIRST);
+        final Task secondRootTask = secondTaskDisplayArea.createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+        new ActivityBuilder(mAtm).setTask(secondRootTask).setUseProcess(firstActivity.app).build();
+        assertEquals(1, secondTaskDisplayArea.getRootTaskCount());
+
+        // Let's pretend that the app has crashed.
+        firstActivity.app.setThread(null);
+        mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test");
+
+        // Verify that the root tasks were removed.
+        assertEquals(originalRootTaskCount, defaultTaskDisplayArea.getRootTaskCount());
+        assertEquals(0, secondTaskDisplayArea.getRootTaskCount());
+    }
+
+    @Test
+    public void testFocusability() {
+        final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
+                .getDefaultTaskDisplayArea();
+        final Task task = defaultTaskDisplayArea.createRootTask(
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
+
+        // Created tasks are focusable by default.
+        assertTrue(task.isTopActivityFocusable());
+        assertTrue(activity.isFocusable());
+
+        // If the task is made unfocusable, its activities should inherit that.
+        task.setFocusable(false);
+        assertFalse(task.isTopActivityFocusable());
+        assertFalse(activity.isFocusable());
+
+        final Task pinnedTask = defaultTaskDisplayArea.createRootTask(
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm)
+                .setTask(pinnedTask).build();
+
+        // We should not be focusable when in pinned mode
+        assertFalse(pinnedTask.isTopActivityFocusable());
+        assertFalse(pinnedActivity.isFocusable());
+
+        // Add flag forcing focusability.
+        pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE;
+
+        // Task with FLAG_ALWAYS_FOCUSABLE should be focusable.
+        assertTrue(pinnedTask.isTopActivityFocusable());
+        assertTrue(pinnedActivity.isFocusable());
+    }
+
+    /**
+     * Verify that split-screen primary root task will be chosen if activity is launched that
+     * targets split-screen secondary, but a matching existing instance is found on top of
+     * split-screen primary root task.
+     */
+    @Test
+    public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() {
+        // Create primary split-screen root task with a task and an activity.
+        final Task primaryRootTask = mRootWindowContainer.getDefaultTaskDisplayArea()
+                .createRootTask(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+                        true /* onTop */);
+        final Task task = new TaskBuilder(mSupervisor).setParentTask(primaryRootTask).build();
+        final ActivityRecord r = new ActivityBuilder(mAtm).setTask(task).build();
+
+        // Find a launch root task for the top activity in split-screen primary, while requesting
+        // split-screen secondary.
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+        final Task result =
+                mRootWindowContainer.getLaunchRootTask(r, options, task, true /* onTop */);
+
+        // Assert that the primary root task is returned.
+        assertEquals(primaryRootTask, result);
+    }
+
+    /**
+     * Verify that home root task would be moved to front when the top activity is Recents.
+     */
+    @Test
+    public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
+        // Create root task/task on default display.
+        final Task targetRootTask = new TaskBuilder(mSupervisor)
+                .setCreateActivity(true)
+                .setOnTop(false)
+                .build();
+        final Task targetTask = targetRootTask.getBottomMostTask();
+
+        // Create Recents on top of the display.
+        final Task rootTask = new TaskBuilder(mSupervisor)
+                .setCreateActivity(true)
+                .setActivityType(ACTIVITY_TYPE_RECENTS)
+                .build();
+
+        final String reason = "findTaskToMoveToFront";
+        mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
+                false);
+
+        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+        verify(taskDisplayArea).moveHomeRootTaskToFront(contains(reason));
+    }
+
+    /**
+     * Verify that home root task won't be moved to front if the top activity on other display is
+     * Recents.
+     */
+    @Test
+    public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() {
+        // Create tasks on default display.
+        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+        final Task targetRootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, false /* onTop */);
+        final Task targetTask = new TaskBuilder(mSupervisor).setParentTask(targetRootTask).build();
+
+        // Create Recents on secondary display.
+        final TestDisplayContent secondDisplay = addNewDisplayContentAt(
+                DisplayContent.POSITION_TOP);
+        final Task rootTask = secondDisplay.getDefaultTaskDisplayArea()
+                .createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
+        new ActivityBuilder(mAtm).setTask(rootTask).build();
+
+        final String reason = "findTaskToMoveToFront";
+        mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
+                false);
+
+        verify(taskDisplayArea, never()).moveHomeRootTaskToFront(contains(reason));
+    }
+
+    /**
+     * Verify if a root task is not at the topmost position, it should be able to resume its
+     * activity if the root task is the top focused.
+     */
+    @Test
+    public void testResumeActivityWhenNonTopmostRootTaskIsTopFocused() {
+        // Create a root task at bottom.
+        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+        final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, false /* onTop */));
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build();
+        taskDisplayArea.positionChildAt(POSITION_BOTTOM, rootTask, false /*includingParents*/);
+
+        // Assume the task is not at the topmost position (e.g. behind always-on-top root tasks)
+        // but it is the current top focused task.
+        assertFalse(rootTask.isTopRootTaskInDisplayArea());
+        doReturn(rootTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
+
+        // Use the task as target to resume.
+        mRootWindowContainer.resumeFocusedTasksTopActivities(
+                rootTask, activity, null /* targetOptions */);
+
+        // Verify the target task should resume its activity.
+        verify(rootTask, times(1)).resumeTopActivityUncheckedLocked(
+                eq(activity), eq(null /* targetOptions */));
+    }
+
+    /**
+     * Verify that home activity will be started on a display even if another display has a
+     * focusable activity.
+     */
+    @Test
+    public void testResumeFocusedRootTasksStartsHomeActivity_NoActivities() {
+        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+        taskDisplayArea.getRootHomeTask().removeIfPossible();
+        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.resumeFocusedTasksTopActivities();
+
+        // Verify that home activity was started on the default display
+        verify(mRootWindowContainer).resumeHomeActivity(any(), any(), eq(taskDisplayArea));
+    }
+
+    /**
+     * Verify that home activity will be started on a display even if another display has a
+     * focusable activity.
+     */
+    @Test
+    public void testResumeFocusedRootTasksStartsHomeActivity_ActivityOnSecondaryScreen() {
+        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+        taskDisplayArea.getRootHomeTask().removeIfPossible();
+        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 rootTask = secondDisplay.getDefaultTaskDisplayArea().createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        new ActivityBuilder(mAtm).setTask(rootTask).build();
+
+        doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any());
+
+        mAtm.setBooted(true);
+
+        // Trigger resume on all displays
+        mRootWindowContainer.resumeFocusedTasksTopActivities();
+
+        // Verify that home activity was started on the default display
+        verify(mRootWindowContainer).resumeHomeActivity(any(), any(), eq(taskDisplayArea));
+    }
+
+    /**
+     * Verify that a lingering transition is being executed in case the activity to be resumed is
+     * already resumed
+     */
+    @Test
+    public void testResumeActivityLingeringTransition() {
+        // Create a root task at top.
+        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+        final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, false /* onTop */));
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setTask(rootTask).setOnTop(true).build();
+        activity.setState(Task.ActivityState.RESUMED, "test");
+
+        // Assume the task is at the topmost position
+        assertTrue(rootTask.isTopRootTaskInDisplayArea());
+
+        // Use the task as target to resume.
+        mRootWindowContainer.resumeFocusedTasksTopActivities();
+
+        // Verify the lingering app transition is being executed because it's already resumed
+        verify(rootTask, times(1)).executeAppTransition(any());
+    }
+
+    @Test
+    public void testResumeActivityLingeringTransition_notExecuted() {
+        // Create a root task at bottom.
+        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+        final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, false /* onTop */));
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setTask(rootTask).setOnTop(true).build();
+        activity.setState(Task.ActivityState.RESUMED, "test");
+        taskDisplayArea.positionChildAt(POSITION_BOTTOM, rootTask, false /*includingParents*/);
+
+        // Assume the task is at the topmost position
+        assertFalse(rootTask.isTopRootTaskInDisplayArea());
+        doReturn(rootTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
+
+        // Use the task as target to resume.
+        mRootWindowContainer.resumeFocusedTasksTopActivities();
+
+        // Verify the lingering app transition is being executed because it's already resumed
+        verify(rootTask, never()).executeAppTransition(any());
+    }
+
+    /**
+     * Tests that home activities can be started on the displays that supports system decorations.
+     */
+    @Test
+    public void testStartHomeOnAllDisplays() {
+        mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+        mockResolveSecondaryHomeActivity();
+
+        // Create secondary displays.
+        final TestDisplayContent secondDisplay =
+                new TestDisplayContent.Builder(mAtm, 1000, 1500)
+                        .setSystemDecorations(true).build();
+
+        doReturn(true).when(mRootWindowContainer)
+                .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
+        doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
+                anyBoolean());
+
+        mRootWindowContainer.startHomeOnAllDisplays(0, "testStartHome");
+
+        assertTrue(mRootWindowContainer.getDefaultDisplay().getTopRootTask().isActivityTypeHome());
+        assertNotNull(secondDisplay.getTopRootTask());
+        assertTrue(secondDisplay.getTopRootTask().isActivityTypeHome());
+    }
+
+    /**
+     * Tests that home activities won't be started before booting when display added.
+     */
+    @Test
+    public void testNotStartHomeBeforeBoot() {
+        final int displayId = 1;
+        final boolean isBooting = mAtm.mAmInternal.isBooting();
+        final boolean isBooted = mAtm.mAmInternal.isBooted();
+        try {
+            mAtm.mAmInternal.setBooting(false);
+            mAtm.mAmInternal.setBooted(false);
+            mRootWindowContainer.onDisplayAdded(displayId);
+            verify(mRootWindowContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt());
+        } finally {
+            mAtm.mAmInternal.setBooting(isBooting);
+            mAtm.mAmInternal.setBooted(isBooted);
+        }
+    }
+
+    /**
+     * Tests whether home can be started if being instrumented.
+     */
+    @Test
+    public void testCanStartHomeWhenInstrumented() {
+        final ActivityInfo info = new ActivityInfo();
+        info.applicationInfo = new ApplicationInfo();
+        final WindowProcessController app = mock(WindowProcessController.class);
+        doReturn(app).when(mAtm).getProcessController(any(), anyInt());
+
+        // Can not start home if we don't want to start home while home is being instrumented.
+        doReturn(true).when(app).isInstrumenting();
+        final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
+                .getDefaultTaskDisplayArea();
+        assertFalse(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
+                false /* allowInstrumenting*/));
+
+        // Can start home for other cases.
+        assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
+                true /* allowInstrumenting*/));
+
+        doReturn(false).when(app).isInstrumenting();
+        assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
+                false /* allowInstrumenting*/));
+        assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
+                true /* allowInstrumenting*/));
+    }
+
+    /**
+     * Tests that secondary home activity should not be resolved if device is still locked.
+     */
+    @Test
+    public void testStartSecondaryHomeOnDisplayWithUserKeyLocked() {
+        // Create secondary displays.
+        final TestDisplayContent secondDisplay =
+                new TestDisplayContent.Builder(mAtm, 1000, 1500)
+                        .setSystemDecorations(true).build();
+
+        // Use invalid user id to let StorageManager.isUserKeyUnlocked() return false.
+        final int currentUser = mRootWindowContainer.mCurrentUser;
+        mRootWindowContainer.mCurrentUser = -1;
+
+        mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "testStartSecondaryHome",
+                secondDisplay.mDisplayId, true /* allowInstrumenting */, true /* fromHomeKey */);
+
+        try {
+            verify(mRootWindowContainer, never()).resolveSecondaryHomeActivity(anyInt(), any());
+        } finally {
+            mRootWindowContainer.mCurrentUser = currentUser;
+        }
+    }
+
+    /**
+     * Tests that secondary home activity should not be resolved if display does not support system
+     * decorations.
+     */
+    @Test
+    public void testStartSecondaryHomeOnDisplayWithoutSysDecorations() {
+        // Create secondary displays.
+        final TestDisplayContent secondDisplay =
+                new TestDisplayContent.Builder(mAtm, 1000, 1500)
+                        .setSystemDecorations(false).build();
+
+        mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "testStartSecondaryHome",
+                secondDisplay.mDisplayId, true /* allowInstrumenting */, true /* fromHomeKey */);
+
+        verify(mRootWindowContainer, never()).resolveSecondaryHomeActivity(anyInt(), any());
+    }
+
+    /**
+     * Tests that when starting {@link #ResolverActivity} for home, it should use the standard
+     * activity type (in a new root task) so the order of back stack won't be broken.
+     */
+    @Test
+    public void testStartResolverActivityForHome() {
+        final ActivityInfo info = new ActivityInfo();
+        info.applicationInfo = new ApplicationInfo();
+        info.applicationInfo.packageName = "android";
+        info.name = ResolverActivity.class.getName();
+        doReturn(info).when(mRootWindowContainer).resolveHomeActivity(anyInt(), any());
+
+        mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "test", DEFAULT_DISPLAY);
+        final ActivityRecord resolverActivity = mRootWindowContainer.topRunningActivity();
+
+        assertEquals(info, resolverActivity.info);
+        assertEquals(ACTIVITY_TYPE_STANDARD, resolverActivity.getRootTask().getActivityType());
+    }
+
+    /**
+     * Tests that secondary home should be selected if primary home not set.
+     */
+    @Test
+    public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSet() {
+        // Setup: primary home not set.
+        final Intent primaryHomeIntent = mAtm.getHomeIntent();
+        final ActivityInfo aInfoPrimary = new ActivityInfo();
+        aInfoPrimary.name = ResolverActivity.class.getName();
+        doReturn(aInfoPrimary).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
+                refEq(primaryHomeIntent));
+        // Setup: set secondary home.
+        mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */);
+
+        // Run the test.
+        final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
+                .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
+        final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
+        assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
+        assertEquals(aInfoSecondary.applicationInfo.packageName,
+                resolvedInfo.first.applicationInfo.packageName);
+    }
+
+    /**
+     * Tests that the default secondary home activity is always picked when it is in forced by
+     * config_useSystemProvidedLauncherForSecondary.
+     */
+    @Test
+    public void testResolveSecondaryHomeActivityForced() {
+        // SetUp: set primary home.
+        mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+        // SetUp: set secondary home and force it.
+        mockResolveHomeActivity(false /* primaryHome */, true /* forceSystemProvided */);
+        final Intent secondaryHomeIntent =
+                mAtm.getSecondaryHomeIntent(null /* preferredPackage */);
+        final List<ResolveInfo> resolutions = new ArrayList<>();
+        final ResolveInfo resolveInfo = new ResolveInfo();
+        final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
+        resolveInfo.activityInfo = aInfoSecondary;
+        resolutions.add(resolveInfo);
+        doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(),
+                refEq(secondaryHomeIntent));
+        doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
+                anyBoolean());
+
+        // Run the test.
+        final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
+                .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
+        assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
+        assertEquals(aInfoSecondary.applicationInfo.packageName,
+                resolvedInfo.first.applicationInfo.packageName);
+    }
+
+    /**
+     * Tests that secondary home should be selected if primary home not support secondary displays
+     * or there is no matched activity in the same package as selected primary home.
+     */
+    @Test
+    public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSupportMultiDisplay() {
+        // Setup: there is no matched activity in the same package as selected primary home.
+        mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+        final List<ResolveInfo> resolutions = new ArrayList<>();
+        doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
+        // Setup: set secondary home.
+        mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */);
+
+        // Run the test.
+        final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
+                .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
+        final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
+        assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
+        assertEquals(aInfoSecondary.applicationInfo.packageName,
+                resolvedInfo.first.applicationInfo.packageName);
+    }
+    /**
+     * Tests that primary home activity should be selected if it already support secondary displays.
+     */
+    @Test
+    public void testResolveSecondaryHomeActivityWhenPrimaryHomeSupportMultiDisplay() {
+        // SetUp: set primary home.
+        mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+        // SetUp: put primary home info on 2nd item
+        final List<ResolveInfo> resolutions = new ArrayList<>();
+        final ResolveInfo infoFake1 = new ResolveInfo();
+        infoFake1.activityInfo = new ActivityInfo();
+        infoFake1.activityInfo.name = "fakeActivity1";
+        infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
+        infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
+        final ResolveInfo infoFake2 = new ResolveInfo();
+        final ActivityInfo aInfoPrimary = getFakeHomeActivityInfo(true /* primaryHome */);
+        infoFake2.activityInfo = aInfoPrimary;
+        resolutions.add(infoFake1);
+        resolutions.add(infoFake2);
+        doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
+
+        doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
+                anyBoolean());
+
+        // Run the test.
+        final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
+                .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
+        assertEquals(aInfoPrimary.name, resolvedInfo.first.name);
+        assertEquals(aInfoPrimary.applicationInfo.packageName,
+                resolvedInfo.first.applicationInfo.packageName);
+    }
+
+    /**
+     * Tests that the first one that matches should be selected if there are multiple activities.
+     */
+    @Test
+    public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() {
+        // SetUp: set primary home.
+        mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+        // Setup: prepare two eligible activity info.
+        final List<ResolveInfo> resolutions = new ArrayList<>();
+        final ResolveInfo infoFake1 = new ResolveInfo();
+        infoFake1.activityInfo = new ActivityInfo();
+        infoFake1.activityInfo.name = "fakeActivity1";
+        infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
+        infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
+        final ResolveInfo infoFake2 = new ResolveInfo();
+        infoFake2.activityInfo = new ActivityInfo();
+        infoFake2.activityInfo.name = "fakeActivity2";
+        infoFake2.activityInfo.applicationInfo = new ApplicationInfo();
+        infoFake2.activityInfo.applicationInfo.packageName = "fakePackage2";
+        resolutions.add(infoFake1);
+        resolutions.add(infoFake2);
+        doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
+
+        doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
+                anyBoolean());
+
+        // Use the first one of matched activities in the same package as selected primary home.
+        final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
+                .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
+
+        assertEquals(infoFake1.activityInfo.applicationInfo.packageName,
+                resolvedInfo.first.applicationInfo.packageName);
+        assertEquals(infoFake1.activityInfo.name, resolvedInfo.first.name);
+    }
+
+    /**
+     * Test that {@link RootWindowContainer#getLaunchRootTask} with the real caller id will get the
+     * expected root task when requesting the activity launch on the secondary display.
+     */
+    @Test
+    public void testGetLaunchRootTaskWithRealCallerId() {
+        // Create a non-system owned virtual display.
+        final TestDisplayContent secondaryDisplay =
+                new TestDisplayContent.Builder(mAtm, 1000, 1500)
+                        .setType(TYPE_VIRTUAL).setOwnerUid(100).build();
+
+        // Create an activity with specify the original launch pid / uid.
+        final ActivityRecord r = new ActivityBuilder(mAtm).setLaunchedFromPid(200)
+                .setLaunchedFromUid(200).build();
+
+        // Simulate ActivityStarter to find a launch root task for requesting the activity to launch
+        // on the secondary display with realCallerId.
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(secondaryDisplay.mDisplayId);
+        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        doReturn(true).when(mSupervisor).canPlaceEntityOnDisplay(secondaryDisplay.mDisplayId,
+                300 /* test realCallerPid */, 300 /* test realCallerUid */, r.info);
+        final Task result = mRootWindowContainer.getLaunchRootTask(r, options,
+                null /* task */, true /* onTop */, null, 300 /* test realCallerPid */,
+                300 /* test realCallerUid */);
+
+        // Assert that the root task is returned as expected.
+        assertNotNull(result);
+        assertEquals("The display ID of the root task should same as secondary display ",
+                secondaryDisplay.mDisplayId, result.getDisplayId());
+    }
+
+    @Test
+    public void testGetValidLaunchRootTaskOnDisplayWithCandidateRootTask() {
+        // Create a root task with an activity on secondary display.
+        final TestDisplayContent secondaryDisplay = new TestDisplayContent.Builder(mAtm, 300,
+                600).build();
+        final Task task = new TaskBuilder(mSupervisor)
+                .setDisplay(secondaryDisplay).build();
+        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 rootTask = mRootWindowContainer.getValidLaunchRootTaskInTaskDisplayArea(
+                mRootWindowContainer.getDefaultTaskDisplayArea(), activity, task,
+                null /* options */, null /* launchParams */);
+        assertEquals(task, rootTask);
+    }
+
+    @Test
+    public void testSwitchUser_missingHomeRootTask() {
+        final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        doReturn(fullscreenTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
+
+        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+        Task rootHomeTask = taskDisplayArea.getRootHomeTask();
+        if (rootHomeTask != null) {
+            rootHomeTask.removeImmediately();
+        }
+        assertNull(taskDisplayArea.getRootHomeTask());
+
+        int currentUser = mRootWindowContainer.mCurrentUser;
+        int otherUser = currentUser + 1;
+
+        mRootWindowContainer.switchUser(otherUser, null);
+
+        assertNotNull(taskDisplayArea.getRootHomeTask());
+        assertEquals(taskDisplayArea.getTopRootTask(), taskDisplayArea.getRootHomeTask());
+    }
+
+    /**
+     * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity
+     * info for test cases.
+     *
+     * @param primaryHome Indicate to use primary home intent as parameter, otherwise, use
+     *                    secondary home intent.
+     * @param forceSystemProvided Indicate to force using system provided home activity.
+     */
+    private void mockResolveHomeActivity(boolean primaryHome, boolean forceSystemProvided) {
+        ActivityInfo targetActivityInfo = getFakeHomeActivityInfo(primaryHome);
+        Intent targetIntent;
+        if (primaryHome) {
+            targetIntent = mAtm.getHomeIntent();
+        } else {
+            Resources resources = mContext.getResources();
+            spyOn(resources);
+            doReturn(targetActivityInfo.applicationInfo.packageName).when(resources).getString(
+                    com.android.internal.R.string.config_secondaryHomePackage);
+            doReturn(forceSystemProvided).when(resources).getBoolean(
+                    com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
+            targetIntent = mAtm.getSecondaryHomeIntent(null /* preferredPackage */);
+        }
+        doReturn(targetActivityInfo).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
+                refEq(targetIntent));
+    }
+
+    /**
+     * Mock {@link RootWindowContainer#resolveSecondaryHomeActivity} for returning consistent
+     * activity info for test cases.
+     */
+    private void mockResolveSecondaryHomeActivity() {
+        final Intent secondaryHomeIntent = mAtm
+                .getSecondaryHomeIntent(null /* preferredPackage */);
+        final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false);
+        doReturn(Pair.create(aInfoSecondary, secondaryHomeIntent)).when(mRootWindowContainer)
+                .resolveSecondaryHomeActivity(anyInt(), any());
+    }
+
+    private ActivityInfo getFakeHomeActivityInfo(boolean primaryHome) {
+        final ActivityInfo aInfo = new ActivityInfo();
+        aInfo.name = primaryHome ? "fakeHomeActivity" : "fakeSecondaryHomeActivity";
+        aInfo.applicationInfo = new ApplicationInfo();
+        aInfo.applicationInfo.packageName =
+                primaryHome ? "fakeHomePackage" : "fakeSecondaryHomePackage";
+        return  aInfo;
+    }
 }
 
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 eba5634..92d4ede 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -28,6 +28,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -35,13 +36,16 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
 import static com.android.server.wm.Task.ActivityState.RESUMED;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -56,8 +60,6 @@
 
 import com.android.server.wm.LaunchParamsController.LaunchParams;
 
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -72,35 +74,17 @@
 @RunWith(WindowTestRunner.class)
 public class TaskDisplayAreaTests extends WindowTestsBase {
 
-    private Task mPinnedTask;
-
-    @Before
-    public void setUp() throws Exception {
-        mPinnedTask = createTaskStackOnDisplay(
-                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
-        // Stack should contain visible app window to be considered visible.
-        assertFalse(mPinnedTask.isVisible());
-        final ActivityRecord pinnedApp = createNonAttachedActivityRecord(mDisplayContent);
-        mPinnedTask.addChild(pinnedApp, 0 /* addPos */);
-        assertTrue(mPinnedTask.isVisible());
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mPinnedTask.removeImmediately();
-    }
-
     @Test
     public void getOrCreateLaunchRootRespectsResolvedWindowingMode() {
-        final Task rootTask = createTaskStackOnDisplay(
-                WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final Task rootTask = createTask(
+                mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
         rootTask.mCreatedByOrganizer = true;
         final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
         taskDisplayArea.setLaunchRootTask(
                 rootTask, new int[]{WINDOWING_MODE_FREEFORM}, new int[]{ACTIVITY_TYPE_STANDARD});
 
-        final Task candidateRootTask = createTaskStackOnDisplay(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final Task candidateRootTask = createTask(
+                mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
         final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
         final LaunchParams launchParams = new LaunchParams();
         launchParams.mWindowingMode = WINDOWING_MODE_FREEFORM;
@@ -113,15 +97,15 @@
 
     @Test
     public void getOrCreateLaunchRootUsesActivityOptionsWindowingMode() {
-        final Task rootTask = createTaskStackOnDisplay(
-                WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final Task rootTask = createTask(
+                mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
         rootTask.mCreatedByOrganizer = true;
         final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
         taskDisplayArea.setLaunchRootTask(
                 rootTask, new int[]{WINDOWING_MODE_FREEFORM}, new int[]{ACTIVITY_TYPE_STANDARD});
 
-        final Task candidateRootTask = createTaskStackOnDisplay(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final Task candidateRootTask = createTask(
+                mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
         final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
         final ActivityOptions options = ActivityOptions.makeBasic();
         options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
@@ -134,9 +118,8 @@
 
     @Test
     public void testActivityWithZBoost_taskDisplayAreaDoesNotMoveUp() {
-        final Task stack = createTaskStackOnDisplay(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
         final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
         task.addChild(activity, 0 /* addPos */);
         final TaskDisplayArea taskDisplayArea = activity.getDisplayArea();
@@ -152,87 +135,103 @@
     }
 
     @Test
-    public void testStackPositionChildAt() {
-        // Test that always-on-top stack can't be moved to position other than top.
-        final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
-        final Task stack2 = createTaskStackOnDisplay(mDisplayContent);
+    public void testRootTaskPositionChildAt() {
+        Task pinnedTask = createTask(
+                mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        // Root task should contain visible app window to be considered visible.
+        assertFalse(pinnedTask.isVisible());
+        final ActivityRecord pinnedApp = createNonAttachedActivityRecord(mDisplayContent);
+        pinnedTask.addChild(pinnedApp, 0 /* addPos */);
+        assertTrue(pinnedTask.isVisible());
 
-        final WindowContainer taskStackContainer = stack1.getParent();
+        // Test that always-on-top root task can't be moved to position other than top.
+        final Task rootTask1 = createTask(mDisplayContent);
+        final Task rootTask2 = createTask(mDisplayContent);
 
-        final int stack1Pos = taskStackContainer.mChildren.indexOf(stack1);
-        final int stack2Pos = taskStackContainer.mChildren.indexOf(stack2);
-        final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedTask);
-        assertThat(pinnedStackPos).isGreaterThan(stack2Pos);
-        assertThat(stack2Pos).isGreaterThan(stack1Pos);
+        final WindowContainer taskContainer = rootTask1.getParent();
 
-        taskStackContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, mPinnedTask, false);
-        assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1);
-        assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2);
-        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask);
+        final int rootTask1Pos = taskContainer.mChildren.indexOf(rootTask1);
+        final int rootTask2Pos = taskContainer.mChildren.indexOf(rootTask2);
+        final int pinnedTaskPos = taskContainer.mChildren.indexOf(pinnedTask);
+        assertThat(pinnedTaskPos).isGreaterThan(rootTask2Pos);
+        assertThat(rootTask2Pos).isGreaterThan(rootTask1Pos);
 
-        taskStackContainer.positionChildAt(1, mPinnedTask, false);
-        assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1);
-        assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2);
-        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask);
+        taskContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, pinnedTask, false);
+        assertEquals(taskContainer.mChildren.get(rootTask1Pos), rootTask1);
+        assertEquals(taskContainer.mChildren.get(rootTask2Pos), rootTask2);
+        assertEquals(taskContainer.mChildren.get(pinnedTaskPos), pinnedTask);
+
+        taskContainer.positionChildAt(1, pinnedTask, false);
+        assertEquals(taskContainer.mChildren.get(rootTask1Pos), rootTask1);
+        assertEquals(taskContainer.mChildren.get(rootTask2Pos), rootTask2);
+        assertEquals(taskContainer.mChildren.get(pinnedTaskPos), pinnedTask);
     }
 
     @Test
-    public void testStackPositionBelowPinnedStack() {
-        // Test that no stack can be above pinned stack.
-        final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
+    public void testRootTaskPositionBelowPinnedRootTask() {
+        Task pinnedTask = createTask(
+                mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        // Root task should contain visible app window to be considered visible.
+        assertFalse(pinnedTask.isVisible());
+        final ActivityRecord pinnedApp = createNonAttachedActivityRecord(mDisplayContent);
+        pinnedTask.addChild(pinnedApp, 0 /* addPos */);
+        assertTrue(pinnedTask.isVisible());
 
-        final WindowContainer taskStackContainer = stack1.getParent();
+        // Test that no root task can be above pinned root task.
+        final Task rootTask1 = createTask(mDisplayContent);
 
-        final int stackPos = taskStackContainer.mChildren.indexOf(stack1);
-        final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedTask);
-        assertThat(pinnedStackPos).isGreaterThan(stackPos);
+        final WindowContainer taskContainer = rootTask1.getParent();
 
-        taskStackContainer.positionChildAt(WindowContainer.POSITION_TOP, stack1, false);
-        assertEquals(taskStackContainer.mChildren.get(stackPos), stack1);
-        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask);
+        final int rootTaskPos = taskContainer.mChildren.indexOf(rootTask1);
+        final int pinnedTaskPos = taskContainer.mChildren.indexOf(pinnedTask);
+        assertThat(pinnedTaskPos).isGreaterThan(rootTaskPos);
 
-        taskStackContainer.positionChildAt(taskStackContainer.mChildren.size() - 1, stack1, false);
-        assertEquals(taskStackContainer.mChildren.get(stackPos), stack1);
-        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask);
+        taskContainer.positionChildAt(WindowContainer.POSITION_TOP, rootTask1, false);
+        assertEquals(taskContainer.mChildren.get(rootTaskPos), rootTask1);
+        assertEquals(taskContainer.mChildren.get(pinnedTaskPos), pinnedTask);
+
+        taskContainer.positionChildAt(taskContainer.mChildren.size() - 1, rootTask1, false);
+        assertEquals(taskContainer.mChildren.get(rootTaskPos), rootTask1);
+        assertEquals(taskContainer.mChildren.get(pinnedTaskPos), pinnedTask);
     }
 
     @Test
-    public void testDisplayPositionWithPinnedStack() {
-        // Make sure the display is trusted display which capable to move the stack to top.
+    public void testDisplayPositionWithPinnedRootTask() {
+        // Make sure the display is trusted display which capable to move the root task to top.
         spyOn(mDisplayContent);
         doReturn(true).when(mDisplayContent).isTrusted();
 
-        // Allow child stack to move to top.
+        // Allow child root task to move to top.
         mDisplayContent.mDontMoveToTop = false;
 
-        // The display contains pinned stack that was added in {@link #setUp}.
-        final Task stack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
+        // The display contains pinned root task that was added in {@link #setUp}.
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
 
         // Add another display at top.
         mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(),
                 false /* includingParents */);
 
         // Move the task of {@code mDisplayContent} to top.
-        stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
-        final int indexOfDisplayWithPinnedStack = mWm.mRoot.mChildren.indexOf(mDisplayContent);
+        rootTask.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
+        final int indexOfDisplayWithPinnedRootTask = mWm.mRoot.mChildren.indexOf(mDisplayContent);
 
         assertEquals("The testing DisplayContent should be moved to top with task",
-                mWm.mRoot.getChildCount() - 1, indexOfDisplayWithPinnedStack);
+                mWm.mRoot.getChildCount() - 1, indexOfDisplayWithPinnedRootTask);
     }
 
     @Test
     public void testMovingChildTaskOnTop() {
-        // Make sure the display is trusted display which capable to move the stack to top.
+        // Make sure the display is trusted display which capable to move the root task to top.
         spyOn(mDisplayContent);
         doReturn(true).when(mDisplayContent).isTrusted();
 
-        // Allow child stack to move to top.
+        // Allow child root task to move to top.
         mDisplayContent.mDontMoveToTop = false;
 
-        // The display contains pinned stack that was added in {@link #setUp}.
-        Task stack = createTaskStackOnDisplay(mDisplayContent);
-        Task task = createTaskInStack(stack, 0 /* userId */);
+        // The display contains pinned root task that was added in {@link #setUp}.
+        Task rootTask = createTask(mDisplayContent);
+        Task task = createTaskInRootTask(rootTask, 0 /* userId */);
 
         // Add another display at top.
         mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(),
@@ -243,7 +242,7 @@
                 mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent));
 
         // Move the task of {@code mDisplayContent} to top.
-        stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
+        rootTask.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
 
         // Ensure that original display ({@code mDisplayContent}) is now on the top.
         assertEquals("The testing DisplayContent should be moved to top with task",
@@ -252,16 +251,16 @@
 
     @Test
     public void testDontMovingChildTaskOnTop() {
-        // Make sure the display is trusted display which capable to move the stack to top.
+        // Make sure the display is trusted display which capable to move the root task to top.
         spyOn(mDisplayContent);
         doReturn(true).when(mDisplayContent).isTrusted();
 
-        // Allow child stack to move to top.
+        // Allow child root task to move to top.
         mDisplayContent.mDontMoveToTop = true;
 
-        // The display contains pinned stack that was added in {@link #setUp}.
-        Task stack = createTaskStackOnDisplay(mDisplayContent);
-        Task task = createTaskInStack(stack, 0 /* userId */);
+        // The display contains pinned root task that was added in {@link #setUp}.
+        Task rootTask = createTask(mDisplayContent);
+        Task task = createTaskInRootTask(rootTask, 0 /* userId */);
 
         // Add another display at top.
         mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(),
@@ -272,7 +271,7 @@
                 mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent));
 
         // Try moving the task of {@code mDisplayContent} to top.
-        stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
+        rootTask.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
 
         // Ensure that original display ({@code mDisplayContent}) hasn't moved and is not
         // on the top.
@@ -282,8 +281,7 @@
 
     @Test
     public void testReuseTaskAsRootTask() {
-        final Task candidateTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final Task candidateTask = createTask(mDisplayContent);
         final int type = ACTIVITY_TYPE_STANDARD;
         assertGetOrCreateRootTask(WINDOWING_MODE_FULLSCREEN, type, candidateTask,
                 true /* reuseCandidate */);
@@ -312,7 +310,7 @@
     }
 
     @Test
-    public void testGetOrientation_nonResizableHomeStackWithHomeActivityPendingVisibilityChange() {
+    public void testGetOrientation_nonResizableHomeTaskWithHomeActivityPendingVisibilityChange() {
         final RootWindowContainer rootWindowContainer = mWm.mAtmService.mRootWindowContainer;
         final TaskDisplayArea defaultTaskDisplayArea =
                 rootWindowContainer.getDefaultTaskDisplayArea();
@@ -330,7 +328,7 @@
         ActivityRecord primarySplitActivity = primarySplitTask.getTopNonFinishingActivity();
         assertNotNull(primarySplitActivity);
         primarySplitActivity.setState(RESUMED,
-                "testGetOrientation_nonResizableHomeStackWithHomeActivityPendingVisibilityChange");
+                "testGetOrientation_nonResizableHomeTaskWithHomeActivityPendingVisibilityChange");
 
         ActivityRecord homeActivity = rootHomeTask.getTopNonFinishingActivity();
         if (homeActivity == null) {
@@ -350,14 +348,14 @@
         final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
                 mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
                 FEATURE_VENDOR_FIRST);
-        final Task firstStack = firstTaskDisplayArea.createRootTask(
+        final Task firstRootTask = firstTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final Task secondStack = secondTaskDisplayArea.createRootTask(
+        final Task secondRootTask = secondTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
         final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
-                .setTask(firstStack).build();
+                .setTask(firstRootTask).build();
         final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
-                .setTask(secondStack).build();
+                .setTask(secondRootTask).build();
 
         // Activity on TDA1 is focused
         mDisplayContent.setFocusedApp(firstActivity);
@@ -384,14 +382,14 @@
         final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
                 mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
                 FEATURE_VENDOR_FIRST);
-        final Task firstStack = firstTaskDisplayArea.createRootTask(
+        final Task firstRootTask = firstTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final Task secondStack = secondTaskDisplayArea.createRootTask(
+        final Task secondRootTask = secondTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
         final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
-                .setTask(firstStack).build();
+                .setTask(firstRootTask).build();
         final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
-                .setTask(secondStack).build();
+                .setTask(secondRootTask).build();
         firstTaskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         secondTaskDisplayArea.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
 
@@ -411,9 +409,9 @@
     @Test
     public void testIgnoreOrientationRequest() {
         final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
-        final Task stack = taskDisplayArea.createRootTask(
+        final Task task = taskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build();
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
 
         mDisplayContent.setFocusedApp(activity);
         activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -428,7 +426,7 @@
     @Test
     @UseTestDisplay
     public void testRemove_reparentToDefault() {
-        final Task task = createTaskStackOnDisplay(mDisplayContent);
+        final Task task = createTask(mDisplayContent);
         final TaskDisplayArea displayArea = task.getDisplayArea();
         displayArea.remove();
         assertTrue(displayArea.isRemoved());
@@ -442,8 +440,8 @@
 
     @Test
     @UseTestDisplay
-    public void testRemove_stackCreatedByOrganizer() {
-        final Task task = createTaskStackOnDisplay(mDisplayContent);
+    public void testRemove_rootTaskCreatedByOrganizer() {
+        final Task task = createTask(mDisplayContent);
         task.mCreatedByOrganizer = true;
         final TaskDisplayArea displayArea = task.getDisplayArea();
         displayArea.remove();
@@ -464,4 +462,221 @@
                 null /* activityOptions */);
         assertEquals(reuseCandidate, rootTask == candidateTask);
     }
+
+    @Test
+    public void testGetOrCreateRootHomeTask_defaultDisplay() {
+        TaskDisplayArea defaultTaskDisplayArea = mWm.mRoot.getDefaultTaskDisplayArea();
+
+        // Remove the current home root task if it exists so a new one can be created below.
+        Task homeTask = defaultTaskDisplayArea.getRootHomeTask();
+        if (homeTask != null) {
+            defaultTaskDisplayArea.removeChild(homeTask);
+        }
+        assertNull(defaultTaskDisplayArea.getRootHomeTask());
+
+        assertNotNull(defaultTaskDisplayArea.getOrCreateRootHomeTask());
+    }
+
+    @Test
+    public void testGetOrCreateRootHomeTask_supportedSecondaryDisplay() {
+        DisplayContent display = createNewDisplay();
+        doReturn(true).when(display).supportsSystemDecorations();
+
+        // Remove the current home root task if it exists so a new one can be created below.
+        TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
+        Task homeTask = taskDisplayArea.getRootHomeTask();
+        if (homeTask != null) {
+            taskDisplayArea.removeChild(homeTask);
+        }
+        assertNull(taskDisplayArea.getRootHomeTask());
+
+        assertNotNull(taskDisplayArea.getOrCreateRootHomeTask());
+    }
+
+    @Test
+    public void testGetOrCreateRootHomeTask_unsupportedSystemDecorations() {
+        DisplayContent display = createNewDisplay();
+        TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
+        doReturn(false).when(display).supportsSystemDecorations();
+
+        assertNull(taskDisplayArea.getRootHomeTask());
+        assertNull(taskDisplayArea.getOrCreateRootHomeTask());
+    }
+
+    @Test
+    public void testGetOrCreateRootHomeTask_untrustedDisplay() {
+        DisplayContent display = createNewDisplay();
+        TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
+        doReturn(false).when(display).isTrusted();
+
+        assertNull(taskDisplayArea.getRootHomeTask());
+        assertNull(taskDisplayArea.getOrCreateRootHomeTask());
+    }
+
+    @Test
+    public void testGetOrCreateRootHomeTask_dontMoveToTop() {
+        DisplayContent display = createNewDisplay();
+        display.mDontMoveToTop = true;
+        TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
+
+        assertNull(taskDisplayArea.getRootHomeTask());
+        assertNull(taskDisplayArea.getOrCreateRootHomeTask());
+    }
+
+    @Test
+    public void testLastFocusedRootTaskIsUpdatedWhenMovingRootTask() {
+        // Create a root task at bottom.
+        final TaskDisplayArea taskDisplayAreas =
+                mRootWindowContainer.getDefaultDisplay().getDefaultTaskDisplayArea();
+        final Task rootTask =
+                new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build();
+        final Task prevFocusedRootTask = taskDisplayAreas.getFocusedRootTask();
+
+        rootTask.moveToFront("moveRootTaskToFront");
+        // After moving the root task to front, the previous focused should be the last focused.
+        assertTrue(rootTask.isFocusedRootTaskOnDisplay());
+        assertEquals(prevFocusedRootTask, taskDisplayAreas.getLastFocusedRootTask());
+
+        rootTask.moveToBack("moveRootTaskToBack", null /* task */);
+        // After moving the root task to back, the root task should be the last focused.
+        assertEquals(rootTask, taskDisplayAreas.getLastFocusedRootTask());
+    }
+
+    /**
+     * This test simulates the picture-in-picture menu activity launches an activity to fullscreen
+     * root task. The fullscreen root task should be the top focused for resuming correctly.
+     */
+    @Test
+    public void testFullscreenRootTaskCanBeFocusedWhenFocusablePinnedRootTaskExists() {
+        // Create a pinned root task and move to front.
+        final Task pinnedRootTask = mRootWindowContainer.getDefaultTaskDisplayArea()
+                .createRootTask(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
+        final Task pinnedTask = new TaskBuilder(mAtm.mTaskSupervisor)
+                .setParentTask(pinnedRootTask).build();
+        new ActivityBuilder(mAtm).setActivityFlags(FLAG_ALWAYS_FOCUSABLE)
+                .setTask(pinnedTask).build();
+        pinnedRootTask.moveToFront("movePinnedRootTaskToFront");
+
+        // The focused root task should be the pinned root task.
+        assertTrue(pinnedRootTask.isFocusedRootTaskOnDisplay());
+
+        // Create a fullscreen root task and move to front.
+        final Task fullscreenRootTask = createTaskWithActivity(
+                mRootWindowContainer.getDefaultTaskDisplayArea(),
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP, true);
+        fullscreenRootTask.moveToFront("moveFullscreenRootTaskToFront");
+
+        // The focused root task should be the fullscreen root task.
+        assertTrue(fullscreenRootTask.isFocusedRootTaskOnDisplay());
+    }
+
+    /**
+     * Test {@link TaskDisplayArea#mPreferredTopFocusableRootTask} will be cleared when
+     * the root task is removed or moved to back, and the focused root task will be according to
+     * z-order.
+     */
+    @Test
+    public void testRootTaskShouldNotBeFocusedAfterMovingToBackOrRemoving() {
+        // Create a display which only contains 2 root task.
+        final DisplayContent display = addNewDisplayContentAt(POSITION_TOP);
+        final Task rootTask1 = createTaskWithActivity(display.getDefaultTaskDisplayArea(),
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP, true /* twoLevelTask */);
+        final Task rootTask2 = createTaskWithActivity(display.getDefaultTaskDisplayArea(),
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP, true /* twoLevelTask */);
+
+        // Put rootTask1 and rootTask2 on top.
+        rootTask1.moveToFront("moveRootTask1ToFront");
+        rootTask2.moveToFront("moveRootTask2ToFront");
+        assertTrue(rootTask2.isFocusedRootTaskOnDisplay());
+
+        // rootTask1 should be focused after moving rootTask2 to back.
+        rootTask2.moveToBack("moveRootTask2ToBack", null /* task */);
+        assertTrue(rootTask1.isFocusedRootTaskOnDisplay());
+
+        // rootTask2 should be focused after removing rootTask1.
+        rootTask1.getDisplayArea().removeRootTask(rootTask1);
+        assertTrue(rootTask2.isFocusedRootTaskOnDisplay());
+    }
+
+    /**
+     * This test enforces that alwaysOnTop root task is placed at proper position.
+     */
+    @Test
+    public void testAlwaysOnTopRootTaskLocation() {
+        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+        final Task alwaysOnTopRootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM,
+                ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setTask(alwaysOnTopRootTask).build();
+        alwaysOnTopRootTask.setAlwaysOnTop(true);
+        taskDisplayArea.positionChildAt(POSITION_TOP, alwaysOnTopRootTask,
+                false /* includingParents */);
+        assertTrue(alwaysOnTopRootTask.isAlwaysOnTop());
+        // Ensure always on top state is synced to the children of the root task.
+        assertTrue(alwaysOnTopRootTask.getTopNonFinishingActivity().isAlwaysOnTop());
+        assertEquals(alwaysOnTopRootTask, taskDisplayArea.getTopRootTask());
+
+        final Task pinnedRootTask = taskDisplayArea.createRootTask(
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        assertEquals(pinnedRootTask, taskDisplayArea.getRootPinnedTask());
+        assertEquals(pinnedRootTask, taskDisplayArea.getTopRootTask());
+
+        final Task anotherAlwaysOnTopRootTask = taskDisplayArea.createRootTask(
+                WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        anotherAlwaysOnTopRootTask.setAlwaysOnTop(true);
+        taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopRootTask,
+                false /* includingParents */);
+        assertTrue(anotherAlwaysOnTopRootTask.isAlwaysOnTop());
+        int topPosition = taskDisplayArea.getRootTaskCount() - 1;
+        // Ensure the new alwaysOnTop root task is put below the pinned root task, but on top of the
+        // existing alwaysOnTop root task.
+        assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopRootTask));
+
+        final Task nonAlwaysOnTopRootTask = taskDisplayArea.createRootTask(
+                WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        assertEquals(taskDisplayArea, nonAlwaysOnTopRootTask.getDisplayArea());
+        topPosition = taskDisplayArea.getRootTaskCount() - 1;
+        // Ensure the non-alwaysOnTop root task is put below the three alwaysOnTop root tasks, but
+        // above the existing other non-alwaysOnTop root tasks.
+        assertEquals(topPosition - 3, getTaskIndexOf(taskDisplayArea, nonAlwaysOnTopRootTask));
+
+        anotherAlwaysOnTopRootTask.setAlwaysOnTop(false);
+        taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopRootTask,
+                false /* includingParents */);
+        assertFalse(anotherAlwaysOnTopRootTask.isAlwaysOnTop());
+        // Ensure, when always on top is turned off for a root task, the root task is put just below
+        // all other always on top root tasks.
+        assertEquals(topPosition - 2, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopRootTask));
+        anotherAlwaysOnTopRootTask.setAlwaysOnTop(true);
+
+        // Ensure always on top state changes properly when windowing mode changes.
+        anotherAlwaysOnTopRootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        assertFalse(anotherAlwaysOnTopRootTask.isAlwaysOnTop());
+        assertEquals(topPosition - 2, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopRootTask));
+        anotherAlwaysOnTopRootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertTrue(anotherAlwaysOnTopRootTask.isAlwaysOnTop());
+        assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopRootTask));
+
+        final Task dreamRootTask = taskDisplayArea.createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM, true /* onTop */);
+        assertEquals(taskDisplayArea, dreamRootTask.getDisplayArea());
+        assertTrue(dreamRootTask.isAlwaysOnTop());
+        topPosition = taskDisplayArea.getRootTaskCount() - 1;
+        // Ensure dream shows above all activities, including PiP
+        assertEquals(dreamRootTask, taskDisplayArea.getTopRootTask());
+        assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, pinnedRootTask));
+
+        final Task assistRootTask = taskDisplayArea.createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
+        assertEquals(taskDisplayArea, assistRootTask.getDisplayArea());
+        assertFalse(assistRootTask.isAlwaysOnTop());
+        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(isAssistantOnTop ? topPosition : topPosition - 4,
+                getTaskIndexOf(taskDisplayArea, assistRootTask));
+    }
 }
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 ed57294..de4c40d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -403,8 +403,8 @@
     public void testOverridesDisplayAreaWithStandardTypeAndFullscreenMode() {
         final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(mDefaultDisplay,
                 mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
-        final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, secondaryDisplayArea);
+        final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD);
         launchRoot.mCreatedByOrganizer = true;
 
         secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FULLSCREEN },
@@ -419,8 +419,8 @@
     public void testOverridesDisplayAreaWithHomeTypeAndFullscreenMode() {
         final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(mDefaultDisplay,
                 mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
-        final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, secondaryDisplayArea);
+        final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD);
         launchRoot.mCreatedByOrganizer = true;
 
         mActivity.setActivityType(ACTIVITY_TYPE_HOME);
@@ -438,8 +438,8 @@
                 WINDOWING_MODE_FREEFORM);
         final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(freeformDisplay,
                 mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
-        final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, secondaryDisplayArea);
+        final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD);
         launchRoot.mCreatedByOrganizer = true;
 
         secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FREEFORM },
@@ -455,8 +455,8 @@
     public void testNotOverrideDisplayAreaWhenActivityOptionsHasDisplayArea() {
         final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(mDefaultDisplay,
                 mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
-        final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, secondaryDisplayArea);
+        final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD);
         launchRoot.mCreatedByOrganizer = true;
 
         secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FULLSCREEN },
@@ -481,8 +481,8 @@
                 mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
         secondaryDisplayArea.setBounds(DISPLAY_BOUNDS.width() / 2, 0,
                         DISPLAY_BOUNDS.width(), DISPLAY_BOUNDS.height());
-        final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, secondaryDisplayArea);
+        final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD);
         launchRoot.mCreatedByOrganizer = true;
         secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FREEFORM },
                 new int[] { ACTIVITY_TYPE_STANDARD });
@@ -512,8 +512,8 @@
                 mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
         secondaryDisplayArea.setBounds(DISPLAY_BOUNDS.width() / 2, 0,
                 DISPLAY_BOUNDS.width(), DISPLAY_BOUNDS.height());
-        final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, secondaryDisplayArea);
+        final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD);
         launchRoot.mCreatedByOrganizer = true;
         secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FREEFORM },
                 new int[] { ACTIVITY_TYPE_STANDARD });
@@ -1687,16 +1687,16 @@
     }
 
     private ActivityRecord createSourceActivity(TestDisplayContent display) {
-        final Task stack = display.getDefaultTaskDisplayArea()
+        final Task rootTask = display.getDefaultTaskDisplayArea()
                 .createRootTask(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true);
-        return new ActivityBuilder(mAtm).setTask(stack).build();
+        return new ActivityBuilder(mAtm).setTask(rootTask).build();
     }
 
     private void addFreeformTaskTo(TestDisplayContent display, Rect bounds) {
-        final Task stack = display.getDefaultTaskDisplayArea()
+        final Task rootTask = display.getDefaultTaskDisplayArea()
                 .createRootTask(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true);
-        stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
+        rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        final Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
         // Just work around the unnecessary adjustments for bounds.
         task.getWindowConfiguration().setBounds(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
deleted file mode 100644
index d853b93..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ /dev/null
@@ -1,1161 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
-import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.util.DisplayMetrics.DENSITY_DEFAULT;
-import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_90;
-import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.hamcrest.Matchers.not;
-import static org.hamcrest.Matchers.sameInstance;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.same;
-
-import android.app.ActivityManager;
-import android.app.TaskInfo;
-import android.app.WindowConfiguration;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-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;
-
-import androidx.test.filters.MediumTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.Reader;
-
-/**
- * Tests for exercising {@link Task}.
- *
- * Build/Install/Run:
- *  atest WmTests:TaskRecordTests
- */
-@MediumTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class TaskRecordTests extends WindowTestsBase {
-
-    private static final String TASK_TAG = "task";
-
-    private Rect mParentBounds;
-
-    @Before
-    public void setUp() throws Exception {
-        mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/);
-        removeGlobalMinSizeRestriction();
-    }
-
-    @Test
-    public void testRestoreWindowedTask() throws Exception {
-        final Task expected = createTask(64);
-        expected.mLastNonFullscreenBounds = new Rect(50, 50, 100, 100);
-
-        final byte[] serializedBytes = serializeToBytes(expected);
-        final Task actual = restoreFromBytes(serializedBytes);
-        assertEquals(expected.mTaskId, actual.mTaskId);
-        assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds);
-    }
-
-    /** Ensure we have no chance to modify the original intent. */
-    @Test
-    public void testCopyBaseIntentForTaskInfo() {
-        final Task task = createTask(1);
-        task.setTaskDescription(new ActivityManager.TaskDescription());
-        final TaskInfo info = task.getTaskInfo();
-
-        // The intent of info should be a copy so assert that they are different instances.
-        assertThat(info.baseIntent, not(sameInstance(task.getBaseIntent())));
-    }
-
-    @Test
-    public void testReturnsToHomeStack() throws Exception {
-        final Task task = createTask(1);
-        spyOn(task);
-        doReturn(true).when(task).hasChild();
-        assertFalse(task.returnsToHomeRootTask());
-        task.intent = null;
-        assertFalse(task.returnsToHomeRootTask());
-        task.intent = new Intent();
-        assertFalse(task.returnsToHomeRootTask());
-        task.intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME);
-        assertTrue(task.returnsToHomeRootTask());
-    }
-
-    /** Ensures that empty bounds cause appBounds to inherit from parent. */
-    @Test
-    public void testAppBounds_EmptyBounds() {
-        final Rect emptyBounds = new Rect();
-        testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, emptyBounds,
-                mParentBounds);
-    }
-
-    /** Ensures that bounds on freeform stacks are not clipped. */
-    @Test
-    public void testAppBounds_FreeFormBounds() {
-        final Rect freeFormBounds = new Rect(mParentBounds);
-        freeFormBounds.offset(10, 10);
-        testStackBoundsConfiguration(WINDOWING_MODE_FREEFORM, mParentBounds, freeFormBounds,
-                freeFormBounds);
-    }
-
-    /** Ensures that fully contained bounds are not clipped. */
-    @Test
-    public void testAppBounds_ContainedBounds() {
-        final Rect insetBounds = new Rect(mParentBounds);
-        insetBounds.inset(5, 5, 5, 5);
-        testStackBoundsConfiguration(
-                WINDOWING_MODE_FREEFORM, mParentBounds, insetBounds, insetBounds);
-    }
-
-    @Test
-    public void testFitWithinBounds() {
-        final Rect parentBounds = new Rect(10, 10, 200, 200);
-        TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
-        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();
-        parentConfig.windowConfiguration.setBounds(parentBounds);
-        parentConfig.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
-
-        // check top and left
-        Rect reqBounds = new Rect(-190, -190, 0, 0);
-        task.setBounds(reqBounds);
-        // Make sure part of it is exposed
-        assertTrue(task.getBounds().right > parentBounds.left);
-        assertTrue(task.getBounds().bottom > parentBounds.top);
-        // Should still be more-or-less in that corner
-        assertTrue(task.getBounds().left <= parentBounds.left);
-        assertTrue(task.getBounds().top <= parentBounds.top);
-
-        assertEquals(reqBounds.width(), task.getBounds().width());
-        assertEquals(reqBounds.height(), task.getBounds().height());
-
-        // check bottom and right
-        reqBounds = new Rect(210, 210, 400, 400);
-        task.setBounds(reqBounds);
-        // Make sure part of it is exposed
-        assertTrue(task.getBounds().left < parentBounds.right);
-        assertTrue(task.getBounds().top < parentBounds.bottom);
-        // Should still be more-or-less in that corner
-        assertTrue(task.getBounds().right >= parentBounds.right);
-        assertTrue(task.getBounds().bottom >= parentBounds.bottom);
-
-        assertEquals(reqBounds.width(), task.getBounds().width());
-        assertEquals(reqBounds.height(), task.getBounds().height());
-    }
-
-    /** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */
-    @Test
-    public void testBoundsOnModeChangeFreeformToFullscreen() {
-        DisplayContent display = mAtm.mRootWindowContainer.getDefaultDisplay();
-        Task stack = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true)
-                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
-        Task task = stack.getBottomMostTask();
-        task.getRootActivity().setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
-        DisplayInfo info = new DisplayInfo();
-        display.mDisplay.getDisplayInfo(info);
-        final Rect fullScreenBounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight);
-        final Rect freeformBounds = new Rect(fullScreenBounds);
-        freeformBounds.inset((int) (freeformBounds.width() * 0.2),
-                (int) (freeformBounds.height() * 0.2));
-        task.setBounds(freeformBounds);
-
-        assertEquals(freeformBounds, task.getBounds());
-
-        // FULLSCREEN inherits bounds
-        stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        assertEquals(fullScreenBounds, task.getBounds());
-        assertEquals(freeformBounds, task.mLastNonFullscreenBounds);
-
-        // FREEFORM restores bounds
-        stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        assertEquals(freeformBounds, task.getBounds());
-    }
-
-    /**
-     * Tests that a task with forced orientation has orientation-consistent bounds within the
-     * parent.
-     */
-    @Test
-    public void testFullscreenBoundsForcedOrientation() {
-        final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
-        final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
-        final DisplayContent display = new TestDisplayContent.Builder(mAtm,
-                fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build();
-        assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId));
-        // Fix the display orientation to landscape which is the natural rotation (0) for the test
-        // display.
-        final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
-        dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
-        dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
-
-        final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true)
-                .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
-        final Task task = stack.getBottomMostTask();
-        final ActivityRecord root = task.getTopNonFinishingActivity();
-
-        assertEquals(fullScreenBounds, task.getBounds());
-
-        // Setting app to fixed portrait fits within parent
-        root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
-        assertEquals(root, task.getRootActivity());
-        assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
-        // Portrait orientation is enforced on activity level. Task should fill fullscreen bounds.
-        assertThat(task.getBounds().height()).isLessThan(task.getBounds().width());
-        assertEquals(fullScreenBounds, task.getBounds());
-
-        // Top activity gets used
-        final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).setParentTask(stack)
-                .build();
-        assertEquals(top, task.getTopNonFinishingActivity());
-        top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-        assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
-        assertEquals(task.getBounds().width(), fullScreenBounds.width());
-
-        // Setting app to unspecified restores
-        top.setRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
-        assertEquals(fullScreenBounds, task.getBounds());
-
-        // Setting app to fixed landscape and changing display
-        top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-        // Fix the display orientation to portrait which is 90 degrees for the test display.
-        dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90);
-
-        // Fixed orientation request should be resolved on activity level. Task fills display
-        // bounds.
-        assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width());
-        assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height());
-        assertEquals(fullScreenBoundsPort, task.getBounds());
-
-        // in FREEFORM, no constraint
-        final Rect freeformBounds = new Rect(display.getBounds());
-        freeformBounds.inset((int) (freeformBounds.width() * 0.2),
-                (int) (freeformBounds.height() * 0.2));
-        stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        task.setBounds(freeformBounds);
-        assertEquals(freeformBounds, task.getBounds());
-
-        // FULLSCREEN letterboxes bounds on activity level, no constraint on task level.
-        stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width());
-        assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height());
-        assertEquals(fullScreenBoundsPort, task.getBounds());
-
-        // FREEFORM restores bounds as before
-        stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        assertEquals(freeformBounds, task.getBounds());
-    }
-
-    @Test
-    public void testReportsOrientationRequestInLetterboxForOrientation() {
-        final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
-        final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
-        final DisplayContent display = new TestDisplayContent.Builder(mAtm,
-                fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build();
-        assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId));
-        // Fix the display orientation to landscape which is the natural rotation (0) for the test
-        // display.
-        final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
-        dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
-        dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
-
-        final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true)
-                .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
-        final Task task = stack.getBottomMostTask();
-        ActivityRecord root = task.getTopNonFinishingActivity();
-
-        assertEquals(fullScreenBounds, task.getBounds());
-
-        // Setting app to fixed portrait fits within parent on activity level. Task fills parent.
-        root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
-        assertThat(root.getBounds().width()).isLessThan(root.getBounds().height());
-        assertEquals(task.getBounds(), fullScreenBounds);
-
-        assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getOrientation());
-    }
-
-    @Test
-    public void testIgnoresForcedOrientationWhenParentHandles() {
-        final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
-        DisplayContent display = new TestDisplayContent.Builder(
-                mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build();
-
-        display.getRequestedOverrideConfiguration().orientation =
-                Configuration.ORIENTATION_LANDSCAPE;
-        display.onRequestedOverrideConfigurationChanged(
-                display.getRequestedOverrideConfiguration());
-        Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true)
-                .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
-        Task task = stack.getBottomMostTask();
-        ActivityRecord root = task.getTopNonFinishingActivity();
-
-        final WindowContainer parentWindowContainer =
-                new WindowContainer(mSystemServicesTestRule.getWindowManagerService());
-        spyOn(parentWindowContainer);
-        parentWindowContainer.setBounds(fullScreenBounds);
-        doReturn(parentWindowContainer).when(task).getParent();
-        doReturn(display.getDefaultTaskDisplayArea()).when(task).getDisplayArea();
-        doReturn(stack).when(task).getRootTask();
-        doReturn(true).when(parentWindowContainer).handlesOrientationChangeFromDescendant();
-
-        // Setting app to fixed portrait fits within parent, but Task shouldn't adjust the
-        // bounds because its parent says it will handle it at a later time.
-        root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
-        assertEquals(root, task.getRootActivity());
-        assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
-        assertEquals(fullScreenBounds, task.getBounds());
-    }
-
-    @Test
-    public void testComputeConfigResourceOverrides() {
-        final Rect fullScreenBounds = new Rect(0, 0, 1080, 1920);
-        TestDisplayContent display = new TestDisplayContent.Builder(
-                mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build();
-        final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
-        final Configuration inOutConfig = new Configuration();
-        final Configuration parentConfig = new Configuration();
-        final int longSide = 1200;
-        final int shortSide = 600;
-        final Rect parentBounds = new Rect(0, 0, 250, 500);
-        final Rect parentAppBounds = new Rect(0, 0, 250, 480);
-        parentConfig.windowConfiguration.setBounds(parentBounds);
-        parentConfig.windowConfiguration.setAppBounds(parentAppBounds);
-        parentConfig.densityDpi = 400;
-        parentConfig.screenHeightDp = (parentBounds.bottom * 160) / parentConfig.densityDpi; // 200
-        parentConfig.screenWidthDp = (parentBounds.right * 160) / parentConfig.densityDpi; // 100
-        parentConfig.windowConfiguration.setRotation(ROTATION_0);
-
-        // By default, the input bounds will fill parent.
-        task.computeConfigResourceOverrides(inOutConfig, parentConfig);
-
-        assertEquals(parentConfig.screenHeightDp, inOutConfig.screenHeightDp);
-        assertEquals(parentConfig.screenWidthDp, inOutConfig.screenWidthDp);
-        assertEquals(parentAppBounds, inOutConfig.windowConfiguration.getAppBounds());
-        assertEquals(Configuration.ORIENTATION_PORTRAIT, inOutConfig.orientation);
-
-        // If bounds are overridden, config properties should be made to match. Surface hierarchy
-        // will crop for policy.
-        inOutConfig.setToDefaults();
-        final Rect largerPortraitBounds = new Rect(0, 0, shortSide, longSide);
-        inOutConfig.windowConfiguration.setBounds(largerPortraitBounds);
-        task.computeConfigResourceOverrides(inOutConfig, parentConfig);
-        // The override bounds are beyond the parent, the out appBounds should not be intersected
-        // by parent appBounds.
-        assertEquals(largerPortraitBounds, inOutConfig.windowConfiguration.getAppBounds());
-        assertEquals(longSide, inOutConfig.screenHeightDp * parentConfig.densityDpi / 160);
-        assertEquals(shortSide, inOutConfig.screenWidthDp * parentConfig.densityDpi / 160);
-
-        inOutConfig.setToDefaults();
-        // Landscape bounds.
-        final Rect largerLandscapeBounds = new Rect(0, 0, longSide, shortSide);
-        inOutConfig.windowConfiguration.setBounds(largerLandscapeBounds);
-
-        // Setup the display with a top stable inset. The later assertion will ensure the inset is
-        // excluded from screenHeightDp.
-        final int statusBarHeight = 100;
-        final DisplayPolicy policy = display.getDisplayPolicy();
-        doAnswer(invocationOnMock -> {
-            final Rect insets = invocationOnMock.<Rect>getArgument(0);
-            insets.top = statusBarHeight;
-            return null;
-        }).when(policy).convertNonDecorInsetsToStableInsets(any(), eq(ROTATION_0));
-
-        // Without limiting to be inside the parent bounds, the out screen size should keep relative
-        // to the input bounds.
-        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
-        final ActivityRecord.CompatDisplayInsets compatIntsets =
-                new ActivityRecord.CompatDisplayInsets(
-                        display, activity, /* fixedOrientationBounds= */ null);
-        task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatIntsets);
-
-        assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds());
-        assertEquals((shortSide - statusBarHeight) * DENSITY_DEFAULT / parentConfig.densityDpi,
-                inOutConfig.screenHeightDp);
-        assertEquals(longSide * DENSITY_DEFAULT / parentConfig.densityDpi,
-                inOutConfig.screenWidthDp);
-        assertEquals(Configuration.ORIENTATION_LANDSCAPE, inOutConfig.orientation);
-    }
-
-    @Test
-    public void testComputeConfigResourceLayoutOverrides() {
-        final Rect fullScreenBounds = new Rect(0, 0, 1000, 2500);
-        TestDisplayContent display = new TestDisplayContent.Builder(
-                mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build();
-        final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
-        final Configuration inOutConfig = new Configuration();
-        final Configuration parentConfig = new Configuration();
-        final Rect nonLongBounds = new Rect(0, 0, 1000, 1250);
-        parentConfig.windowConfiguration.setBounds(fullScreenBounds);
-        parentConfig.windowConfiguration.setAppBounds(fullScreenBounds);
-        parentConfig.densityDpi = 400;
-        parentConfig.screenHeightDp = (fullScreenBounds.bottom * 160) / parentConfig.densityDpi;
-        parentConfig.screenWidthDp = (fullScreenBounds.right * 160) / parentConfig.densityDpi;
-        parentConfig.windowConfiguration.setRotation(ROTATION_0);
-
-        // Set BOTH screenW/H to an override value
-        inOutConfig.screenWidthDp = nonLongBounds.width() * 160 / parentConfig.densityDpi;
-        inOutConfig.screenHeightDp = nonLongBounds.height() * 160 / parentConfig.densityDpi;
-        task.computeConfigResourceOverrides(inOutConfig, parentConfig);
-
-        // screenLayout should honor override when both screenW/H are set.
-        assertTrue((inOutConfig.screenLayout & Configuration.SCREENLAYOUT_LONG_NO) != 0);
-    }
-
-    @Test
-    public void testComputeNestedConfigResourceOverrides() {
-        final Task task = new TaskBuilder(mSupervisor).build();
-        assertTrue(task.getResolvedOverrideBounds().isEmpty());
-        int origScreenH = task.getConfiguration().screenHeightDp;
-        Configuration stackConfig = new Configuration();
-        stackConfig.setTo(task.getRootTask().getRequestedOverrideConfiguration());
-        stackConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
-
-        // Set bounds on stack (not task) and verify that the task resource configuration changes
-        // despite it's override bounds being empty.
-        Rect bounds = new Rect(task.getRootTask().getBounds());
-        bounds.bottom = (int) (bounds.bottom * 0.6f);
-        stackConfig.windowConfiguration.setBounds(bounds);
-        task.getRootTask().onRequestedOverrideConfigurationChanged(stackConfig);
-        assertNotEquals(origScreenH, task.getConfiguration().screenHeightDp);
-    }
-
-    @Test
-    public void testFullScreenTaskNotAdjustedByMinimalSize() {
-        final Task fullscreenTask = new TaskBuilder(mSupervisor).build();
-        final Rect originalTaskBounds = new Rect(fullscreenTask.getBounds());
-        final ActivityInfo aInfo = new ActivityInfo();
-        aInfo.windowLayout = new ActivityInfo.WindowLayout(0 /* width */, 0 /* widthFraction */,
-                    0 /* height */, 0 /* heightFraction */, 0 /* gravity */,
-                    originalTaskBounds.width() * 2 /* minWidth */,
-                    originalTaskBounds.height() * 2 /* minHeight */);
-        fullscreenTask.setMinDimensions(aInfo);
-        fullscreenTask.onConfigurationChanged(fullscreenTask.getParent().getConfiguration());
-
-        assertEquals(originalTaskBounds, fullscreenTask.getBounds());
-    }
-
-    @Test
-    public void testInsetDisregardedWhenFreeformOverlapsNavBar() {
-        TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
-        Task stack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        DisplayInfo displayInfo = new DisplayInfo();
-        mAtm.mContext.getDisplay().getDisplayInfo(displayInfo);
-        final int displayHeight = displayInfo.logicalHeight;
-        final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
-        final Configuration inOutConfig = new Configuration();
-        final Configuration parentConfig = new Configuration();
-        final int longSide = 1200;
-        final int shortSide = 600;
-        parentConfig.densityDpi = 400;
-        parentConfig.screenHeightDp = 200; // 200 * 400 / 160 = 500px
-        parentConfig.screenWidthDp = 100; // 100 * 400 / 160 = 250px
-        parentConfig.windowConfiguration.setRotation(ROTATION_0);
-
-        final int longSideDp = 480; // longSide / density = 1200 / 400 * 160
-        final int shortSideDp = 240; // shortSide / density = 600 / 400 * 160
-        final int screenLayout = parentConfig.screenLayout
-                & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
-        final int reducedScreenLayout =
-                Configuration.reduceScreenLayout(screenLayout, longSideDp, shortSideDp);
-
-        // Portrait bounds overlapping with navigation bar, without insets.
-        final Rect freeformBounds = new Rect(0,
-                displayHeight - 10 - longSide,
-                shortSide,
-                displayHeight - 10);
-        inOutConfig.windowConfiguration.setBounds(freeformBounds);
-        // Set to freeform mode to verify bug fix.
-        inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
-
-        task.computeConfigResourceOverrides(inOutConfig, parentConfig);
-
-        // screenW/H should not be effected by parent since overridden and freeform
-        assertEquals(freeformBounds.width() * 160 / parentConfig.densityDpi,
-                inOutConfig.screenWidthDp);
-        assertEquals(freeformBounds.height() * 160 / parentConfig.densityDpi,
-                inOutConfig.screenHeightDp);
-        assertEquals(reducedScreenLayout, inOutConfig.screenLayout);
-
-        inOutConfig.setToDefaults();
-        // Landscape bounds overlapping with navigtion bar, without insets.
-        freeformBounds.set(0,
-                displayHeight - 10 - shortSide,
-                longSide,
-                displayHeight - 10);
-        inOutConfig.windowConfiguration.setBounds(freeformBounds);
-        inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
-
-        task.computeConfigResourceOverrides(inOutConfig, parentConfig);
-
-        assertEquals(freeformBounds.width() * 160 / parentConfig.densityDpi,
-                inOutConfig.screenWidthDp);
-        assertEquals(freeformBounds.height() * 160 / parentConfig.densityDpi,
-                inOutConfig.screenHeightDp);
-        assertEquals(reducedScreenLayout, inOutConfig.screenLayout);
-    }
-
-    /** Ensures that the alias intent won't have target component resolved. */
-    @Test
-    public void testTaskIntentActivityAlias() {
-        final String aliasClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".aliasActivity";
-        final String targetClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".targetActivity";
-        final ComponentName aliasComponent =
-                new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasClassName);
-        final ComponentName targetComponent =
-                new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, targetClassName);
-
-        final Intent intent = new Intent();
-        intent.setComponent(aliasComponent);
-        final ActivityInfo info = new ActivityInfo();
-        info.applicationInfo = new ApplicationInfo();
-        info.packageName = DEFAULT_COMPONENT_PACKAGE_NAME;
-        info.targetActivity = targetClassName;
-
-        final Task task = new Task.Builder(mAtm)
-                .setTaskId(1)
-                .setActivityInfo(info)
-                .setIntent(intent)
-                .build();
-        assertEquals("The alias activity component should be saved in task intent.", aliasClassName,
-                task.intent.getComponent().getClassName());
-
-        ActivityRecord aliasActivity = new ActivityBuilder(mAtm).setComponent(
-                aliasComponent).setTargetActivity(targetClassName).build();
-        assertEquals("Should be the same intent filter.", true,
-                task.isSameIntentFilter(aliasActivity));
-
-        ActivityRecord targetActivity = new ActivityBuilder(mAtm).setComponent(
-                targetComponent).build();
-        assertEquals("Should be the same intent filter.", true,
-                task.isSameIntentFilter(targetActivity));
-
-        ActivityRecord defaultActivity = new ActivityBuilder(mAtm).build();
-        assertEquals("Should not be the same intent filter.", false,
-                task.isSameIntentFilter(defaultActivity));
-    }
-
-    /** Test that root activity index is reported correctly for several activities in the task. */
-    @Test
-    public void testFindRootIndex() {
-        final Task task = getTestTask();
-        // Add an extra activity on top of the root one
-        new ActivityBuilder(mAtm).setTask(task).build();
-
-        assertEquals("The root activity in the task must be reported.", task.getChildAt(0),
-                task.getRootActivity(
-                        true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
-    }
-
-    /**
-     * Test that root activity index is reported correctly for several activities in the task when
-     * the activities on the bottom are finishing.
-     */
-    @Test
-    public void testFindRootIndex_finishing() {
-        final Task task = getTestTask();
-        // Add extra two activities and mark the two on the bottom as finishing.
-        final ActivityRecord activity0 = task.getBottomMostActivity();
-        activity0.finishing = true;
-        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
-        activity1.finishing = true;
-        new ActivityBuilder(mAtm).setTask(task).build();
-
-        assertEquals("The first non-finishing activity in the task must be reported.",
-                task.getChildAt(2), task.getRootActivity(
-                        true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
-    }
-
-    /**
-     * Test that root activity index is reported correctly for several activities in the task when
-     * looking for the 'effective root'.
-     */
-    @Test
-    public void testFindRootIndex_effectiveRoot() {
-        final Task task = getTestTask();
-        // Add an extra activity on top of the root one
-        new ActivityBuilder(mAtm).setTask(task).build();
-
-        assertEquals("The root activity in the task must be reported.",
-                task.getChildAt(0), task.getRootActivity(
-                        false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
-    }
-
-    /**
-     * Test that root activity index is reported correctly when looking for the 'effective root' in
-     * case when bottom activities are relinquishing task identity or finishing.
-     */
-    @Test
-    public void testFindRootIndex_effectiveRoot_finishingAndRelinquishing() {
-        final Task task = getTestTask();
-        // Add extra two activities. Mark the one on the bottom with "relinquishTaskIdentity" and
-        // one above as finishing.
-        final ActivityRecord activity0 = task.getBottomMostActivity();
-        activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
-        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
-        activity1.finishing = true;
-        new ActivityBuilder(mAtm).setTask(task).build();
-
-        assertEquals("The first non-finishing activity and non-relinquishing task identity "
-                + "must be reported.", task.getChildAt(2), task.getRootActivity(
-                        false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
-    }
-
-    /**
-     * Test that root activity index is reported correctly when looking for the 'effective root'
-     * for the case when there is only a single activity that also has relinquishTaskIdentity set.
-     */
-    @Test
-    public void testFindRootIndex_effectiveRoot_relinquishingAndSingleActivity() {
-        final Task task = getTestTask();
-        // Set relinquishTaskIdentity for the only activity in the task
-        task.getBottomMostActivity().info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
-
-        assertEquals("The root activity in the task must be reported.",
-                task.getChildAt(0), task.getRootActivity(
-                        false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
-    }
-
-    /**
-     * Test that the topmost activity index is reported correctly when looking for the
-     * 'effective root' for the case when all activities have relinquishTaskIdentity set.
-     */
-    @Test
-    public void testFindRootIndex_effectiveRoot_relinquishingMultipleActivities() {
-        final Task task = getTestTask();
-        // Set relinquishTaskIdentity for all activities in the task
-        final ActivityRecord activity0 = task.getBottomMostActivity();
-        activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
-        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
-        activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
-
-        assertEquals("The topmost activity in the task must be reported.",
-                task.getChildAt(task.getChildCount() - 1), task.getRootActivity(
-                        false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
-    }
-
-    /** Test that bottom-most activity is reported in {@link Task#getRootActivity()}. */
-    @Test
-    public void testGetRootActivity() {
-        final Task task = getTestTask();
-        // Add an extra activity on top of the root one
-        new ActivityBuilder(mAtm).setTask(task).build();
-
-        assertEquals("The root activity in the task must be reported.",
-                task.getBottomMostActivity(), task.getRootActivity());
-    }
-
-    /**
-     * Test that first non-finishing activity is reported in {@link Task#getRootActivity()}.
-     */
-    @Test
-    public void testGetRootActivity_finishing() {
-        final Task task = getTestTask();
-        // Add an extra activity on top of the root one
-        new ActivityBuilder(mAtm).setTask(task).build();
-        // Mark the root as finishing
-        task.getBottomMostActivity().finishing = true;
-
-        assertEquals("The first non-finishing activity in the task must be reported.",
-                task.getChildAt(1), task.getRootActivity());
-    }
-
-    /**
-     * Test that relinquishTaskIdentity flag is ignored in {@link Task#getRootActivity()}.
-     */
-    @Test
-    public void testGetRootActivity_relinquishTaskIdentity() {
-        final Task task = getTestTask();
-        // Mark the bottom-most activity with FLAG_RELINQUISH_TASK_IDENTITY.
-        final ActivityRecord activity0 = task.getBottomMostActivity();
-        activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
-        // Add an extra activity on top of the root one.
-        new ActivityBuilder(mAtm).setTask(task).build();
-
-        assertEquals("The root activity in the task must be reported.",
-                task.getBottomMostActivity(), task.getRootActivity());
-    }
-
-    /**
-     * Test that no activity is reported in {@link Task#getRootActivity()} when all activities
-     * in the task are finishing.
-     */
-    @Test
-    public void testGetRootActivity_allFinishing() {
-        final Task task = getTestTask();
-        // Mark the bottom-most activity as finishing.
-        final ActivityRecord activity0 = task.getBottomMostActivity();
-        activity0.finishing = true;
-        // Add an extra activity on top of the root one and mark it as finishing
-        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
-        activity1.finishing = true;
-
-        assertNull("No activity must be reported if all are finishing", task.getRootActivity());
-    }
-
-    /**
-     * Test that first non-finishing activity is the root of task.
-     */
-    @Test
-    public void testIsRootActivity() {
-        final Task task = getTestTask();
-        // Mark the bottom-most activity as finishing.
-        final ActivityRecord activity0 = task.getBottomMostActivity();
-        activity0.finishing = true;
-        // Add an extra activity on top of the root one.
-        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
-
-        assertFalse("Finishing activity must not be the root of task", activity0.isRootOfTask());
-        assertTrue("Non-finishing activity must be the root of task", activity1.isRootOfTask());
-    }
-
-    /**
-     * Test that if all activities in the task are finishing, then the one on the bottom is the
-     * root of task.
-     */
-    @Test
-    public void testIsRootActivity_allFinishing() {
-        final Task task = getTestTask();
-        // Mark the bottom-most activity as finishing.
-        final ActivityRecord activity0 = task.getBottomMostActivity();
-        activity0.finishing = true;
-        // Add an extra activity on top of the root one and mark it as finishing
-        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
-        activity1.finishing = true;
-
-        assertTrue("Bottom activity must be the root of task", activity0.isRootOfTask());
-        assertFalse("Finishing activity on top must not be the root of task",
-                activity1.isRootOfTask());
-    }
-
-    /**
-     * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)}.
-     */
-    @Test
-    public void testGetTaskForActivity() {
-        final Task task0 = getTestTask();
-        final ActivityRecord activity0 = task0.getBottomMostActivity();
-
-        final Task task1 = getTestTask();
-        final ActivityRecord activity1 = task1.getBottomMostActivity();
-
-        assertEquals(task0.mTaskId,
-                ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */));
-        assertEquals(task1.mTaskId,
-                ActivityRecord.getTaskForActivityLocked(activity1.appToken,  false /* onlyRoot */));
-    }
-
-    /**
-     * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with finishing
-     * activity.
-     */
-    @Test
-    public void testGetTaskForActivity_onlyRoot_finishing() {
-        final Task task = getTestTask();
-        // Make the current root activity finishing
-        final ActivityRecord activity0 = task.getBottomMostActivity();
-        activity0.finishing = true;
-        // Add an extra activity on top - this will be the new root
-        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
-        // Add one more on top
-        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
-
-        assertEquals(task.mTaskId,
-                ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */));
-        assertEquals(task.mTaskId,
-                ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */));
-        assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
-                ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */));
-    }
-
-    /**
-     * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with activity that
-     * relinquishes task identity.
-     */
-    @Test
-    public void testGetTaskForActivity_onlyRoot_relinquishTaskIdentity() {
-        final Task task = getTestTask();
-        // Make the current root activity relinquish task identity
-        final ActivityRecord activity0 = task.getBottomMostActivity();
-        activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
-        // Add an extra activity on top - this will be the new root
-        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
-        // Add one more on top
-        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
-
-        assertEquals(task.mTaskId,
-                ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */));
-        assertEquals(task.mTaskId,
-                ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */));
-        assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
-                ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */));
-    }
-
-    /**
-     * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} allowing non-root
-     * entries.
-     */
-    @Test
-    public void testGetTaskForActivity_notOnlyRoot() {
-        final Task task = getTestTask();
-        // Mark the bottom-most activity as finishing.
-        final ActivityRecord activity0 = task.getBottomMostActivity();
-        activity0.finishing = true;
-
-        // Add an extra activity on top of the root one and make it relinquish task identity
-        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
-        activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
-
-        // Add one more activity on top
-        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
-
-        assertEquals(task.mTaskId,
-                ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */));
-        assertEquals(task.mTaskId,
-                ActivityRecord.getTaskForActivityLocked(activity1.appToken, false /* onlyRoot */));
-        assertEquals(task.mTaskId,
-                ActivityRecord.getTaskForActivityLocked(activity2.appToken, false /* onlyRoot */));
-    }
-
-    /**
-     * Test {@link Task#updateEffectiveIntent()}.
-     */
-    @Test
-    public void testUpdateEffectiveIntent() {
-        // Test simple case with a single activity.
-        final Task task = getTestTask();
-        final ActivityRecord activity0 = task.getBottomMostActivity();
-
-        spyOn(task);
-        task.updateEffectiveIntent();
-        verify(task).setIntent(eq(activity0));
-    }
-
-    /**
-     * Test {@link Task#updateEffectiveIntent()} with root activity marked as finishing. This
-     * should make the task use the second activity when updating the intent.
-     */
-    @Test
-    public void testUpdateEffectiveIntent_rootFinishing() {
-        // Test simple case with a single activity.
-        final Task task = getTestTask();
-        final ActivityRecord activity0 = task.getBottomMostActivity();
-        // Mark the bottom-most activity as finishing.
-        activity0.finishing = true;
-        // Add an extra activity on top of the root one
-        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
-
-        spyOn(task);
-        task.updateEffectiveIntent();
-        verify(task).setIntent(eq(activity1));
-    }
-
-    /**
-     * Test {@link Task#updateEffectiveIntent()} when all activities are finishing or
-     * relinquishing task identity. In this case the root activity should still be used when
-     * updating the intent (legacy behavior).
-     */
-    @Test
-    public void testUpdateEffectiveIntent_allFinishing() {
-        // Test simple case with a single activity.
-        final Task task = getTestTask();
-        final ActivityRecord activity0 = task.getBottomMostActivity();
-        // Mark the bottom-most activity as finishing.
-        activity0.finishing = true;
-        // Add an extra activity on top of the root one and make it relinquish task identity
-        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
-        activity1.finishing = true;
-
-        // Task must still update the intent using the root activity (preserving legacy behavior).
-        spyOn(task);
-        task.updateEffectiveIntent();
-        verify(task).setIntent(eq(activity0));
-    }
-
-    @Test
-    public void testSaveLaunchingStateWhenConfigurationChanged() {
-        LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
-        spyOn(persister);
-
-        final Task task = getTestTask();
-        task.setHasBeenVisible(false);
-        task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
-        task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-
-        task.setHasBeenVisible(true);
-        task.onConfigurationChanged(task.getParent().getConfiguration());
-
-        verify(persister).saveTask(task, task.getDisplayContent());
-    }
-
-    @Test
-    public void testSaveLaunchingStateWhenClearingParent() {
-        LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
-        spyOn(persister);
-
-        final Task task = getTestTask();
-        task.setHasBeenVisible(false);
-        task.getDisplayContent().setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
-        task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        final DisplayContent oldDisplay = task.getDisplayContent();
-
-        LaunchParamsController.LaunchParams params = new LaunchParamsController.LaunchParams();
-        params.mWindowingMode = WINDOWING_MODE_UNDEFINED;
-        persister.getLaunchParams(task, null, params);
-        assertEquals(WINDOWING_MODE_UNDEFINED, params.mWindowingMode);
-
-        task.setHasBeenVisible(true);
-        task.removeImmediately();
-
-        verify(persister).saveTask(task, oldDisplay);
-
-        persister.getLaunchParams(task, null, params);
-        assertEquals(WINDOWING_MODE_FULLSCREEN, params.mWindowingMode);
-    }
-
-    @Test
-    public void testNotSaveLaunchingStateNonFreeformDisplay() {
-        LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
-        spyOn(persister);
-
-        final Task task = getTestTask();
-        task.setHasBeenVisible(false);
-        task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-
-        task.setHasBeenVisible(true);
-        task.onConfigurationChanged(task.getParent().getConfiguration());
-
-        verify(persister, never()).saveTask(same(task), any());
-    }
-
-    @Test
-    public void testNotSaveLaunchingStateWhenNotFullscreenOrFreeformWindow() {
-        LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
-        spyOn(persister);
-
-        final Task task = getTestTask();
-        task.setHasBeenVisible(false);
-        task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
-        task.getRootTask().setWindowingMode(WINDOWING_MODE_PINNED);
-
-        task.setHasBeenVisible(true);
-        task.onConfigurationChanged(task.getParent().getConfiguration());
-
-        verify(persister, never()).saveTask(same(task), any());
-    }
-
-    @Test
-    public void testNotSaveLaunchingStateForNonLeafTask() {
-        LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
-        spyOn(persister);
-
-        final Task task = getTestTask();
-        task.setHasBeenVisible(false);
-        task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
-        task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-
-        final Task leafTask = createTaskInStack(task, 0 /* userId */);
-
-        leafTask.setHasBeenVisible(true);
-        task.setHasBeenVisible(true);
-        task.onConfigurationChanged(task.getParent().getConfiguration());
-
-        verify(persister, never()).saveTask(same(task), any());
-        verify(persister).saveTask(same(leafTask), any());
-    }
-
-    @Test
-    public void testNotSpecifyOrientationByFloatingTask() {
-        final Task task = new TaskBuilder(mSupervisor)
-                .setCreateActivity(true).setCreateParentTask(true).build();
-        final ActivityRecord activity = task.getTopMostActivity();
-        final WindowContainer<?> parentContainer = task.getParent();
-        final TaskDisplayArea taskDisplayArea = task.getDisplayArea();
-        activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, parentContainer.getOrientation());
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation());
-
-        task.setWindowingMode(WINDOWING_MODE_PINNED);
-
-        // TDA returns the last orientation when child returns UNSET
-        assertEquals(SCREEN_ORIENTATION_UNSET, parentContainer.getOrientation());
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation());
-    }
-
-    @Test
-    public void testNotSpecifyOrientation_taskDisplayAreaNotFocused() {
-        final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
-        final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
-                mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
-                FEATURE_VENDOR_FIRST);
-        final Task firstStack = firstTaskDisplayArea.createRootTask(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final Task secondStack = secondTaskDisplayArea.createRootTask(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
-                .setTask(firstStack).build();
-        final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
-                .setTask(secondStack).build();
-        firstActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-        secondActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
-
-        // Activity on TDA1 is focused
-        mDisplayContent.setFocusedApp(firstActivity);
-
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation());
-        assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation());
-
-        // No focused app, TDA1 is still recorded as last focused.
-        mDisplayContent.setFocusedApp(null);
-
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation());
-        assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation());
-
-        // Activity on TDA2 is focused
-        mDisplayContent.setFocusedApp(secondActivity);
-
-        assertEquals(SCREEN_ORIENTATION_UNSET, firstTaskDisplayArea.getOrientation());
-        assertEquals(SCREEN_ORIENTATION_PORTRAIT, secondTaskDisplayArea.getOrientation());
-    }
-
-    @Test
-    public void testNotifyOrientationChangeCausedByConfigurationChange() {
-        final Task task = getTestTask();
-        final ActivityRecord activity = task.getTopMostActivity();
-        final DisplayContent display = task.getDisplayContent();
-        display.setWindowingMode(WINDOWING_MODE_FREEFORM);
-
-        activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-        assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation());
-        verify(display).onDescendantOrientationChanged(same(task));
-        reset(display);
-
-        display.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, task.getOrientation());
-        verify(display).onDescendantOrientationChanged(same(task));
-    }
-
-    private Task getTestTask() {
-        final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
-        return stack.getBottomMostTask();
-    }
-
-    private void testStackBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds,
-            Rect expectedConfigBounds) {
-
-        TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
-        Task stack = taskDisplayArea.createRootTask(windowingMode, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
-
-        final Configuration parentConfig = stack.getConfiguration();
-        parentConfig.windowConfiguration.setAppBounds(parentBounds);
-        task.setBounds(bounds);
-
-        task.resolveOverrideConfiguration(parentConfig);
-        // Assert that both expected and actual are null or are equal to each other
-        assertEquals(expectedConfigBounds,
-                task.getResolvedOverrideConfiguration().windowConfiguration.getAppBounds());
-    }
-
-    private byte[] serializeToBytes(Task r) throws Exception {
-        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
-            final TypedXmlSerializer serializer = Xml.newFastSerializer();
-            serializer.setOutput(os, "UTF-8");
-            serializer.startDocument(null, true);
-            serializer.startTag(null, TASK_TAG);
-            r.saveToXml(serializer);
-            serializer.endTag(null, TASK_TAG);
-            serializer.endDocument();
-
-            os.flush();
-            return os.toByteArray();
-        }
-    }
-
-    private Task restoreFromBytes(byte[] in) throws IOException, XmlPullParserException {
-        try (Reader reader = new InputStreamReader(new ByteArrayInputStream(in))) {
-            final TypedXmlPullParser parser = Xml.newFastPullParser();
-            parser.setInput(reader);
-            assertEquals(XmlPullParser.START_TAG, parser.next());
-            assertEquals(TASK_TAG, parser.getName());
-            return Task.restoreFromXml(parser, mAtm.mTaskSupervisor);
-        }
-    }
-
-    private Task createTask(int taskId) {
-        return new Task.Builder(mAtm)
-                .setTaskId(taskId)
-                .setIntent(new Intent())
-                .setRealActivity(ActivityBuilder.getDefaultComponent())
-                .setEffectiveUid(10050)
-                .buildInner();
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
deleted file mode 100644
index e58c162..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
-import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.clearInvocations;
-
-import android.app.WindowConfiguration;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link Task} class.
- *
- * Build/Install/Run:
- *  atest WmTests:TaskStackTests
- */
-@SmallTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class TaskStackTests extends WindowTestsBase {
-
-    @Test
-    public void testStackPositionChildAt() {
-        final Task stack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task1 = createTaskInStack(stack, 0 /* userId */);
-        final Task task2 = createTaskInStack(stack, 1 /* userId */);
-
-        // Current user task should be moved to top.
-        stack.positionChildAt(WindowContainer.POSITION_TOP, task1, false /* includingParents */);
-        assertEquals(stack.mChildren.get(0), task2);
-        assertEquals(stack.mChildren.get(1), task1);
-
-        // Non-current user won't be moved to top.
-        stack.positionChildAt(WindowContainer.POSITION_TOP, task2, false /* includingParents */);
-        assertEquals(stack.mChildren.get(0), task2);
-        assertEquals(stack.mChildren.get(1), task1);
-
-        // Non-leaf task should be moved to top regardless of the user id.
-        createTaskInStack(task2, 0 /* userId */);
-        createTaskInStack(task2, 1 /* userId */);
-        stack.positionChildAt(WindowContainer.POSITION_TOP, task2, false /* includingParents */);
-        assertEquals(stack.mChildren.get(0), task1);
-        assertEquals(stack.mChildren.get(1), task2);
-    }
-
-    @Test
-    public void testClosingAppDifferentTaskOrientation() {
-        final ActivityRecord activity1 = createActivityRecord(mDisplayContent);
-        activity1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-
-        final ActivityRecord activity2 = createActivityRecord(mDisplayContent);
-        activity2.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
-
-        final WindowContainer parent = activity1.getTask().getParent();
-        assertEquals(SCREEN_ORIENTATION_PORTRAIT, parent.getOrientation());
-        mDisplayContent.mClosingApps.add(activity2);
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, parent.getOrientation());
-    }
-
-    @Test
-    public void testMoveTaskToBackDifferentTaskOrientation() {
-        final ActivityRecord activity1 = createActivityRecord(mDisplayContent);
-        activity1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-
-        final ActivityRecord activity2 = createActivityRecord(mDisplayContent);
-        activity2.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
-
-        final WindowContainer parent = activity1.getTask().getParent();
-        assertEquals(SCREEN_ORIENTATION_PORTRAIT, parent.getOrientation());
-    }
-
-    @Test
-    public void testStackRemoveImmediately() {
-        final Task stack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
-        assertEquals(stack, task.getRootTask());
-
-        // Remove stack and check if its child is also removed.
-        stack.removeImmediately();
-        assertNull(stack.getDisplayContent());
-        assertNull(task.getParent());
-    }
-
-    @Test
-    public void testRemoveContainer() {
-        final Task stack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
-
-        assertNotNull(stack);
-        assertNotNull(task);
-        stack.removeIfPossible();
-        // Assert that the container was removed.
-        assertNull(stack.getParent());
-        assertEquals(0, stack.getChildCount());
-        assertNull(stack.getDisplayContent());
-        assertNull(task.getDisplayContent());
-        assertNull(task.getParent());
-    }
-
-    @Test
-    public void testRemoveContainer_deferRemoval() {
-        final Task stack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
-
-        // Stack removal is deferred if one of its child is animating.
-        doReturn(true).when(stack).hasWindowsAlive();
-        doReturn(stack).when(task).getAnimatingContainer(
-                eq(TRANSITION | CHILDREN), anyInt());
-
-        stack.removeIfPossible();
-        // For the case of deferred removal the task controller will still be connected to the its
-        // container until the stack window container is removed.
-        assertNotNull(stack.getParent());
-        assertNotEquals(0, stack.getChildCount());
-        assertNotNull(task);
-
-        stack.removeImmediately();
-        // After removing, the task will be isolated.
-        assertNull(task.getParent());
-        assertEquals(0, task.getChildCount());
-    }
-
-    @Test
-    public void testReparent() {
-        // Create first stack on primary display.
-        final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
-        final Task task1 = createTaskInStack(stack1, 0 /* userId */);
-
-        // Create second display and put second stack on it.
-        final DisplayContent dc = createNewDisplay();
-        final Task stack2 = createTaskStackOnDisplay(dc);
-
-        // Reparent
-        clearInvocations(task1); // reset the number of onDisplayChanged for task.
-        stack1.reparent(dc.getDefaultTaskDisplayArea(), true /* onTop */);
-        assertEquals(dc, stack1.getDisplayContent());
-        final int stack1PositionInParent = stack1.getParent().mChildren.indexOf(stack1);
-        final int stack2PositionInParent = stack1.getParent().mChildren.indexOf(stack2);
-        assertEquals(stack1PositionInParent, stack2PositionInParent + 1);
-        verify(task1, times(1)).onDisplayChanged(any());
-    }
-
-    @Test
-    public void testStackOutset() {
-        final Task stack = createTaskStackOnDisplay(mDisplayContent);
-        final int stackOutset = 10;
-        spyOn(stack);
-        doReturn(stackOutset).when(stack).getTaskOutset();
-        doReturn(true).when(stack).inMultiWindowMode();
-
-        // Mock the resolved override windowing mode to non-fullscreen
-        final WindowConfiguration windowConfiguration =
-                stack.getResolvedOverrideConfiguration().windowConfiguration;
-        spyOn(windowConfiguration);
-        doReturn(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)
-                .when(windowConfiguration).getWindowingMode();
-
-        // Prevent adjust task dimensions
-        doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any(), any());
-
-        final Rect stackBounds = new Rect(200, 200, 800, 1000);
-        // Update surface position and size by the given bounds.
-        stack.setBounds(stackBounds);
-
-        assertEquals(stackBounds.width() + 2 * stackOutset, stack.getLastSurfaceSize().x);
-        assertEquals(stackBounds.height() + 2 * stackOutset, stack.getLastSurfaceSize().y);
-        assertEquals(stackBounds.left - stackOutset, stack.getLastSurfacePosition().x);
-        assertEquals(stackBounds.top - stackOutset, stack.getLastSurfacePosition().y);
-    }
-
-    @Test
-    public void testActivityAndTaskGetsProperType() {
-        final Task task1 = new TaskBuilder(mSupervisor).build();
-        ActivityRecord activity1 = createNonAttachedActivityRecord(mDisplayContent);
-
-        // First activity should become standard
-        task1.addChild(activity1, 0);
-        assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, activity1.getActivityType());
-        assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, task1.getActivityType());
-
-        // Second activity should also become standard
-        ActivityRecord activity2 = createNonAttachedActivityRecord(mDisplayContent);
-        task1.addChild(activity2, WindowContainer.POSITION_TOP);
-        assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, activity2.getActivityType());
-        assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, task1.getActivityType());
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index dca6b08..2389d2d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -16,20 +16,39 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
+import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.util.DisplayMetrics.DENSITY_DEFAULT;
+import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
+import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE;
 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.sameInstance;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -39,18 +58,45 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 
+import android.app.ActivityManager;
+import android.app.TaskInfo;
 import android.app.WindowConfiguration;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
+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;
 
-import androidx.test.filters.SmallTest;
+import androidx.test.filters.MediumTest;
 
+import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
 
 /**
  * Test class for {@link Task}.
@@ -58,15 +104,25 @@
  * Build/Install/Run:
  *  atest WmTests:TaskTests
  */
-@SmallTest
+@MediumTest
 @Presubmit
 @RunWith(WindowTestRunner.class)
 public class TaskTests extends WindowTestsBase {
 
+    private static final String TASK_TAG = "task";
+
+    private Rect mParentBounds;
+
+    @Before
+    public void setUp() throws Exception {
+        mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/);
+        removeGlobalMinSizeRestriction();
+    }
+
     @Test
     public void testRemoveContainer() {
-        final Task stackController1 = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stackController1, 0 /* userId */);
+        final Task taskController1 = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(taskController1, 0 /* userId */);
         final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
 
         task.removeIfPossible();
@@ -78,8 +134,8 @@
 
     @Test
     public void testRemoveContainer_deferRemoval() {
-        final Task stackController1 = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stackController1, 0 /* userId */);
+        final Task taskController1 = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(taskController1, 0 /* userId */);
         final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
 
         doReturn(true).when(task).shouldDeferRemoval();
@@ -99,14 +155,14 @@
 
     @Test
     public void testReparent() {
-        final Task stackController1 = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stackController1, 0 /* userId */);
-        final Task stackController2 = createTaskStackOnDisplay(mDisplayContent);
-        final Task task2 = createTaskInStack(stackController2, 0 /* userId */);
+        final Task taskController1 = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(taskController1, 0 /* userId */);
+        final Task taskController2 = createTask(mDisplayContent);
+        final Task task2 = createTaskInRootTask(taskController2, 0 /* userId */);
 
         boolean gotException = false;
         try {
-            task.reparent(stackController1, 0, false/* moveParents */, "testReparent");
+            task.reparent(taskController1, 0, false/* moveParents */, "testReparent");
         } catch (IllegalArgumentException e) {
             gotException = true;
         }
@@ -118,29 +174,29 @@
         } catch (Exception e) {
             gotException = true;
         }
-        assertTrue("Should not be able to reparent to a stack that doesn't exist", gotException);
+        assertTrue("Should not be able to reparent to a task that doesn't exist", gotException);
 
-        task.reparent(stackController2, 0, false/* moveParents */, "testReparent");
-        assertEquals(stackController2, task.getParent());
+        task.reparent(taskController2, 0, false/* moveParents */, "testReparent");
+        assertEquals(taskController2, task.getParent());
         assertEquals(0, task.getParent().mChildren.indexOf(task));
         assertEquals(1, task2.getParent().mChildren.indexOf(task2));
     }
 
     @Test
     public void testReparent_BetweenDisplays() {
-        // Create first stack on primary display.
-        final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack1, 0 /* userId */);
-        assertEquals(mDisplayContent, stack1.getDisplayContent());
+        // Create first task on primary display.
+        final Task rootTask1 = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask1, 0 /* userId */);
+        assertEquals(mDisplayContent, rootTask1.getDisplayContent());
 
-        // Create second display and put second stack on it.
+        // Create second display and put second task on it.
         final DisplayContent dc = createNewDisplay();
-        final Task stack2 = createTaskStackOnDisplay(dc);
-        final Task task2 = createTaskInStack(stack2, 0 /* userId */);
+        final Task rootTask2 = createTask(dc);
+        final Task task2 = createTaskInRootTask(rootTask2, 0 /* userId */);
         // Reparent and check state
         clearInvocations(task);  // reset the number of onDisplayChanged for task.
-        task.reparent(stack2, 0, false /* moveParents */, "testReparent_BetweenDisplays");
-        assertEquals(stack2, task.getParent());
+        task.reparent(rootTask2, 0, false /* moveParents */, "testReparent_BetweenDisplays");
+        assertEquals(rootTask2, task.getParent());
         assertEquals(0, task.getParent().mChildren.indexOf(task));
         assertEquals(1, task2.getParent().mChildren.indexOf(task2));
         verify(task, times(1)).onDisplayChanged(any());
@@ -148,8 +204,8 @@
 
     @Test
     public void testBounds() {
-        final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack1, 0 /* userId */);
+        final Task rootTask1 = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask1, 0 /* userId */);
 
         // Check that setting bounds also updates surface position
         task.setWindowingMode(WINDOWING_MODE_FREEFORM);
@@ -159,9 +215,9 @@
     }
 
     @Test
-    public void testIsInStack() {
-        final Task task1 = createTaskStackOnDisplay(mDisplayContent);
-        final Task task2 = createTaskStackOnDisplay(mDisplayContent);
+    public void testIsInTask() {
+        final Task task1 = createTask(mDisplayContent);
+        final Task task2 = createTask(mDisplayContent);
         final ActivityRecord activity1 = createActivityRecord(mDisplayContent, task1);
         final ActivityRecord activity2 = createActivityRecord(mDisplayContent, task2);
         assertEquals(activity1, task1.isInTask(activity1));
@@ -170,7 +226,7 @@
 
     @Test
     public void testRemoveChildForOverlayTask() {
-        final Task task = createTaskStackOnDisplay(mDisplayContent);
+        final Task task = createTask(mDisplayContent);
         final int taskId = task.mTaskId;
         final ActivityRecord activity1 = createActivityRecord(mDisplayContent, task);
         final ActivityRecord activity2 = createActivityRecord(mDisplayContent, task);
@@ -193,10 +249,10 @@
 
     @Test
     public void testSwitchUser() {
-        final Task rootTask = createTaskStackOnDisplay(mDisplayContent);
-        final Task childTask = createTaskInStack(rootTask, 0 /* userId */);
-        final Task leafTask1 = createTaskInStack(childTask, 10 /* userId */);
-        final Task leafTask2 = createTaskInStack(childTask, 0 /* userId */);
+        final Task rootTask = createTask(mDisplayContent);
+        final Task childTask = createTaskInRootTask(rootTask, 0 /* userId */);
+        final Task leafTask1 = createTaskInRootTask(childTask, 10 /* userId */);
+        final Task leafTask2 = createTaskInRootTask(childTask, 0 /* userId */);
         assertEquals(1, rootTask.getChildCount());
         assertEquals(leafTask2, childTask.getTopChild());
 
@@ -208,9 +264,9 @@
 
     @Test
     public void testEnsureActivitiesVisible() {
-        final Task rootTask = createTaskStackOnDisplay(mDisplayContent);
-        final Task leafTask1 = createTaskInStack(rootTask, 0 /* userId */);
-        final Task leafTask2 = createTaskInStack(rootTask, 0 /* userId */);
+        final Task rootTask = createTask(mDisplayContent);
+        final Task leafTask1 = createTaskInRootTask(rootTask, 0 /* userId */);
+        final Task leafTask2 = createTaskInRootTask(rootTask, 0 /* userId */);
         final ActivityRecord activity1 = createActivityRecord(mDisplayContent, leafTask1);
         final ActivityRecord activity2 = createActivityRecord(mDisplayContent, leafTask2);
 
@@ -233,7 +289,7 @@
 
     @Test
     public void testResolveNonResizableTaskWindowingMode() {
-        final Task task = createTaskStackOnDisplay(mDisplayContent);
+        final Task task = createTask(mDisplayContent);
         Configuration parentConfig = task.getParent().getConfiguration();
         parentConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
         doReturn(false).when(task).isResizeable();
@@ -264,10 +320,10 @@
 
     @Test
     public void testHandlesOrientationChangeFromDescendant() {
-        final Task rootTask = createTaskStackOnDisplay(WINDOWING_MODE_MULTI_WINDOW,
-                ACTIVITY_TYPE_STANDARD, mDisplayContent);
-        final Task leafTask1 = createTaskInStack(rootTask, 0 /* userId */);
-        final Task leafTask2 = createTaskInStack(rootTask, 0 /* userId */);
+        final Task rootTask = createTask(mDisplayContent,
+                WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+        final Task leafTask1 = createTaskInRootTask(rootTask, 0 /* userId */);
+        final Task leafTask2 = createTaskInRootTask(rootTask, 0 /* userId */);
         leafTask1.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_HOME);
         leafTask2.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_STANDARD);
 
@@ -281,7 +337,7 @@
 
     @Test
     public void testAlwaysOnTop() {
-        final Task task = createTaskStackOnDisplay(mDisplayContent);
+        final Task task = createTask(mDisplayContent);
         task.setAlwaysOnTop(true);
         task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         assertTrue(task.isAlwaysOnTop());
@@ -289,4 +345,1053 @@
         task.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true /* set */);
         assertFalse(task.isAlwaysOnTop());
     }
+
+    @Test
+    public void testRestoreWindowedTask() throws Exception {
+        final Task expected = createTask(64);
+        expected.mLastNonFullscreenBounds = new Rect(50, 50, 100, 100);
+
+        final byte[] serializedBytes = serializeToBytes(expected);
+        final Task actual = restoreFromBytes(serializedBytes);
+        assertEquals(expected.mTaskId, actual.mTaskId);
+        assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds);
+    }
+
+    /** Ensure we have no chance to modify the original intent. */
+    @Test
+    public void testCopyBaseIntentForTaskInfo() {
+        final Task task = createTask(1);
+        task.setTaskDescription(new ActivityManager.TaskDescription());
+        final TaskInfo info = task.getTaskInfo();
+
+        // The intent of info should be a copy so assert that they are different instances.
+        Assert.assertThat(info.baseIntent, not(sameInstance(task.getBaseIntent())));
+    }
+
+    @Test
+    public void testReturnsToHomeRootTask() throws Exception {
+        final Task task = createTask(1);
+        spyOn(task);
+        doReturn(true).when(task).hasChild();
+        assertFalse(task.returnsToHomeRootTask());
+        task.intent = null;
+        assertFalse(task.returnsToHomeRootTask());
+        task.intent = new Intent();
+        assertFalse(task.returnsToHomeRootTask());
+        task.intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME);
+        assertTrue(task.returnsToHomeRootTask());
+    }
+
+    /** Ensures that empty bounds cause appBounds to inherit from parent. */
+    @Test
+    public void testAppBounds_EmptyBounds() {
+        final Rect emptyBounds = new Rect();
+        testRootTaskBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, emptyBounds,
+                mParentBounds);
+    }
+
+    /** Ensures that bounds on freeform root tasks are not clipped. */
+    @Test
+    public void testAppBounds_FreeFormBounds() {
+        final Rect freeFormBounds = new Rect(mParentBounds);
+        freeFormBounds.offset(10, 10);
+        testRootTaskBoundsConfiguration(WINDOWING_MODE_FREEFORM, mParentBounds, freeFormBounds,
+                freeFormBounds);
+    }
+
+    /** Ensures that fully contained bounds are not clipped. */
+    @Test
+    public void testAppBounds_ContainedBounds() {
+        final Rect insetBounds = new Rect(mParentBounds);
+        insetBounds.inset(5, 5, 5, 5);
+        testRootTaskBoundsConfiguration(
+                WINDOWING_MODE_FREEFORM, mParentBounds, insetBounds, insetBounds);
+    }
+
+    @Test
+    public void testFitWithinBounds() {
+        final Rect parentBounds = new Rect(10, 10, 200, 200);
+        TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
+        Task rootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM,
+                ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
+        final Configuration parentConfig = rootTask.getConfiguration();
+        parentConfig.windowConfiguration.setBounds(parentBounds);
+        parentConfig.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
+
+        // check top and left
+        Rect reqBounds = new Rect(-190, -190, 0, 0);
+        task.setBounds(reqBounds);
+        // Make sure part of it is exposed
+        assertTrue(task.getBounds().right > parentBounds.left);
+        assertTrue(task.getBounds().bottom > parentBounds.top);
+        // Should still be more-or-less in that corner
+        assertTrue(task.getBounds().left <= parentBounds.left);
+        assertTrue(task.getBounds().top <= parentBounds.top);
+
+        assertEquals(reqBounds.width(), task.getBounds().width());
+        assertEquals(reqBounds.height(), task.getBounds().height());
+
+        // check bottom and right
+        reqBounds = new Rect(210, 210, 400, 400);
+        task.setBounds(reqBounds);
+        // Make sure part of it is exposed
+        assertTrue(task.getBounds().left < parentBounds.right);
+        assertTrue(task.getBounds().top < parentBounds.bottom);
+        // Should still be more-or-less in that corner
+        assertTrue(task.getBounds().right >= parentBounds.right);
+        assertTrue(task.getBounds().bottom >= parentBounds.bottom);
+
+        assertEquals(reqBounds.width(), task.getBounds().width());
+        assertEquals(reqBounds.height(), task.getBounds().height());
+    }
+
+    /** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */
+    @Test
+    public void testBoundsOnModeChangeFreeformToFullscreen() {
+        DisplayContent display = mAtm.mRootWindowContainer.getDefaultDisplay();
+        Task rootTask = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true)
+                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        Task task = rootTask.getBottomMostTask();
+        task.getRootActivity().setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
+        DisplayInfo info = new DisplayInfo();
+        display.mDisplay.getDisplayInfo(info);
+        final Rect fullScreenBounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight);
+        final Rect freeformBounds = new Rect(fullScreenBounds);
+        freeformBounds.inset((int) (freeformBounds.width() * 0.2),
+                (int) (freeformBounds.height() * 0.2));
+        task.setBounds(freeformBounds);
+
+        assertEquals(freeformBounds, task.getBounds());
+
+        // FULLSCREEN inherits bounds
+        rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        assertEquals(fullScreenBounds, task.getBounds());
+        assertEquals(freeformBounds, task.mLastNonFullscreenBounds);
+
+        // FREEFORM restores bounds
+        rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertEquals(freeformBounds, task.getBounds());
+    }
+
+    /**
+     * Tests that a task with forced orientation has orientation-consistent bounds within the
+     * parent.
+     */
+    @Test
+    public void testFullscreenBoundsForcedOrientation() {
+        final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
+        final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
+        final DisplayContent display = new TestDisplayContent.Builder(mAtm,
+                fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build();
+        assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId));
+        // Fix the display orientation to landscape which is the natural rotation (0) for the test
+        // display.
+        final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
+        dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
+        dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
+
+        final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+                .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
+        final Task task = rootTask.getBottomMostTask();
+        final ActivityRecord root = task.getTopNonFinishingActivity();
+
+        assertEquals(fullScreenBounds, task.getBounds());
+
+        // Setting app to fixed portrait fits within parent
+        root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+        assertEquals(root, task.getRootActivity());
+        assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
+        // Portrait orientation is enforced on activity level. Task should fill fullscreen bounds.
+        assertThat(task.getBounds().height()).isLessThan(task.getBounds().width());
+        assertEquals(fullScreenBounds, task.getBounds());
+
+        // Top activity gets used
+        final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).setParentTask(rootTask)
+                .build();
+        assertEquals(top, task.getTopNonFinishingActivity());
+        top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
+        assertEquals(task.getBounds().width(), fullScreenBounds.width());
+
+        // Setting app to unspecified restores
+        top.setRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
+        assertEquals(fullScreenBounds, task.getBounds());
+
+        // Setting app to fixed landscape and changing display
+        top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        // Fix the display orientation to portrait which is 90 degrees for the test display.
+        dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90);
+
+        // Fixed orientation request should be resolved on activity level. Task fills display
+        // bounds.
+        assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width());
+        assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height());
+        assertEquals(fullScreenBoundsPort, task.getBounds());
+
+        // in FREEFORM, no constraint
+        final Rect freeformBounds = new Rect(display.getBounds());
+        freeformBounds.inset((int) (freeformBounds.width() * 0.2),
+                (int) (freeformBounds.height() * 0.2));
+        rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        task.setBounds(freeformBounds);
+        assertEquals(freeformBounds, task.getBounds());
+
+        // FULLSCREEN letterboxes bounds on activity level, no constraint on task level.
+        rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width());
+        assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height());
+        assertEquals(fullScreenBoundsPort, task.getBounds());
+
+        // FREEFORM restores bounds as before
+        rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertEquals(freeformBounds, task.getBounds());
+    }
+
+    @Test
+    public void testReportsOrientationRequestInLetterboxForOrientation() {
+        final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
+        final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
+        final DisplayContent display = new TestDisplayContent.Builder(mAtm,
+                fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build();
+        assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId));
+        // Fix the display orientation to landscape which is the natural rotation (0) for the test
+        // display.
+        final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
+        dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
+        dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
+
+        final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+                .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
+        final Task task = rootTask.getBottomMostTask();
+        ActivityRecord root = task.getTopNonFinishingActivity();
+
+        assertEquals(fullScreenBounds, task.getBounds());
+
+        // Setting app to fixed portrait fits within parent on activity level. Task fills parent.
+        root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+        assertThat(root.getBounds().width()).isLessThan(root.getBounds().height());
+        assertEquals(task.getBounds(), fullScreenBounds);
+
+        assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getOrientation());
+    }
+
+    @Test
+    public void testIgnoresForcedOrientationWhenParentHandles() {
+        final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
+        DisplayContent display = new TestDisplayContent.Builder(
+                mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build();
+
+        display.getRequestedOverrideConfiguration().orientation =
+                Configuration.ORIENTATION_LANDSCAPE;
+        display.onRequestedOverrideConfigurationChanged(
+                display.getRequestedOverrideConfiguration());
+        Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+                .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
+        Task task = rootTask.getBottomMostTask();
+        ActivityRecord root = task.getTopNonFinishingActivity();
+
+        final WindowContainer parentWindowContainer =
+                new WindowContainer(mSystemServicesTestRule.getWindowManagerService());
+        spyOn(parentWindowContainer);
+        parentWindowContainer.setBounds(fullScreenBounds);
+        doReturn(parentWindowContainer).when(task).getParent();
+        doReturn(display.getDefaultTaskDisplayArea()).when(task).getDisplayArea();
+        doReturn(rootTask).when(task).getRootTask();
+        doReturn(true).when(parentWindowContainer).handlesOrientationChangeFromDescendant();
+
+        // Setting app to fixed portrait fits within parent, but Task shouldn't adjust the
+        // bounds because its parent says it will handle it at a later time.
+        root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+        assertEquals(root, task.getRootActivity());
+        assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
+        assertEquals(fullScreenBounds, task.getBounds());
+    }
+
+    @Test
+    public void testComputeConfigResourceOverrides() {
+        final Rect fullScreenBounds = new Rect(0, 0, 1080, 1920);
+        TestDisplayContent display = new TestDisplayContent.Builder(
+                mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build();
+        final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+        final Configuration inOutConfig = new Configuration();
+        final Configuration parentConfig = new Configuration();
+        final int longSide = 1200;
+        final int shortSide = 600;
+        final Rect parentBounds = new Rect(0, 0, 250, 500);
+        final Rect parentAppBounds = new Rect(0, 0, 250, 480);
+        parentConfig.windowConfiguration.setBounds(parentBounds);
+        parentConfig.windowConfiguration.setAppBounds(parentAppBounds);
+        parentConfig.densityDpi = 400;
+        parentConfig.screenHeightDp = (parentBounds.bottom * 160) / parentConfig.densityDpi; // 200
+        parentConfig.screenWidthDp = (parentBounds.right * 160) / parentConfig.densityDpi; // 100
+        parentConfig.windowConfiguration.setRotation(ROTATION_0);
+
+        // By default, the input bounds will fill parent.
+        task.computeConfigResourceOverrides(inOutConfig, parentConfig);
+
+        assertEquals(parentConfig.screenHeightDp, inOutConfig.screenHeightDp);
+        assertEquals(parentConfig.screenWidthDp, inOutConfig.screenWidthDp);
+        assertEquals(parentAppBounds, inOutConfig.windowConfiguration.getAppBounds());
+        assertEquals(Configuration.ORIENTATION_PORTRAIT, inOutConfig.orientation);
+
+        // If bounds are overridden, config properties should be made to match. Surface hierarchy
+        // will crop for policy.
+        inOutConfig.setToDefaults();
+        final Rect largerPortraitBounds = new Rect(0, 0, shortSide, longSide);
+        inOutConfig.windowConfiguration.setBounds(largerPortraitBounds);
+        task.computeConfigResourceOverrides(inOutConfig, parentConfig);
+        // The override bounds are beyond the parent, the out appBounds should not be intersected
+        // by parent appBounds.
+        assertEquals(largerPortraitBounds, inOutConfig.windowConfiguration.getAppBounds());
+        assertEquals(longSide, inOutConfig.screenHeightDp * parentConfig.densityDpi / 160);
+        assertEquals(shortSide, inOutConfig.screenWidthDp * parentConfig.densityDpi / 160);
+
+        inOutConfig.setToDefaults();
+        // Landscape bounds.
+        final Rect largerLandscapeBounds = new Rect(0, 0, longSide, shortSide);
+        inOutConfig.windowConfiguration.setBounds(largerLandscapeBounds);
+
+        // Setup the display with a top stable inset. The later assertion will ensure the inset is
+        // excluded from screenHeightDp.
+        final int statusBarHeight = 100;
+        final DisplayPolicy policy = display.getDisplayPolicy();
+        doAnswer(invocationOnMock -> {
+            final Rect insets = invocationOnMock.<Rect>getArgument(0);
+            insets.top = statusBarHeight;
+            return null;
+        }).when(policy).convertNonDecorInsetsToStableInsets(any(), eq(ROTATION_0));
+
+        // Without limiting to be inside the parent bounds, the out screen size should keep relative
+        // to the input bounds.
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
+        final ActivityRecord.CompatDisplayInsets compatIntsets =
+                new ActivityRecord.CompatDisplayInsets(
+                        display, activity, /* fixedOrientationBounds= */ null);
+        task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatIntsets);
+
+        assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds());
+        assertEquals((shortSide - statusBarHeight) * DENSITY_DEFAULT / parentConfig.densityDpi,
+                inOutConfig.screenHeightDp);
+        assertEquals(longSide * DENSITY_DEFAULT / parentConfig.densityDpi,
+                inOutConfig.screenWidthDp);
+        assertEquals(Configuration.ORIENTATION_LANDSCAPE, inOutConfig.orientation);
+    }
+
+    @Test
+    public void testComputeConfigResourceLayoutOverrides() {
+        final Rect fullScreenBounds = new Rect(0, 0, 1000, 2500);
+        TestDisplayContent display = new TestDisplayContent.Builder(
+                mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build();
+        final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+        final Configuration inOutConfig = new Configuration();
+        final Configuration parentConfig = new Configuration();
+        final Rect nonLongBounds = new Rect(0, 0, 1000, 1250);
+        parentConfig.windowConfiguration.setBounds(fullScreenBounds);
+        parentConfig.windowConfiguration.setAppBounds(fullScreenBounds);
+        parentConfig.densityDpi = 400;
+        parentConfig.screenHeightDp = (fullScreenBounds.bottom * 160) / parentConfig.densityDpi;
+        parentConfig.screenWidthDp = (fullScreenBounds.right * 160) / parentConfig.densityDpi;
+        parentConfig.windowConfiguration.setRotation(ROTATION_0);
+
+        // Set BOTH screenW/H to an override value
+        inOutConfig.screenWidthDp = nonLongBounds.width() * 160 / parentConfig.densityDpi;
+        inOutConfig.screenHeightDp = nonLongBounds.height() * 160 / parentConfig.densityDpi;
+        task.computeConfigResourceOverrides(inOutConfig, parentConfig);
+
+        // screenLayout should honor override when both screenW/H are set.
+        assertTrue((inOutConfig.screenLayout & Configuration.SCREENLAYOUT_LONG_NO) != 0);
+    }
+
+    @Test
+    public void testComputeNestedConfigResourceOverrides() {
+        final Task task = new TaskBuilder(mSupervisor).build();
+        assertTrue(task.getResolvedOverrideBounds().isEmpty());
+        int origScreenH = task.getConfiguration().screenHeightDp;
+        Configuration rootTaskConfig = new Configuration();
+        rootTaskConfig.setTo(task.getRootTask().getRequestedOverrideConfiguration());
+        rootTaskConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+
+        // Set bounds on root task (not task) and verify that the task resource configuration
+        // changes despite it's override bounds being empty.
+        Rect bounds = new Rect(task.getRootTask().getBounds());
+        bounds.bottom = (int) (bounds.bottom * 0.6f);
+        rootTaskConfig.windowConfiguration.setBounds(bounds);
+        task.getRootTask().onRequestedOverrideConfigurationChanged(rootTaskConfig);
+        assertNotEquals(origScreenH, task.getConfiguration().screenHeightDp);
+    }
+
+    @Test
+    public void testFullScreenTaskNotAdjustedByMinimalSize() {
+        final Task fullscreenTask = new TaskBuilder(mSupervisor).build();
+        final Rect originalTaskBounds = new Rect(fullscreenTask.getBounds());
+        final ActivityInfo aInfo = new ActivityInfo();
+        aInfo.windowLayout = new ActivityInfo.WindowLayout(0 /* width */, 0 /* widthFraction */,
+                0 /* height */, 0 /* heightFraction */, 0 /* gravity */,
+                originalTaskBounds.width() * 2 /* minWidth */,
+                originalTaskBounds.height() * 2 /* minHeight */);
+        fullscreenTask.setMinDimensions(aInfo);
+        fullscreenTask.onConfigurationChanged(fullscreenTask.getParent().getConfiguration());
+
+        assertEquals(originalTaskBounds, fullscreenTask.getBounds());
+    }
+
+    @Test
+    public void testInsetDisregardedWhenFreeformOverlapsNavBar() {
+        TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
+        Task rootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        DisplayInfo displayInfo = new DisplayInfo();
+        mAtm.mContext.getDisplay().getDisplayInfo(displayInfo);
+        final int displayHeight = displayInfo.logicalHeight;
+        final Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
+        final Configuration inOutConfig = new Configuration();
+        final Configuration parentConfig = new Configuration();
+        final int longSide = 1200;
+        final int shortSide = 600;
+        parentConfig.densityDpi = 400;
+        parentConfig.screenHeightDp = 200; // 200 * 400 / 160 = 500px
+        parentConfig.screenWidthDp = 100; // 100 * 400 / 160 = 250px
+        parentConfig.windowConfiguration.setRotation(ROTATION_0);
+
+        final int longSideDp = 480; // longSide / density = 1200 / 400 * 160
+        final int shortSideDp = 240; // shortSide / density = 600 / 400 * 160
+        final int screenLayout = parentConfig.screenLayout
+                & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
+        final int reducedScreenLayout =
+                Configuration.reduceScreenLayout(screenLayout, longSideDp, shortSideDp);
+
+        // Portrait bounds overlapping with navigation bar, without insets.
+        final Rect freeformBounds = new Rect(0,
+                displayHeight - 10 - longSide,
+                shortSide,
+                displayHeight - 10);
+        inOutConfig.windowConfiguration.setBounds(freeformBounds);
+        // Set to freeform mode to verify bug fix.
+        inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+
+        task.computeConfigResourceOverrides(inOutConfig, parentConfig);
+
+        // screenW/H should not be effected by parent since overridden and freeform
+        assertEquals(freeformBounds.width() * 160 / parentConfig.densityDpi,
+                inOutConfig.screenWidthDp);
+        assertEquals(freeformBounds.height() * 160 / parentConfig.densityDpi,
+                inOutConfig.screenHeightDp);
+        assertEquals(reducedScreenLayout, inOutConfig.screenLayout);
+
+        inOutConfig.setToDefaults();
+        // Landscape bounds overlapping with navigtion bar, without insets.
+        freeformBounds.set(0,
+                displayHeight - 10 - shortSide,
+                longSide,
+                displayHeight - 10);
+        inOutConfig.windowConfiguration.setBounds(freeformBounds);
+        inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+
+        task.computeConfigResourceOverrides(inOutConfig, parentConfig);
+
+        assertEquals(freeformBounds.width() * 160 / parentConfig.densityDpi,
+                inOutConfig.screenWidthDp);
+        assertEquals(freeformBounds.height() * 160 / parentConfig.densityDpi,
+                inOutConfig.screenHeightDp);
+        assertEquals(reducedScreenLayout, inOutConfig.screenLayout);
+    }
+
+    /** Ensures that the alias intent won't have target component resolved. */
+    @Test
+    public void testTaskIntentActivityAlias() {
+        final String aliasClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".aliasActivity";
+        final String targetClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".targetActivity";
+        final ComponentName aliasComponent =
+                new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasClassName);
+        final ComponentName targetComponent =
+                new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, targetClassName);
+
+        final Intent intent = new Intent();
+        intent.setComponent(aliasComponent);
+        final ActivityInfo info = new ActivityInfo();
+        info.applicationInfo = new ApplicationInfo();
+        info.packageName = DEFAULT_COMPONENT_PACKAGE_NAME;
+        info.targetActivity = targetClassName;
+
+        final Task task = new Task.Builder(mAtm)
+                .setTaskId(1)
+                .setActivityInfo(info)
+                .setIntent(intent)
+                .build();
+        assertEquals("The alias activity component should be saved in task intent.", aliasClassName,
+                task.intent.getComponent().getClassName());
+
+        ActivityRecord aliasActivity = new ActivityBuilder(mAtm).setComponent(
+                aliasComponent).setTargetActivity(targetClassName).build();
+        assertEquals("Should be the same intent filter.", true,
+                task.isSameIntentFilter(aliasActivity));
+
+        ActivityRecord targetActivity = new ActivityBuilder(mAtm).setComponent(
+                targetComponent).build();
+        assertEquals("Should be the same intent filter.", true,
+                task.isSameIntentFilter(targetActivity));
+
+        ActivityRecord defaultActivity = new ActivityBuilder(mAtm).build();
+        assertEquals("Should not be the same intent filter.", false,
+                task.isSameIntentFilter(defaultActivity));
+    }
+
+    /** Test that root activity index is reported correctly for several activities in the task. */
+    @Test
+    public void testFindRootIndex() {
+        final Task task = getTestTask();
+        // Add an extra activity on top of the root one
+        new ActivityBuilder(mAtm).setTask(task).build();
+
+        assertEquals("The root activity in the task must be reported.", task.getChildAt(0),
+                task.getRootActivity(
+                        true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
+    }
+
+    /**
+     * Test that root activity index is reported correctly for several activities in the task when
+     * the activities on the bottom are finishing.
+     */
+    @Test
+    public void testFindRootIndex_finishing() {
+        final Task task = getTestTask();
+        // Add extra two activities and mark the two on the bottom as finishing.
+        final ActivityRecord activity0 = task.getBottomMostActivity();
+        activity0.finishing = true;
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+        activity1.finishing = true;
+        new ActivityBuilder(mAtm).setTask(task).build();
+
+        assertEquals("The first non-finishing activity in the task must be reported.",
+                task.getChildAt(2), task.getRootActivity(
+                        true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
+    }
+
+    /**
+     * Test that root activity index is reported correctly for several activities in the task when
+     * looking for the 'effective root'.
+     */
+    @Test
+    public void testFindRootIndex_effectiveRoot() {
+        final Task task = getTestTask();
+        // Add an extra activity on top of the root one
+        new ActivityBuilder(mAtm).setTask(task).build();
+
+        assertEquals("The root activity in the task must be reported.",
+                task.getChildAt(0), task.getRootActivity(
+                        false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
+    }
+
+    /**
+     * Test that root activity index is reported correctly when looking for the 'effective root' in
+     * case when bottom activities are relinquishing task identity or finishing.
+     */
+    @Test
+    public void testFindRootIndex_effectiveRoot_finishingAndRelinquishing() {
+        final Task task = getTestTask();
+        // Add extra two activities. Mark the one on the bottom with "relinquishTaskIdentity" and
+        // one above as finishing.
+        final ActivityRecord activity0 = task.getBottomMostActivity();
+        activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+        activity1.finishing = true;
+        new ActivityBuilder(mAtm).setTask(task).build();
+
+        assertEquals("The first non-finishing activity and non-relinquishing task identity "
+                + "must be reported.", task.getChildAt(2), task.getRootActivity(
+                false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
+    }
+
+    /**
+     * Test that root activity index is reported correctly when looking for the 'effective root'
+     * for the case when there is only a single activity that also has relinquishTaskIdentity set.
+     */
+    @Test
+    public void testFindRootIndex_effectiveRoot_relinquishingAndSingleActivity() {
+        final Task task = getTestTask();
+        // Set relinquishTaskIdentity for the only activity in the task
+        task.getBottomMostActivity().info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
+
+        assertEquals("The root activity in the task must be reported.",
+                task.getChildAt(0), task.getRootActivity(
+                        false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
+    }
+
+    /**
+     * Test that the topmost activity index is reported correctly when looking for the
+     * 'effective root' for the case when all activities have relinquishTaskIdentity set.
+     */
+    @Test
+    public void testFindRootIndex_effectiveRoot_relinquishingMultipleActivities() {
+        final Task task = getTestTask();
+        // Set relinquishTaskIdentity for all activities in the task
+        final ActivityRecord activity0 = task.getBottomMostActivity();
+        activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+        activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
+
+        assertEquals("The topmost activity in the task must be reported.",
+                task.getChildAt(task.getChildCount() - 1), task.getRootActivity(
+                        false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
+    }
+
+    /** Test that bottom-most activity is reported in {@link Task#getRootActivity()}. */
+    @Test
+    public void testGetRootActivity() {
+        final Task task = getTestTask();
+        // Add an extra activity on top of the root one
+        new ActivityBuilder(mAtm).setTask(task).build();
+
+        assertEquals("The root activity in the task must be reported.",
+                task.getBottomMostActivity(), task.getRootActivity());
+    }
+
+    /**
+     * Test that first non-finishing activity is reported in {@link Task#getRootActivity()}.
+     */
+    @Test
+    public void testGetRootActivity_finishing() {
+        final Task task = getTestTask();
+        // Add an extra activity on top of the root one
+        new ActivityBuilder(mAtm).setTask(task).build();
+        // Mark the root as finishing
+        task.getBottomMostActivity().finishing = true;
+
+        assertEquals("The first non-finishing activity in the task must be reported.",
+                task.getChildAt(1), task.getRootActivity());
+    }
+
+    /**
+     * Test that relinquishTaskIdentity flag is ignored in {@link Task#getRootActivity()}.
+     */
+    @Test
+    public void testGetRootActivity_relinquishTaskIdentity() {
+        final Task task = getTestTask();
+        // Mark the bottom-most activity with FLAG_RELINQUISH_TASK_IDENTITY.
+        final ActivityRecord activity0 = task.getBottomMostActivity();
+        activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
+        // Add an extra activity on top of the root one.
+        new ActivityBuilder(mAtm).setTask(task).build();
+
+        assertEquals("The root activity in the task must be reported.",
+                task.getBottomMostActivity(), task.getRootActivity());
+    }
+
+    /**
+     * Test that no activity is reported in {@link Task#getRootActivity()} when all activities
+     * in the task are finishing.
+     */
+    @Test
+    public void testGetRootActivity_allFinishing() {
+        final Task task = getTestTask();
+        // Mark the bottom-most activity as finishing.
+        final ActivityRecord activity0 = task.getBottomMostActivity();
+        activity0.finishing = true;
+        // Add an extra activity on top of the root one and mark it as finishing
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+        activity1.finishing = true;
+
+        assertNull("No activity must be reported if all are finishing", task.getRootActivity());
+    }
+
+    /**
+     * Test that first non-finishing activity is the root of task.
+     */
+    @Test
+    public void testIsRootActivity() {
+        final Task task = getTestTask();
+        // Mark the bottom-most activity as finishing.
+        final ActivityRecord activity0 = task.getBottomMostActivity();
+        activity0.finishing = true;
+        // Add an extra activity on top of the root one.
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+
+        assertFalse("Finishing activity must not be the root of task", activity0.isRootOfTask());
+        assertTrue("Non-finishing activity must be the root of task", activity1.isRootOfTask());
+    }
+
+    /**
+     * Test that if all activities in the task are finishing, then the one on the bottom is the
+     * root of task.
+     */
+    @Test
+    public void testIsRootActivity_allFinishing() {
+        final Task task = getTestTask();
+        // Mark the bottom-most activity as finishing.
+        final ActivityRecord activity0 = task.getBottomMostActivity();
+        activity0.finishing = true;
+        // Add an extra activity on top of the root one and mark it as finishing
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+        activity1.finishing = true;
+
+        assertTrue("Bottom activity must be the root of task", activity0.isRootOfTask());
+        assertFalse("Finishing activity on top must not be the root of task",
+                activity1.isRootOfTask());
+    }
+
+    /**
+     * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)}.
+     */
+    @Test
+    public void testGetTaskForActivity() {
+        final Task task0 = getTestTask();
+        final ActivityRecord activity0 = task0.getBottomMostActivity();
+
+        final Task task1 = getTestTask();
+        final ActivityRecord activity1 = task1.getBottomMostActivity();
+
+        assertEquals(task0.mTaskId,
+                ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */));
+        assertEquals(task1.mTaskId,
+                ActivityRecord.getTaskForActivityLocked(activity1.appToken,  false /* onlyRoot */));
+    }
+
+    /**
+     * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with finishing
+     * activity.
+     */
+    @Test
+    public void testGetTaskForActivity_onlyRoot_finishing() {
+        final Task task = getTestTask();
+        // Make the current root activity finishing
+        final ActivityRecord activity0 = task.getBottomMostActivity();
+        activity0.finishing = true;
+        // Add an extra activity on top - this will be the new root
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+        // Add one more on top
+        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
+
+        assertEquals(task.mTaskId,
+                ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */));
+        assertEquals(task.mTaskId,
+                ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */));
+        assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
+                ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */));
+    }
+
+    /**
+     * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with activity that
+     * relinquishes task identity.
+     */
+    @Test
+    public void testGetTaskForActivity_onlyRoot_relinquishTaskIdentity() {
+        final Task task = getTestTask();
+        // Make the current root activity relinquish task identity
+        final ActivityRecord activity0 = task.getBottomMostActivity();
+        activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
+        // Add an extra activity on top - this will be the new root
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+        // Add one more on top
+        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
+
+        assertEquals(task.mTaskId,
+                ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */));
+        assertEquals(task.mTaskId,
+                ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */));
+        assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
+                ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */));
+    }
+
+    /**
+     * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} allowing non-root
+     * entries.
+     */
+    @Test
+    public void testGetTaskForActivity_notOnlyRoot() {
+        final Task task = getTestTask();
+        // Mark the bottom-most activity as finishing.
+        final ActivityRecord activity0 = task.getBottomMostActivity();
+        activity0.finishing = true;
+
+        // Add an extra activity on top of the root one and make it relinquish task identity
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+        activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
+
+        // Add one more activity on top
+        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
+
+        assertEquals(task.mTaskId,
+                ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */));
+        assertEquals(task.mTaskId,
+                ActivityRecord.getTaskForActivityLocked(activity1.appToken, false /* onlyRoot */));
+        assertEquals(task.mTaskId,
+                ActivityRecord.getTaskForActivityLocked(activity2.appToken, false /* onlyRoot */));
+    }
+
+    /**
+     * Test {@link Task#updateEffectiveIntent()}.
+     */
+    @Test
+    public void testUpdateEffectiveIntent() {
+        // Test simple case with a single activity.
+        final Task task = getTestTask();
+        final ActivityRecord activity0 = task.getBottomMostActivity();
+
+        spyOn(task);
+        task.updateEffectiveIntent();
+        verify(task).setIntent(eq(activity0));
+    }
+
+    /**
+     * Test {@link Task#updateEffectiveIntent()} with root activity marked as finishing. This
+     * should make the task use the second activity when updating the intent.
+     */
+    @Test
+    public void testUpdateEffectiveIntent_rootFinishing() {
+        // Test simple case with a single activity.
+        final Task task = getTestTask();
+        final ActivityRecord activity0 = task.getBottomMostActivity();
+        // Mark the bottom-most activity as finishing.
+        activity0.finishing = true;
+        // Add an extra activity on top of the root one
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+
+        spyOn(task);
+        task.updateEffectiveIntent();
+        verify(task).setIntent(eq(activity1));
+    }
+
+    /**
+     * Test {@link Task#updateEffectiveIntent()} when all activities are finishing or
+     * relinquishing task identity. In this case the root activity should still be used when
+     * updating the intent (legacy behavior).
+     */
+    @Test
+    public void testUpdateEffectiveIntent_allFinishing() {
+        // Test simple case with a single activity.
+        final Task task = getTestTask();
+        final ActivityRecord activity0 = task.getBottomMostActivity();
+        // Mark the bottom-most activity as finishing.
+        activity0.finishing = true;
+        // Add an extra activity on top of the root one and make it relinquish task identity
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+        activity1.finishing = true;
+
+        // Task must still update the intent using the root activity (preserving legacy behavior).
+        spyOn(task);
+        task.updateEffectiveIntent();
+        verify(task).setIntent(eq(activity0));
+    }
+
+    @Test
+    public void testSaveLaunchingStateWhenConfigurationChanged() {
+        LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
+        spyOn(persister);
+
+        final Task task = getTestTask();
+        task.setHasBeenVisible(false);
+        task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
+        task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+        task.setHasBeenVisible(true);
+        task.onConfigurationChanged(task.getParent().getConfiguration());
+
+        verify(persister).saveTask(task, task.getDisplayContent());
+    }
+
+    @Test
+    public void testSaveLaunchingStateWhenClearingParent() {
+        LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
+        spyOn(persister);
+
+        final Task task = getTestTask();
+        task.setHasBeenVisible(false);
+        task.getDisplayContent().setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+        task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        final DisplayContent oldDisplay = task.getDisplayContent();
+
+        LaunchParamsController.LaunchParams params = new LaunchParamsController.LaunchParams();
+        params.mWindowingMode = WINDOWING_MODE_UNDEFINED;
+        persister.getLaunchParams(task, null, params);
+        assertEquals(WINDOWING_MODE_UNDEFINED, params.mWindowingMode);
+
+        task.setHasBeenVisible(true);
+        task.removeImmediately();
+
+        verify(persister).saveTask(task, oldDisplay);
+
+        persister.getLaunchParams(task, null, params);
+        assertEquals(WINDOWING_MODE_FULLSCREEN, params.mWindowingMode);
+    }
+
+    @Test
+    public void testNotSaveLaunchingStateNonFreeformDisplay() {
+        LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
+        spyOn(persister);
+
+        final Task task = getTestTask();
+        task.setHasBeenVisible(false);
+        task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+        task.setHasBeenVisible(true);
+        task.onConfigurationChanged(task.getParent().getConfiguration());
+
+        Mockito.verify(persister, never()).saveTask(same(task), any());
+    }
+
+    @Test
+    public void testNotSaveLaunchingStateWhenNotFullscreenOrFreeformWindow() {
+        LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
+        spyOn(persister);
+
+        final Task task = getTestTask();
+        task.setHasBeenVisible(false);
+        task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
+        task.getRootTask().setWindowingMode(WINDOWING_MODE_PINNED);
+
+        task.setHasBeenVisible(true);
+        task.onConfigurationChanged(task.getParent().getConfiguration());
+
+        Mockito.verify(persister, never()).saveTask(same(task), any());
+    }
+
+    @Test
+    public void testNotSaveLaunchingStateForNonLeafTask() {
+        LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
+        spyOn(persister);
+
+        final Task task = getTestTask();
+        task.setHasBeenVisible(false);
+        task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
+        task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+        final Task leafTask = createTaskInRootTask(task, 0 /* userId */);
+
+        leafTask.setHasBeenVisible(true);
+        task.setHasBeenVisible(true);
+        task.onConfigurationChanged(task.getParent().getConfiguration());
+
+        Mockito.verify(persister, never()).saveTask(same(task), any());
+        verify(persister).saveTask(same(leafTask), any());
+    }
+
+    @Test
+    public void testNotSpecifyOrientationByFloatingTask() {
+        final Task task = new TaskBuilder(mSupervisor)
+                .setCreateActivity(true).setCreateParentTask(true).build();
+        final ActivityRecord activity = task.getTopMostActivity();
+        final WindowContainer<?> parentContainer = task.getParent();
+        final TaskDisplayArea taskDisplayArea = task.getDisplayArea();
+        activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, parentContainer.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation());
+
+        task.setWindowingMode(WINDOWING_MODE_PINNED);
+
+        // TDA returns the last orientation when child returns UNSET
+        assertEquals(SCREEN_ORIENTATION_UNSET, parentContainer.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation());
+    }
+
+    @Test
+    public void testNotSpecifyOrientation_taskDisplayAreaNotFocused() {
+        final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+        final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
+                mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
+                FEATURE_VENDOR_FIRST);
+        final Task firstRootTask = firstTaskDisplayArea.createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+        final Task secondRootTask = secondTaskDisplayArea.createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+        final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
+                .setTask(firstRootTask).build();
+        final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
+                .setTask(secondRootTask).build();
+        firstActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        secondActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+
+        // Activity on TDA1 is focused
+        mDisplayContent.setFocusedApp(firstActivity);
+
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation());
+
+        // No focused app, TDA1 is still recorded as last focused.
+        mDisplayContent.setFocusedApp(null);
+
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation());
+
+        // Activity on TDA2 is focused
+        mDisplayContent.setFocusedApp(secondActivity);
+
+        assertEquals(SCREEN_ORIENTATION_UNSET, firstTaskDisplayArea.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_PORTRAIT, secondTaskDisplayArea.getOrientation());
+    }
+
+    @Test
+    public void testNotifyOrientationChangeCausedByConfigurationChange() {
+        final Task task = getTestTask();
+        final ActivityRecord activity = task.getTopMostActivity();
+        final DisplayContent display = task.getDisplayContent();
+        display.setWindowingMode(WINDOWING_MODE_FREEFORM);
+
+        activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation());
+        verify(display).onDescendantOrientationChanged(same(task));
+        reset(display);
+
+        display.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, task.getOrientation());
+        verify(display).onDescendantOrientationChanged(same(task));
+    }
+
+    private Task getTestTask() {
+        final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
+        return task.getBottomMostTask();
+    }
+
+    private void testRootTaskBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds,
+            Rect expectedConfigBounds) {
+
+        TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
+        Task rootTask = taskDisplayArea.createRootTask(windowingMode, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
+
+        final Configuration parentConfig = rootTask.getConfiguration();
+        parentConfig.windowConfiguration.setAppBounds(parentBounds);
+        task.setBounds(bounds);
+
+        task.resolveOverrideConfiguration(parentConfig);
+        // Assert that both expected and actual are null or are equal to each other
+        assertEquals(expectedConfigBounds,
+                task.getResolvedOverrideConfiguration().windowConfiguration.getAppBounds());
+    }
+
+    private byte[] serializeToBytes(Task r) throws Exception {
+        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+            final TypedXmlSerializer serializer = Xml.newFastSerializer();
+            serializer.setOutput(os, "UTF-8");
+            serializer.startDocument(null, true);
+            serializer.startTag(null, TASK_TAG);
+            r.saveToXml(serializer);
+            serializer.endTag(null, TASK_TAG);
+            serializer.endDocument();
+
+            os.flush();
+            return os.toByteArray();
+        }
+    }
+
+    private Task restoreFromBytes(byte[] in) throws IOException, XmlPullParserException {
+        try (Reader reader = new InputStreamReader(new ByteArrayInputStream(in))) {
+            final TypedXmlPullParser parser = Xml.newFastPullParser();
+            parser.setInput(reader);
+            assertEquals(XmlPullParser.START_TAG, parser.next());
+            assertEquals(TASK_TAG, parser.getName());
+            return Task.restoreFromXml(parser, mAtm.mTaskSupervisor);
+        }
+    }
+
+    private Task createTask(int taskId) {
+        return new Task.Builder(mAtm)
+                .setTaskId(taskId)
+                .setIntent(new Intent())
+                .setRealActivity(ActivityBuilder.getDefaultComponent())
+                .setEffectiveUid(10050)
+                .buildInner();
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 79ef868..2dfb3a1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -69,10 +69,8 @@
         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
         ArraySet<WindowContainer> participants = transition.mParticipants;
 
-        final Task newTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, mDisplayContent);
-        final Task oldTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final Task newTask = createTask(mDisplayContent);
+        final Task oldTask = createTask(mDisplayContent);
         final ActivityRecord closing = createActivityRecord(oldTask);
         final ActivityRecord opening = createActivityRecord(newTask);
         // Start states.
@@ -128,12 +126,10 @@
         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
         ArraySet<WindowContainer> participants = transition.mParticipants;
 
-        final Task newTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, mDisplayContent);
-        final Task newNestedTask = createTaskInStack(newTask, 0);
-        final Task newNestedTask2 = createTaskInStack(newTask, 0);
-        final Task oldTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final Task newTask = createTask(mDisplayContent);
+        final Task newNestedTask = createTaskInRootTask(newTask, 0);
+        final Task newNestedTask2 = createTaskInRootTask(newTask, 0);
+        final Task oldTask = createTask(mDisplayContent);
         final ActivityRecord closing = createActivityRecord(oldTask);
         final ActivityRecord opening = createActivityRecord(newNestedTask);
         final ActivityRecord opening2 = createActivityRecord(newNestedTask2);
@@ -179,11 +175,9 @@
         final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
         ArraySet<WindowContainer> participants = transition.mParticipants;
-        final Task showTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, mDisplayContent);
-        final Task showNestedTask = createTaskInStack(showTask, 0);
-        final Task showTask2 = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final Task showTask = createTask(mDisplayContent);
+        final Task showNestedTask = createTaskInRootTask(showTask, 0);
+        final Task showTask2 = createTask(mDisplayContent);
         final DisplayArea tda = showTask.getDisplayArea();
         final ActivityRecord showing = createActivityRecord(showNestedTask);
         final ActivityRecord showing2 = createActivityRecord(showTask2);
@@ -231,12 +225,10 @@
     public void testCreateInfo_existenceChange() {
         final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
 
-        final Task openTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final Task openTask = createTask(mDisplayContent);
         final ActivityRecord opening = createActivityRecord(openTask);
         opening.mVisibleRequested = false; // starts invisible
-        final Task closeTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final Task closeTask = createTask(mDisplayContent);
         final ActivityRecord closing = createActivityRecord(closeTask);
         closing.mVisibleRequested = true; // starts visible
 
@@ -268,8 +260,8 @@
         final Task[] tasks = new Task[taskCount];
         for (int i = 0; i < taskCount; ++i) {
             // Each add goes on top, so at the end of this, task[9] should be on top
-            tasks[i] = createTaskStackOnDisplay(WINDOWING_MODE_FREEFORM,
-                    ACTIVITY_TYPE_STANDARD, mDisplayContent);
+            tasks[i] = createTask(mDisplayContent,
+                    WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
             final ActivityRecord act = createActivityRecord(tasks[i]);
             // alternate so that the transition doesn't get promoted to the display area
             act.mVisibleRequested = (i % 2) == 0; // starts invisible
@@ -305,8 +297,8 @@
         final Task[] tasks = new Task[taskCount];
         for (int i = 0; i < taskCount; ++i) {
             // Each add goes on top, so at the end of this, task[9] should be on top
-            tasks[i] = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
-                    ACTIVITY_TYPE_STANDARD, mDisplayContent);
+            tasks[i] = createTask(mDisplayContent,
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
             final ActivityRecord act = createActivityRecord(tasks[i]);
             // alternate so that the transition doesn't get promoted to the display area
             act.mVisibleRequested = (i % 2) == 0; // starts invisible
@@ -353,15 +345,13 @@
         ArraySet<WindowContainer> participants = transition.mParticipants;
         ITaskOrganizer mockOrg = mock(ITaskOrganizer.class);
 
-        final Task openTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, mDisplayContent);
-        final Task openInOpenTask = createTaskInStack(openTask, 0);
+        final Task openTask = createTask(mDisplayContent);
+        final Task openInOpenTask = createTaskInRootTask(openTask, 0);
         final ActivityRecord openInOpen = createActivityRecord(openInOpenTask);
 
-        final Task changeTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, mDisplayContent);
-        final Task changeInChangeTask = createTaskInStack(changeTask, 0);
-        final Task openInChangeTask = createTaskInStack(changeTask, 0);
+        final Task changeTask = createTask(mDisplayContent);
+        final Task changeInChangeTask = createTaskInRootTask(changeTask, 0);
+        final Task openInChangeTask = createTaskInRootTask(changeTask, 0);
         final ActivityRecord changeInChange = createActivityRecord(changeInChangeTask);
         final ActivityRecord openInChange = createActivityRecord(openInChangeTask);
         // set organizer for everything so that they all get added to transition info
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 b88173d..6919c4c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -811,18 +811,18 @@
 
     @Test
     public void testOnDisplayChanged() {
-        final Task stack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
         final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
 
         final DisplayContent newDc = createNewDisplay();
-        stack.getDisplayArea().removeRootTask(stack);
-        newDc.getDefaultTaskDisplayArea().addChild(stack, POSITION_TOP);
+        rootTask.getDisplayArea().removeRootTask(rootTask);
+        newDc.getDefaultTaskDisplayArea().addChild(rootTask, POSITION_TOP);
 
-        verify(stack).onDisplayChanged(newDc);
+        verify(rootTask).onDisplayChanged(newDc);
         verify(task).onDisplayChanged(newDc);
         verify(activity).onDisplayChanged(newDc);
-        assertEquals(newDc, stack.mDisplayContent);
+        assertEquals(newDc, rootTask.mDisplayContent);
         assertEquals(newDc, task.mDisplayContent);
         assertEquals(newDc, activity.mDisplayContent);
     }
@@ -854,21 +854,21 @@
 
     @Test
     public void testTaskCanApplyAnimation() {
-        final Task stack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
         final ActivityRecord activity2 = createActivityRecord(mDisplayContent, task);
         final ActivityRecord activity1 = createActivityRecord(mDisplayContent, task);
         verifyWindowContainerApplyAnimation(task, activity1, activity2);
     }
 
     @Test
-    public void testStackCanApplyAnimation() {
-        final Task stack = createTaskStackOnDisplay(mDisplayContent);
+    public void testRootTaskCanApplyAnimation() {
+        final Task rootTask = createTask(mDisplayContent);
         final ActivityRecord activity2 = createActivityRecord(mDisplayContent,
-                createTaskInStack(stack, 0 /* userId */));
+                createTaskInRootTask(rootTask, 0 /* userId */));
         final ActivityRecord activity1 = createActivityRecord(mDisplayContent,
-                createTaskInStack(stack, 0 /* userId */));
-        verifyWindowContainerApplyAnimation(stack, activity1, activity2);
+                createTaskInRootTask(rootTask, 0 /* userId */));
+        verifyWindowContainerApplyAnimation(rootTask, activity1, activity2);
     }
 
     @Test
@@ -878,21 +878,21 @@
 
         assertNull(windowContainer.getDisplayArea());
 
-        // ActivityStack > WindowContainer
-        final Task activityStack = createTaskStackOnDisplay(mDisplayContent);
-        activityStack.addChild(windowContainer, 0);
-        activityStack.setParent(null);
+        // Task > WindowContainer
+        final Task task = createTask(mDisplayContent);
+        task.addChild(windowContainer, 0);
+        task.setParent(null);
 
         assertNull(windowContainer.getDisplayArea());
-        assertNull(activityStack.getDisplayArea());
+        assertNull(task.getDisplayArea());
 
-        // TaskDisplayArea > ActivityStack > WindowContainer
+        // TaskDisplayArea > Task > WindowContainer
         final TaskDisplayArea taskDisplayArea = new TaskDisplayArea(
                 mDisplayContent, mWm, "TaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
-        taskDisplayArea.addChild(activityStack, 0);
+        taskDisplayArea.addChild(task, 0);
 
         assertEquals(taskDisplayArea, windowContainer.getDisplayArea());
-        assertEquals(taskDisplayArea, activityStack.getDisplayArea());
+        assertEquals(taskDisplayArea, task.getDisplayArea());
         assertEquals(taskDisplayArea, taskDisplayArea.getDisplayArea());
 
         // DisplayArea
@@ -986,8 +986,8 @@
 
     @Test
     public void testFreezeInsets() {
-        final Task stack = createTaskStackOnDisplay(mDisplayContent);
-        final ActivityRecord activity = createActivityRecord(mDisplayContent, stack);
+        final Task task = createTask(mDisplayContent);
+        final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
         final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
 
         // Set visibility to false, verify the main window of the task will be set the frozen
@@ -1002,8 +1002,8 @@
 
     @Test
     public void testFreezeInsetsStateWhenAppTransition() {
-        final Task stack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
         final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
         final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
         task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 6d0e510..baf072d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -80,18 +80,18 @@
     }
 
     @Test
-    public void testTaskFocusChange_stackNotHomeType_focusChanges() throws RemoteException {
+    public void testTaskFocusChange_rootTaskNotHomeType_focusChanges() throws RemoteException {
         DisplayContent display = createNewDisplay();
         // Current focused window
-        Task focusedStack = createTaskStackOnDisplay(
-                WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, display);
-        Task focusedTask = createTaskInStack(focusedStack, 0 /* userId */);
+        Task focusedRootTask = createTask(
+                display, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+        Task focusedTask = createTaskInRootTask(focusedRootTask, 0 /* userId */);
         WindowState focusedWindow = createAppWindow(focusedTask, TYPE_APPLICATION, "App Window");
         mDisplayContent.mCurrentFocus = focusedWindow;
         // Tapped task
-        Task tappedStack = createTaskStackOnDisplay(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, display);
-        Task tappedTask = createTaskInStack(tappedStack, 0 /* userId */);
+        Task tappedRootTask = createTask(
+                display, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
         spyOn(mWm.mAtmService);
 
         mWm.handleTaskFocusChange(tappedTask);
@@ -100,19 +100,19 @@
     }
 
     @Test
-    public void testTaskFocusChange_stackHomeTypeWithSameTaskDisplayArea_focusDoesNotChange()
+    public void testTaskFocusChange_rootTaskHomeTypeWithSameTaskDisplayArea_focusDoesNotChange()
             throws RemoteException {
         DisplayContent display = createNewDisplay();
         // Current focused window
-        Task focusedStack = createTaskStackOnDisplay(
-                WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, display);
-        Task focusedTask = createTaskInStack(focusedStack, 0 /* userId */);
+        Task focusedRootTask = createTask(
+                display, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+        Task focusedTask = createTaskInRootTask(focusedRootTask, 0 /* userId */);
         WindowState focusedWindow = createAppWindow(focusedTask, TYPE_APPLICATION, "App Window");
         mDisplayContent.mCurrentFocus = focusedWindow;
         // Tapped home task
-        Task tappedStack = createTaskStackOnDisplay(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, display);
-        Task tappedTask = createTaskInStack(tappedStack, 0 /* userId */);
+        Task tappedRootTask = createTask(
+                display, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+        Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
         spyOn(mWm.mAtmService);
 
         mWm.handleTaskFocusChange(tappedTask);
@@ -121,21 +121,21 @@
     }
 
     @Test
-    public void testTaskFocusChange_stackHomeTypeWithDifferentTaskDisplayArea_focusChanges()
+    public void testTaskFocusChange_rootTaskHomeTypeWithDifferentTaskDisplayArea_focusChanges()
             throws RemoteException {
         final DisplayContent display = createNewDisplay();
         final TaskDisplayArea secondTda = createTaskDisplayArea(
                 display, mWm, "Tapped TDA", FEATURE_VENDOR_FIRST);
         // Current focused window
-        Task focusedStack = createTaskStackOnDisplay(
-                WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, display);
-        Task focusedTask = createTaskInStack(focusedStack, 0 /* userId */);
+        Task focusedRootTask = createTask(
+                display, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+        Task focusedTask = createTaskInRootTask(focusedRootTask, 0 /* userId */);
         WindowState focusedWindow = createAppWindow(focusedTask, TYPE_APPLICATION, "App Window");
         mDisplayContent.mCurrentFocus = focusedWindow;
         // Tapped home task on another task display area
-        Task tappedStack = createTaskStackOnTaskDisplayArea(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, secondTda);
-        Task tappedTask = createTaskInStack(tappedStack, 0 /* userId */);
+        Task tappedRootTask = createTask(secondTda, WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD);
+        Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
         spyOn(mWm.mAtmService);
 
         mWm.handleTaskFocusChange(tappedTask);
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 01c503e..e2b58bc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -125,8 +125,8 @@
         return registerMockOrganizer(null);
     }
 
-    Task createTask(Task stack, boolean fakeDraw) {
-        final Task task = createTaskInStack(stack, 0);
+    Task createTask(Task rootTask, boolean fakeDraw) {
+        final Task task = createTaskInRootTask(rootTask, 0);
 
         if (fakeDraw) {
             task.setHasBeenVisible(true);
@@ -134,13 +134,13 @@
         return task;
     }
 
-    Task createTask(Task stack) {
+    Task createTask(Task rootTask) {
         // Fake draw notifications for most of our tests.
-        return createTask(stack, true);
+        return createTask(rootTask, true);
     }
 
-    Task createStack() {
-        return createTaskStackOnDisplay(mDisplayContent);
+    Task createRootTask() {
+        return createTask(mDisplayContent);
     }
 
     @Before
@@ -153,14 +153,14 @@
     @Test
     public void testAppearVanish() throws RemoteException {
         final ITaskOrganizer organizer = registerMockOrganizer();
-        final Task stack = createStack();
-        final Task task = createTask(stack);
+        final Task rootTask = createRootTask();
+        final Task task = createTask(rootTask);
         // Ensure events dispatch to organizer.
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
 
         verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
 
-        stack.removeImmediately();
+        rootTask.removeImmediately();
         // Ensure events dispatch to organizer.
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
         verify(organizer).onTaskVanished(any());
@@ -169,21 +169,21 @@
     @Test
     public void testAppearWaitsForVisibility() throws RemoteException {
         final ITaskOrganizer organizer = registerMockOrganizer();
-        final Task stack = createStack();
-        final Task task = createTask(stack, false);
+        final Task rootTask = createRootTask();
+        final Task task = createTask(rootTask, false);
         // Ensure events dispatch to organizer.
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
 
         verify(organizer, never())
                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
-        stack.setHasBeenVisible(true);
+        rootTask.setHasBeenVisible(true);
         // Ensure events dispatch to organizer.
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
-        assertTrue(stack.getHasBeenVisible());
+        assertTrue(rootTask.getHasBeenVisible());
 
         verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
 
-        stack.removeImmediately();
+        rootTask.removeImmediately();
         // Ensure events dispatch to organizer.
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
         verify(organizer).onTaskVanished(any());
@@ -192,108 +192,108 @@
     @Test
     public void testNoVanishedIfNoAppear() throws RemoteException {
         final ITaskOrganizer organizer = registerMockOrganizer();
-        final Task stack = createStack();
-        final Task task = createTask(stack, false /* hasBeenVisible */);
+        final Task rootTask = createRootTask();
+        final Task task = createTask(rootTask, false /* hasBeenVisible */);
 
         // In this test we skip making the Task visible, and verify
         // that even though a TaskOrganizer is set remove doesn't emit
         // a vanish callback, because we never emitted appear.
-        stack.setTaskOrganizer(organizer);
+        rootTask.setTaskOrganizer(organizer);
         verify(organizer, never())
                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
-        stack.removeImmediately();
+        rootTask.removeImmediately();
         verify(organizer, never()).onTaskVanished(any());
     }
 
     @Test
     public void testTaskNoDraw() throws RemoteException {
         final ITaskOrganizer organizer = registerMockOrganizer();
-        final Task stack = createStack();
-        final Task task = createTask(stack, false /* fakeDraw */);
+        final Task rootTask = createRootTask();
+        final Task task = createTask(rootTask, false /* fakeDraw */);
         // Ensure events dispatch to organizer.
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
 
         verify(organizer, never())
                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
-        assertTrue(stack.isOrganized());
+        assertTrue(rootTask.isOrganized());
 
         mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
         // Ensure events dispatch to organizer.
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
-        assertTaskVanished(organizer, false /* expectVanished */, stack);
-        assertFalse(stack.isOrganized());
+        assertTaskVanished(organizer, false /* expectVanished */, rootTask);
+        assertFalse(rootTask.isOrganized());
     }
 
     @Test
     public void testClearOrganizer() throws RemoteException {
         final ITaskOrganizer organizer = registerMockOrganizer();
-        final Task stack = createStack();
-        final Task task = createTask(stack);
+        final Task rootTask = createRootTask();
+        final Task task = createTask(rootTask);
         // Ensure events dispatch to organizer.
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
 
         verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
-        assertTrue(stack.isOrganized());
+        assertTrue(rootTask.isOrganized());
 
-        stack.setTaskOrganizer(null);
+        rootTask.setTaskOrganizer(null);
         // Ensure events dispatch to organizer.
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
 
         verify(organizer).onTaskVanished(any());
-        assertFalse(stack.isOrganized());
+        assertFalse(rootTask.isOrganized());
     }
 
     @Test
     public void testUnregisterOrganizer() throws RemoteException {
         final ITaskOrganizer organizer = registerMockOrganizer();
-        final Task stack = createStack();
-        final Task task = createTask(stack);
+        final Task rootTask = createRootTask();
+        final Task task = createTask(rootTask);
         // Ensure events dispatch to organizer.
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
 
         verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
-        assertTrue(stack.isOrganized());
+        assertTrue(rootTask.isOrganized());
 
         mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
         // Ensure events dispatch to organizer.
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
 
-        assertTaskVanished(organizer, true /* expectVanished */, stack);
-        assertFalse(stack.isOrganized());
+        assertTaskVanished(organizer, true /* expectVanished */, rootTask);
+        assertFalse(rootTask.isOrganized());
     }
 
     @Test
     public void testUnregisterOrganizerReturnsRegistrationToPrevious() throws RemoteException {
-        final Task stack = createStack();
-        final Task task = createTask(stack);
-        final Task stack2 = createStack();
-        final Task task2 = createTask(stack2);
-        final Task stack3 = createStack();
-        final Task task3 = createTask(stack3);
+        final Task rootTask = createRootTask();
+        final Task task = createTask(rootTask);
+        final Task rootTask2 = createRootTask();
+        final Task task2 = createTask(rootTask2);
+        final Task rootTask3 = createRootTask();
+        final Task task3 = createTask(rootTask3);
         final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>();
         final ITaskOrganizer organizer = registerMockOrganizer(existingTasks);
         // Ensure events dispatch to organizer.
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
 
         // verify that tasks are returned and taskAppeared is not called
-        assertContainsTasks(existingTasks, stack, stack2, stack3);
+        assertContainsTasks(existingTasks, rootTask, rootTask2, rootTask3);
         verify(organizer, times(0)).onTaskAppeared(any(RunningTaskInfo.class),
                 any(SurfaceControl.class));
         verify(organizer, times(0)).onTaskVanished(any());
-        assertTrue(stack.isOrganized());
+        assertTrue(rootTask.isOrganized());
 
         // Now we replace the registration and verify the new organizer receives existing tasks
         final ArrayList<TaskAppearedInfo> existingTasks2 = new ArrayList<>();
         final ITaskOrganizer organizer2 = registerMockOrganizer(existingTasks2);
         // Ensure events dispatch to organizer.
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
-        assertContainsTasks(existingTasks2, stack, stack2, stack3);
+        assertContainsTasks(existingTasks2, rootTask, rootTask2, rootTask3);
         verify(organizer2, times(0)).onTaskAppeared(any(RunningTaskInfo.class),
                 any(SurfaceControl.class));
         verify(organizer2, times(0)).onTaskVanished(any());
         // Removed tasks from the original organizer
-        assertTaskVanished(organizer, true /* expectVanished */, stack, stack2, stack3);
-        assertTrue(stack2.isOrganized());
+        assertTaskVanished(organizer, true /* expectVanished */, rootTask, rootTask2, rootTask3);
+        assertTrue(rootTask2.isOrganized());
 
         // Now we unregister the second one, the first one should automatically be reregistered
         // so we verify that it's now seeing changes.
@@ -302,18 +302,18 @@
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
         verify(organizer, times(3))
                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
-        assertTaskVanished(organizer2, true /* expectVanished */, stack, stack2, stack3);
+        assertTaskVanished(organizer2, true /* expectVanished */, rootTask, rootTask2, rootTask3);
     }
 
     @Test
     public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException {
-        final Task stack = createStack();
-        final Task task = createTask(stack);
-        final Task stack2 = createStack();
-        final Task task2 = createTask(stack2);
+        final Task rootTask = createRootTask();
+        final Task task = createTask(rootTask);
+        final Task rootTask2 = createRootTask();
+        final Task task2 = createTask(rootTask2);
         ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>();
         final ITaskOrganizer organizer = registerMockOrganizer(existingTasks);
-        assertContainsTasks(existingTasks, stack, stack2);
+        assertContainsTasks(existingTasks, rootTask, rootTask2);
 
         // Verify we don't get onTaskAppeared if we are returned the tasks
         verify(organizer, never())
@@ -323,21 +323,21 @@
     @Test
     public void testTaskTransaction() {
         removeGlobalMinSizeRestriction();
-        final Task stack = new TaskBuilder(mSupervisor)
+        final Task rootTask = new TaskBuilder(mSupervisor)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
-        final Task task = stack.getTopMostTask();
+        final Task task = rootTask.getTopMostTask();
         testTransaction(task);
     }
 
     @Test
-    public void testStackTransaction() {
+    public void testRootTaskTransaction() {
         removeGlobalMinSizeRestriction();
-        final Task stack = new TaskBuilder(mSupervisor)
+        final Task rootTask = new TaskBuilder(mSupervisor)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         RootTaskInfo info =
                 mWm.mAtmService.getRootTaskInfo(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
-        assertEquals(stack.mRemoteToken.toWindowContainerToken(), info.token);
-        testTransaction(stack);
+        assertEquals(rootTask.mRemoteToken.toWindowContainerToken(), info.token);
+        testTransaction(rootTask);
     }
 
     @Test
@@ -357,9 +357,9 @@
 
     @Test
     public void testSetWindowingMode() {
-        final Task stack = new TaskBuilder(mSupervisor)
+        final Task rootTask = new TaskBuilder(mSupervisor)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
-        testSetWindowingMode(stack);
+        testSetWindowingMode(rootTask);
 
         final DisplayArea displayArea = new DisplayArea<>(mWm, ABOVE_TASKS, "DisplayArea");
         displayArea.setWindowingMode(WINDOWING_MODE_FREEFORM);
@@ -376,30 +376,30 @@
     @Test
     public void testSetActivityWindowingMode() {
         final ActivityRecord record = makePipableActivity();
-        final Task stack = record.getRootTask();
+        final Task rootTask = record.getRootTask();
         final WindowContainerTransaction t = new WindowContainerTransaction();
 
-        t.setWindowingMode(stack.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_PINNED);
+        t.setWindowingMode(rootTask.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_PINNED);
         t.setActivityWindowingMode(
-                stack.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_FULLSCREEN);
+                rootTask.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_FULLSCREEN);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
 
         assertEquals(WINDOWING_MODE_FULLSCREEN, record.getWindowingMode());
-        assertEquals(WINDOWING_MODE_PINNED, stack.getWindowingMode());
+        assertEquals(WINDOWING_MODE_PINNED, rootTask.getWindowingMode());
     }
 
     @Test
     public void testContainerFocusableChanges() {
         removeGlobalMinSizeRestriction();
-        final Task stack = new TaskBuilder(mSupervisor)
+        final Task rootTask = new TaskBuilder(mSupervisor)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
-        final Task task = stack.getTopMostTask();
+        final Task task = rootTask.getTopMostTask();
         WindowContainerTransaction t = new WindowContainerTransaction();
         assertTrue(task.isFocusable());
-        t.setFocusable(stack.mRemoteToken.toWindowContainerToken(), false);
+        t.setFocusable(rootTask.mRemoteToken.toWindowContainerToken(), false);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
         assertFalse(task.isFocusable());
-        t.setFocusable(stack.mRemoteToken.toWindowContainerToken(), true);
+        t.setFocusable(rootTask.mRemoteToken.toWindowContainerToken(), true);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
         assertTrue(task.isFocusable());
     }
@@ -407,25 +407,25 @@
     @Test
     public void testContainerHiddenChanges() {
         removeGlobalMinSizeRestriction();
-        final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true)
+        final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         WindowContainerTransaction t = new WindowContainerTransaction();
-        assertTrue(stack.shouldBeVisible(null));
-        t.setHidden(stack.mRemoteToken.toWindowContainerToken(), true);
+        assertTrue(rootTask.shouldBeVisible(null));
+        t.setHidden(rootTask.mRemoteToken.toWindowContainerToken(), true);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
-        assertFalse(stack.shouldBeVisible(null));
-        t.setHidden(stack.mRemoteToken.toWindowContainerToken(), false);
+        assertFalse(rootTask.shouldBeVisible(null));
+        t.setHidden(rootTask.mRemoteToken.toWindowContainerToken(), false);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
-        assertTrue(stack.shouldBeVisible(null));
+        assertTrue(rootTask.shouldBeVisible(null));
     }
 
     @Test
     public void testSetIgnoreOrientationRequest_taskDisplayArea() {
         removeGlobalMinSizeRestriction();
         final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
-        final Task stack = taskDisplayArea.createRootTask(
+        final Task rootTask = taskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build();
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build();
         taskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         mDisplayContent.setFocusedApp(activity);
         activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -461,9 +461,9 @@
     public void testSetIgnoreOrientationRequest_displayContent() {
         removeGlobalMinSizeRestriction();
         final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
-        final Task stack = taskDisplayArea.createRootTask(
+        final Task rootTask = taskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build();
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build();
         mDisplayContent.setFocusedApp(activity);
         activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
@@ -491,21 +491,21 @@
     @Test
     public void testOverrideConfigSize() {
         removeGlobalMinSizeRestriction();
-        final Task stack = new TaskBuilder(mSupervisor)
+        final Task rootTask = new TaskBuilder(mSupervisor)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
-        final Task task = stack.getTopMostTask();
+        final Task task = rootTask.getTopMostTask();
         WindowContainerTransaction t = new WindowContainerTransaction();
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
         final int origScreenWDp = task.getConfiguration().screenHeightDp;
         final int origScreenHDp = task.getConfiguration().screenHeightDp;
         t = new WindowContainerTransaction();
         // verify that setting config overrides on parent restricts children.
-        t.setScreenSizeDp(stack.mRemoteToken
+        t.setScreenSizeDp(rootTask.mRemoteToken
                 .toWindowContainerToken(), origScreenWDp, origScreenHDp / 2);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
         assertEquals(origScreenHDp / 2, task.getConfiguration().screenHeightDp);
         t = new WindowContainerTransaction();
-        t.setScreenSizeDp(stack.mRemoteToken.toWindowContainerToken(), SCREEN_WIDTH_DP_UNDEFINED,
+        t.setScreenSizeDp(rootTask.mRemoteToken.toWindowContainerToken(), SCREEN_WIDTH_DP_UNDEFINED,
                 SCREEN_HEIGHT_DP_UNDEFINED);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
         assertEquals(origScreenHDp, task.getConfiguration().screenHeightDp);
@@ -572,14 +572,14 @@
                 mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
         RunningTaskInfo info1 = task.getTaskInfo();
 
-        final Task stack = createTaskStackOnDisplay(
-                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
-        assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode());
+        final Task rootTask = createTask(
+                mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
+        assertEquals(mDisplayContent.getWindowingMode(), rootTask.getWindowingMode());
         WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.reparent(stack.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */);
+        wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertEquals(info1.configuration.windowConfiguration.getWindowingMode(),
-                stack.getWindowingMode());
+                rootTask.getWindowingMode());
 
         // Info should reflect new membership
         List<Task> infos = getTasksCreatedByOrganizer(mDisplayContent);
@@ -591,14 +591,14 @@
         Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask();
         Configuration c = new Configuration(task1.getRequestedOverrideConfiguration());
         c.windowConfiguration.setBounds(newSize);
-        doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any(), any());
+        doNothing().when(rootTask).adjustForMinimalTaskDimensions(any(), any(), any());
         task1.onRequestedOverrideConfigurationChanged(c);
-        assertEquals(newSize, stack.getBounds());
+        assertEquals(newSize, rootTask.getBounds());
 
         wct = new WindowContainerTransaction();
-        wct.reparent(stack.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
+        wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
-        assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode());
+        assertEquals(mDisplayContent.getWindowingMode(), rootTask.getWindowingMode());
         infos = getTasksCreatedByOrganizer(mDisplayContent);
         info1 = infos.get(0).getTaskInfo();
         assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
@@ -644,36 +644,39 @@
         lastReportedTiles.clear();
         called[0] = false;
 
-        final Task stack = createTaskStackOnDisplay(
-                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final Task rootTask = createTask(
+                mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
         Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask();
         WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.reparent(stack.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */);
+        wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertTrue(called[0]);
         assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
 
         lastReportedTiles.clear();
         called[0] = false;
-        final Task stack2 = createTaskStackOnDisplay(
-                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent);
+        final Task rootTask2 = createTask(
+                mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
         wct = new WindowContainerTransaction();
-        wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */);
+        wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(),
+                info1.token, true /* onTop */);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertTrue(called[0]);
         assertEquals(ACTIVITY_TYPE_HOME, lastReportedTiles.get(0).topActivityType);
 
         lastReportedTiles.clear();
         called[0] = false;
-        task1.positionChildAt(POSITION_TOP, stack, false /* includingParents */);
+        task1.positionChildAt(POSITION_TOP, rootTask, false /* includingParents */);
         assertTrue(called[0]);
         assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
 
         lastReportedTiles.clear();
         called[0] = false;
         wct = new WindowContainerTransaction();
-        wct.reparent(stack.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
-        wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
+        wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(),
+                null, true /* onTop */);
+        wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(),
+                null, true /* onTop */);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertTrue(called[0]);
         assertEquals(ACTIVITY_TYPE_UNDEFINED, lastReportedTiles.get(0).topActivityType);
@@ -723,10 +726,10 @@
         final int initialRootTaskCount = mWm.mAtmService.mTaskOrganizerController.getRootTasks(
                 mDisplayContent.mDisplayId, null /* activityTypes */).size();
 
-        final Task stack = createTaskStackOnDisplay(
-                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
-        final Task stack2 = createTaskStackOnDisplay(
-                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent);
+        final Task rootTask = createTask(
+                mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
+        final Task rootTask2 = createTask(
+                mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
 
         // Check getRootTasks works
         List<RunningTaskInfo> roots = mWm.mAtmService.mTaskOrganizerController.getRootTasks(
@@ -735,8 +738,10 @@
 
         lastReportedTiles.clear();
         WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.reparent(stack.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */);
-        wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), info2.token, true /* onTop */);
+        wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(),
+                info1.token, true /* onTop */);
+        wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(),
+                info2.token, true /* onTop */);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertFalse(lastReportedTiles.isEmpty());
         assertEquals(ACTIVITY_TYPE_STANDARD,
@@ -746,7 +751,8 @@
 
         lastReportedTiles.clear();
         wct = new WindowContainerTransaction();
-        wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), info1.token, false /* onTop */);
+        wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(),
+                info1.token, false /* onTop */);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertFalse(lastReportedTiles.isEmpty());
         // Standard should still be on top of tile 1, so no change there
@@ -771,7 +777,7 @@
 
         lastReportedTiles.clear();
         wct = new WindowContainerTransaction();
-        wct.reorder(stack2.mRemoteToken.toWindowContainerToken(), true /* onTop */);
+        wct.reorder(rootTask2.mRemoteToken.toWindowContainerToken(), true /* onTop */);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         // Home should now be on top. No change occurs in second tile, so not reported
         assertEquals(1, lastReportedTiles.size());
@@ -780,10 +786,10 @@
 
         // This just needs to not crash (ie. it should be possible to reparent to display twice)
         wct = new WindowContainerTransaction();
-        wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
+        wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         wct = new WindowContainerTransaction();
-        wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
+        wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
     }
 
@@ -799,8 +805,8 @@
 
     @Test
     public void testBLASTCallbackWithActivityChildren() {
-        final Task stackController1 = createStack();
-        final Task task = createTask(stackController1);
+        final Task rootTaskController1 = createRootTask();
+        final Task task = createTask(rootTaskController1);
         final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
 
         w.mActivityRecord.mVisibleRequested = true;
@@ -926,11 +932,11 @@
         ChangeSavingOrganizer o = new ChangeSavingOrganizer();
         mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o);
 
-        final Task stack = createStack();
-        final Task task = createTask(stack);
-        final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+        final Task rootTask = createRootTask();
+        final Task task = createTask(rootTask);
+        final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task);
 
-        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
         waitUntilHandlersIdle();
         assertEquals("TestDescription", o.mChangedInfo.taskDescription.getLabel());
@@ -939,29 +945,29 @@
     @Test
     public void testPreventDuplicateAppear() throws RemoteException {
         final ITaskOrganizer organizer = registerMockOrganizer();
-        final Task stack = createStack();
-        final Task task = createTask(stack, false /* fakeDraw */);
+        final Task rootTask = createRootTask();
+        final Task task = createTask(rootTask, false /* fakeDraw */);
 
-        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        stack.setTaskOrganizer(organizer);
+        rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        rootTask.setTaskOrganizer(organizer);
         // setHasBeenVisible was already called once by the set-up code.
-        stack.setHasBeenVisible(true);
+        rootTask.setHasBeenVisible(true);
         // Ensure events dispatch to organizer.
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
         verify(organizer, times(1))
                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
 
-        stack.setTaskOrganizer(null);
+        rootTask.setTaskOrganizer(null);
         // Ensure events dispatch to organizer.
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
         verify(organizer, times(1)).onTaskVanished(any());
-        stack.setTaskOrganizer(organizer);
+        rootTask.setTaskOrganizer(organizer);
         // Ensure events dispatch to organizer.
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
         verify(organizer, times(2))
                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
 
-        stack.removeImmediately();
+        rootTask.removeImmediately();
         // Ensure events dispatch to organizer.
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
         verify(organizer, times(2)).onTaskVanished(any());
@@ -970,15 +976,15 @@
     @Test
     public void testInterceptBackPressedOnTaskRoot() throws RemoteException {
         final ITaskOrganizer organizer = registerMockOrganizer();
-        final Task stack = createStack();
-        final Task task = createTask(stack);
-        final ActivityRecord activity = createActivityRecord(stack.mDisplayContent, task);
-        final Task stack2 = createStack();
-        final Task task2 = createTask(stack2);
-        final ActivityRecord activity2 = createActivityRecord(stack.mDisplayContent, task2);
+        final Task rootTask = createRootTask();
+        final Task task = createTask(rootTask);
+        final ActivityRecord activity = createActivityRecord(rootTask.mDisplayContent, task);
+        final Task rootTask2 = createRootTask();
+        final Task task2 = createTask(rootTask2);
+        final ActivityRecord activity2 = createActivityRecord(rootTask.mDisplayContent, task2);
 
-        assertTrue(stack.isOrganized());
-        assertTrue(stack2.isOrganized());
+        assertTrue(rootTask.isOrganized());
+        assertTrue(rootTask2.isOrganized());
 
         // Verify a back pressed does not call the organizer
         mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token,
@@ -989,7 +995,7 @@
 
         // Enable intercepting back
         mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot(
-                stack.mRemoteToken.toWindowContainerToken(), true);
+                rootTask.mRemoteToken.toWindowContainerToken(), true);
 
         // Verify now that the back press does call the organizer
         mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token,
@@ -1000,7 +1006,7 @@
 
         // Disable intercepting back
         mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot(
-                stack.mRemoteToken.toWindowContainerToken(), false);
+                rootTask.mRemoteToken.toWindowContainerToken(), false);
 
         // Verify now that the back press no longer calls the organizer
         mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token,
@@ -1012,8 +1018,8 @@
 
     @Test
     public void testBLASTCallbackWithWindows() throws Exception {
-        final Task stackController = createStack();
-        final Task task = createTask(stackController);
+        final Task rootTaskController = createRootTask();
+        final Task task = createTask(rootTaskController);
         final WindowState w1 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 1");
         final WindowState w2 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 2");
         makeWindowVisible(w1);
@@ -1077,7 +1083,7 @@
         final ITaskOrganizer organizer = registerMockOrganizer();
         Task rootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask(
                 mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null);
-        final Task task1 = createStack();
+        final Task task1 = createRootTask();
         final Task task2 = createTask(rootTask, false /* fakeDraw */);
         WindowContainerTransaction wct = new WindowContainerTransaction();
         wct.reparent(task1.mRemoteToken.toWindowContainerToken(),
@@ -1090,19 +1096,19 @@
     @Test
     public void testAppearDeferThenInfoChange() {
         final ITaskOrganizer organizer = registerMockOrganizer();
-        final Task stack = createStack();
+        final Task rootTask = createRootTask();
 
         // Assume layout defer
         mWm.mWindowPlacerLocked.deferLayout();
 
-        final Task task = createTask(stack);
-        final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+        final Task task = createTask(rootTask);
+        final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task);
 
-        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
         waitUntilHandlersIdle();
 
-        ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+        ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask);
         assertEquals(1, pendingEvents.size());
         assertEquals(PendingTaskEvent.EVENT_APPEARED, pendingEvents.get(0).mEventType);
         assertEquals("TestDescription",
@@ -1112,35 +1118,35 @@
     @Test
     public void testAppearDeferThenVanish() {
         final ITaskOrganizer organizer = registerMockOrganizer();
-        final Task stack = createStack();
+        final Task rootTask = createRootTask();
 
         // Assume layout defer
         mWm.mWindowPlacerLocked.deferLayout();
 
-        final Task task = createTask(stack);
+        final Task task = createTask(rootTask);
 
-        stack.removeImmediately();
+        rootTask.removeImmediately();
         waitUntilHandlersIdle();
 
-        ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+        ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask);
         assertEquals(0, pendingEvents.size());
     }
 
     @Test
     public void testInfoChangeDeferMultiple() {
         final ITaskOrganizer organizer = registerMockOrganizer();
-        final Task stack = createStack();
-        final Task task = createTask(stack);
-        final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+        final Task rootTask = createRootTask();
+        final Task task = createTask(rootTask);
+        final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task);
 
         // Assume layout defer
         mWm.mWindowPlacerLocked.deferLayout();
 
-        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
         waitUntilHandlersIdle();
 
-        ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+        ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask);
         assertEquals(1, pendingEvents.size());
         assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType);
         assertEquals("TestDescription",
@@ -1149,7 +1155,7 @@
         record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription2"));
         waitUntilHandlersIdle();
 
-        pendingEvents = getTaskPendingEvent(stack);
+        pendingEvents = getTaskPendingEvent(rootTask);
         assertEquals(1, pendingEvents.size());
         assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType);
         assertEquals("TestDescription2",
@@ -1159,20 +1165,20 @@
     @Test
     public void testInfoChangDeferThenVanish() {
         final ITaskOrganizer organizer = registerMockOrganizer();
-        final Task stack = createStack();
-        final Task task = createTask(stack);
-        final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+        final Task rootTask = createRootTask();
+        final Task task = createTask(rootTask);
+        final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task);
 
         // Assume layout defer
         mWm.mWindowPlacerLocked.deferLayout();
 
-        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
 
-        stack.removeImmediately();
+        rootTask.removeImmediately();
         waitUntilHandlersIdle();
 
-        ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+        ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask);
         assertEquals(1, pendingEvents.size());
         assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
         assertEquals("TestDescription",
@@ -1182,18 +1188,18 @@
     @Test
     public void testVanishDeferThenInfoChange() {
         final ITaskOrganizer organizer = registerMockOrganizer();
-        final Task stack = createStack();
-        final Task task = createTask(stack);
-        final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+        final Task rootTask = createRootTask();
+        final Task task = createTask(rootTask);
+        final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task);
 
         // Assume layout defer
         mWm.mWindowPlacerLocked.deferLayout();
 
-        stack.removeImmediately();
-        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        rootTask.removeImmediately();
+        rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         waitUntilHandlersIdle();
 
-        ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+        ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask);
         assertEquals(1, pendingEvents.size());
         assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
     }
@@ -1201,19 +1207,19 @@
     @Test
     public void testVanishDeferThenBackOnRoot() {
         final ITaskOrganizer organizer = registerMockOrganizer();
-        final Task stack = createStack();
-        final Task task = createTask(stack);
-        final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+        final Task rootTask = createRootTask();
+        final Task task = createTask(rootTask);
+        final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task);
 
         // Assume layout defer
         mWm.mWindowPlacerLocked.deferLayout();
 
-        stack.removeImmediately();
+        rootTask.removeImmediately();
         mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(record.token,
                 new IRequestFinishCallback.Default());
         waitUntilHandlersIdle();
 
-        ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+        ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask);
         assertEquals(1, pendingEvents.size());
         assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
     }
@@ -1264,7 +1270,7 @@
     @Test
     public void testSizeCompatModeChangedOnFirstOrganizedTask() throws RemoteException {
         final ITaskOrganizer organizer = registerMockOrganizer();
-        final Task rootTask = createStack();
+        final Task rootTask = createRootTask();
         final Task task = createTask(rootTask);
         final ActivityRecord activity = createActivityRecord(rootTask.mDisplayContent, task);
         final ArgumentCaptor<RunningTaskInfo> infoCaptor =
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 5b5b1da..bfbe203 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -249,21 +249,22 @@
         assertFalse(appWindow.canBeImeTarget());
         assertFalse(imeWindow.canBeImeTarget());
 
-        // Simulate the window is in split screen primary stack and the current state is
-        // minimized and home stack is resizable, so that we should ignore input for the stack.
+        // Simulate the window is in split screen primary root task and the current state is
+        // minimized and home root task is resizable, so that we should ignore input for the
+        // root task.
         final DockedTaskDividerController controller =
                 mDisplayContent.getDockedDividerController();
-        final Task stack = createTaskStackOnDisplay(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
-                ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final Task rootTask = createTask(mDisplayContent,
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
         spyOn(appWindow);
         spyOn(controller);
-        spyOn(stack);
-        stack.setFocusable(false);
-        doReturn(stack).when(appWindow).getRootTask();
+        spyOn(rootTask);
+        rootTask.setFocusable(false);
+        doReturn(rootTask).when(appWindow).getRootTask();
 
         // Make sure canBeImeTarget is false due to shouldIgnoreInput is true;
         assertFalse(appWindow.canBeImeTarget());
-        assertTrue(stack.shouldIgnoreInput());
+        assertTrue(rootTask.shouldIgnoreInput());
     }
 
     @Test
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 b210dfb..e9e0c99 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -429,35 +429,55 @@
         return newTaskDisplayArea;
     }
 
-    /** Creates a {@link Task} and adds it to the specified {@link DisplayContent}. */
-    Task createTaskStackOnDisplay(DisplayContent dc) {
-        return createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc);
+    /**
+     *  Creates a {@link Task} with a simple {@link ActivityRecord} and adds to the given
+     *  {@link TaskDisplayArea}.
+     */
+    Task createTaskWithActivity(TaskDisplayArea taskDisplayArea,
+            int windowingMode, int activityType, boolean onTop, boolean twoLevelTask) {
+        return createTask(taskDisplayArea, windowingMode, activityType,
+                onTop, true /* createActivity */, twoLevelTask);
     }
 
-    Task createTaskStackOnDisplay(int windowingMode, int activityType, DisplayContent dc) {
-        return new TaskBuilder(dc.mAtmService.mTaskSupervisor)
-                .setDisplay(dc)
+    /** Creates a {@link Task} and adds to the given {@link DisplayContent}. */
+    Task createTask(DisplayContent dc) {
+        return createTask(dc.getDefaultTaskDisplayArea(),
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+    }
+
+    Task createTask(DisplayContent dc, int windowingMode, int activityType) {
+        return createTask(dc.getDefaultTaskDisplayArea(), windowingMode, activityType);
+    }
+
+    Task createTask(TaskDisplayArea taskDisplayArea, int windowingMode, int activityType) {
+        return createTask(taskDisplayArea, windowingMode, activityType,
+                true /* onTop */, false /* createActivity */, false /* twoLevelTask */);
+    }
+
+    /** Creates a {@link Task} and adds to the given {@link TaskDisplayArea}. */
+    Task createTask(TaskDisplayArea taskDisplayArea, int windowingMode, int activityType,
+            boolean onTop, boolean createActivity, boolean twoLevelTask) {
+        final TaskBuilder builder = new TaskBuilder(mSupervisor)
+                .setTaskDisplayArea(taskDisplayArea)
                 .setWindowingMode(windowingMode)
                 .setActivityType(activityType)
-                .setIntent(new Intent())
-                .build();
+                .setOnTop(onTop)
+                .setCreateActivity(createActivity);
+        if (twoLevelTask) {
+            return builder
+                    .setCreateParentTask(true)
+                    .build()
+                    .getRootTask();
+        } else {
+            return builder.build();
+        }
     }
 
-    Task createTaskStackOnTaskDisplayArea(int windowingMode, int activityType,
-            TaskDisplayArea tda) {
-        return new TaskBuilder(tda.mDisplayContent.mAtmService.mTaskSupervisor)
-                .setTaskDisplayArea(tda)
-                .setWindowingMode(windowingMode)
-                .setActivityType(activityType)
-                .setIntent(new Intent())
-                .build();
-    }
-
-    /** Creates a {@link Task} and adds it to the specified {@link Task}. */
-    Task createTaskInStack(Task stack, int userId) {
-        final Task task = new TaskBuilder(stack.mTaskSupervisor)
+    /** Creates a {@link Task} and adds to the given root {@link Task}. */
+    Task createTaskInRootTask(Task rootTask, int userId) {
+        final Task task = new TaskBuilder(rootTask.mTaskSupervisor)
                 .setUserId(userId)
-                .setParentTask(stack)
+                .setParentTask(rootTask)
                 .build();
         return task;
     }
@@ -485,7 +505,7 @@
      */
     ActivityRecord createActivityRecord(DisplayContent dc, int windowingMode,
             int activityType) {
-        final Task task = createTaskStackOnDisplay(windowingMode, activityType, dc);
+        final Task task = createTask(dc, windowingMode, activityType);
         return createActivityRecord(dc, task);
     }
 
@@ -517,7 +537,7 @@
      */
     ActivityRecord createActivityRecordWithParentTask(DisplayContent dc, int windowingMode,
             int activityType) {
-        final Task task = createTaskStackOnDisplay(windowingMode, activityType, dc);
+        final Task task = createTask(dc, windowingMode, activityType);
         return createActivityRecordWithParentTask(task);
     }
 
@@ -871,7 +891,7 @@
                         .setParentTask(mParentTask).build();
             } else if (mTask == null && mParentTask != null && DisplayContent.alwaysCreateRootTask(
                     mParentTask.getWindowingMode(), mParentTask.getActivityType())) {
-                // The stack can be the task root.
+                // The parent task can be the task root.
                 mTask = mParentTask;
             }
 
@@ -921,9 +941,9 @@
                 doReturn(true).when(activity).fillsParent();
                 mTask.addChild(activity);
                 if (mOnTop) {
-                    // Move the task to front after activity added.
-                    // Or {@link TaskDisplayArea#mPreferredTopFocusableStack} could be other stacks
-                    // (e.g. home stack).
+                    // Move the task to front after activity is added.
+                    // Or {@link TaskDisplayArea#mPreferredTopFocusableRootTask} could be other
+                    // root tasks (e.g. home root task).
                     mTask.moveToFront("createActivity");
                 }
                 // Make visible by default...
@@ -1127,8 +1147,8 @@
                         .build();
                 if (mOnTop) {
                     // We move the task to front again in order to regain focus after activity
-                    // added to the stack. Or {@link TaskDisplayArea#mPreferredTopFocusableStack}
-                    // could be other stacks (e.g. home stack).
+                    // is added. Or {@link TaskDisplayArea#mPreferredTopFocusableRootTask} could be
+                    // other root tasks (e.g. home root task).
                     task.moveToFront("createActivityTask");
                 } else {
                     task.moveToBack("createActivityTask", null);
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index afa35fe..2e692e6 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -917,20 +917,31 @@
                     boolean prevHostConnected = mHostConnected;
                     UsbPort port = (UsbPort) args.arg1;
                     UsbPortStatus status = (UsbPortStatus) args.arg2;
-                    mHostConnected = status.getCurrentDataRole() == DATA_ROLE_HOST;
-                    mSourcePower = status.getCurrentPowerRole() == POWER_ROLE_SOURCE;
-                    mSinkPower = status.getCurrentPowerRole() == POWER_ROLE_SINK;
-                    mAudioAccessoryConnected = (status.getCurrentMode() == MODE_AUDIO_ACCESSORY);
+
+                    if (status != null) {
+                        mHostConnected = status.getCurrentDataRole() == DATA_ROLE_HOST;
+                        mSourcePower = status.getCurrentPowerRole() == POWER_ROLE_SOURCE;
+                        mSinkPower = status.getCurrentPowerRole() == POWER_ROLE_SINK;
+                        mAudioAccessoryConnected = (status.getCurrentMode() == MODE_AUDIO_ACCESSORY);
+
+                        // Ideally we want to see if PR_SWAP and DR_SWAP is supported.
+                        // But, this should be suffice, since, all four combinations are only supported
+                        // when PR_SWAP and DR_SWAP are supported.
+                        mSupportsAllCombinations = status.isRoleCombinationSupported(
+                                POWER_ROLE_SOURCE, DATA_ROLE_HOST)
+                                && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST)
+                                && status.isRoleCombinationSupported(POWER_ROLE_SOURCE,
+                                DATA_ROLE_DEVICE)
+                                && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_DEVICE);
+                    } else {
+                        mHostConnected = false;
+                        mSourcePower = false;
+                        mSinkPower = false;
+                        mAudioAccessoryConnected = false;
+                        mSupportsAllCombinations = false;
+                    }
+
                     mAudioAccessorySupported = port.isModeSupported(MODE_AUDIO_ACCESSORY);
-                    // Ideally we want to see if PR_SWAP and DR_SWAP is supported.
-                    // But, this should be suffice, since, all four combinations are only supported
-                    // when PR_SWAP and DR_SWAP are supported.
-                    mSupportsAllCombinations = status.isRoleCombinationSupported(
-                            POWER_ROLE_SOURCE, DATA_ROLE_HOST)
-                            && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST)
-                            && status.isRoleCombinationSupported(POWER_ROLE_SOURCE,
-                            DATA_ROLE_DEVICE)
-                            && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_DEVICE);
 
                     args.recycle();
                     updateUsbNotification(false);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 6541774..11ccfd8 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -96,10 +96,10 @@
                     mBound = connected;
                     if (connected) {
                         try {
-                            service.setConfig(options, sharedMemory);
+                            service.updateState(options, sharedMemory);
                         } catch (RemoteException e) {
                             // TODO: (b/181842909) Report an error to voice interactor
-                            Slog.w(TAG, "Failed to setConfig for HotwordDetectionService", e);
+                            Slog.w(TAG, "Failed to updateState for HotwordDetectionService", e);
                         }
                     }
                 }
@@ -129,9 +129,9 @@
         }
     }
 
-    void setConfigLocked(PersistableBundle options, SharedMemory sharedMemory) {
+    void updateStateLocked(PersistableBundle options, SharedMemory sharedMemory) {
         mRemoteHotwordDetectionService.run(
-                service -> service.setConfig(options, sharedMemory));
+                service -> service.updateState(options, sharedMemory));
     }
 
     private void detectFromDspSource(SoundTrigger.KeyphraseRecognitionEvent recognitionEvent,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 29354eb..c110b23 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -984,21 +984,19 @@
         }
 
         @Override
-        public void setHotwordDetectionServiceConfig(@Nullable PersistableBundle options,
+        public void updateState(@Nullable PersistableBundle options,
                 @Nullable SharedMemory sharedMemory) {
             enforceCallingPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION);
             synchronized (this) {
                 enforceIsCurrentVoiceInteractionService();
 
                 if (mImpl == null) {
-                    Slog.w(TAG,
-                            "setHotwordDetectionServiceConfig without running voice"
-                                    + " interaction service");
+                    Slog.w(TAG, "updateState without running voice interaction service");
                     return;
                 }
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    mImpl.setHotwordDetectionServiceConfigLocked(options, sharedMemory);
+                    mImpl.updateStateLocked(options, sharedMemory);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 0d4c302..0742cb4 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -401,10 +401,10 @@
         return mInfo.getSupportsLocalInteraction();
     }
 
-    public void setHotwordDetectionServiceConfigLocked(@Nullable PersistableBundle options,
+    public void updateStateLocked(@Nullable PersistableBundle options,
             @Nullable SharedMemory sharedMemory) {
         if (DEBUG) {
-            Slog.d(TAG, "setHotwordDetectionServiceConfigLocked");
+            Slog.d(TAG, "updateStateLocked");
         }
         if (mHotwordDetectionComponentName == null) {
             Slog.w(TAG, "Hotword detection service name not found");
@@ -426,7 +426,7 @@
                     mHotwordDetectionComponentName, mUser, /* bindInstantServiceAllowed= */ false,
                     options, sharedMemory);
         } else {
-            mHotwordDetectionConnection.setConfigLocked(options, sharedMemory);
+            mHotwordDetectionConnection.updateStateLocked(options, sharedMemory);
         }
     }
 
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 4e64838..d77ab2b 100755
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -687,7 +687,11 @@
         public static final int PROPERTY_IS_ADHOC_CONFERENCE = 0x00002000;
 
         /**
-         * Connection is using Cross SIM Calling.
+         * Connection is using cross sim technology.
+         * <p>
+         * Indicates that the {@link Connection} is using a cross sim technology which would
+         * register IMS over internet APN of default data subscription.
+         * <p>
          */
         public static final int PROPERTY_CROSS_SIM = 0x00004000;
 
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index a012498..3b28616 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -293,6 +293,14 @@
     }
 
     /**
+     * Return a copy of this PhysicalChannelConfig object but redact all the location info.
+     * @hide
+     */
+    public PhysicalChannelConfig createLocationInfoSanitizedCopy() {
+        return new Builder(this).setPhysicalCellId(PHYSICAL_CELL_ID_UNKNOWN).build();
+    }
+
+    /**
      * @return String representation of the connection status
      * @hide
      */
@@ -541,6 +549,23 @@
             mBand = BAND_UNKNOWN;
         }
 
+        /**
+         * Builder object constructed from existing PhysicalChannelConfig object.
+         * @hide
+         */
+        public Builder(PhysicalChannelConfig config) {
+            mNetworkType = config.getNetworkType();
+            mFrequencyRange = config.getFrequencyRange();
+            mDownlinkChannelNumber = config.getDownlinkChannelNumber();
+            mUplinkChannelNumber = config.getUplinkChannelNumber();
+            mCellBandwidthDownlinkKhz = config.getCellBandwidthDownlinkKhz();
+            mCellBandwidthUplinkKhz = config.getCellBandwidthUplinkKhz();
+            mCellConnectionStatus = config.getConnectionStatus();
+            mContextIds = Arrays.copyOf(config.getContextIds(), config.getContextIds().length);
+            mPhysicalCellId = config.getPhysicalCellId();
+            mBand = config.getBand();
+        }
+
         public PhysicalChannelConfig build() {
             return new PhysicalChannelConfig(this);
         }
diff --git a/telephony/java/android/telephony/data/NrQos.java b/telephony/java/android/telephony/data/NrQos.java
index 2011eed..fe124ac 100644
--- a/telephony/java/android/telephony/data/NrQos.java
+++ b/telephony/java/android/telephony/data/NrQos.java
@@ -50,6 +50,18 @@
         return new NrQos(in);
     }
 
+    public int get5Qi() {
+        return fiveQi;
+    }
+
+    public int getQfi() {
+        return qosFlowId;
+    }
+
+    public int getAveragingWindow() {
+        return averagingWindowMs;
+    }
+
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(Qos.QOS_TYPE_NR, dest, flags);
diff --git a/telephony/java/android/telephony/data/NrQosSessionAttributes.aidl b/telephony/java/android/telephony/data/NrQosSessionAttributes.aidl
new file mode 100644
index 0000000..fd3bbb0
--- /dev/null
+++ b/telephony/java/android/telephony/data/NrQosSessionAttributes.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package android.telephony.data;
+
+ parcelable NrQosSessionAttributes;
diff --git a/telephony/java/android/telephony/data/NrQosSessionAttributes.java b/telephony/java/android/telephony/data/NrQosSessionAttributes.java
new file mode 100644
index 0000000..857ccb9
--- /dev/null
+++ b/telephony/java/android/telephony/data/NrQosSessionAttributes.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.net.QosSessionAttributes;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Provides Qos attributes of an NR bearer.
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class NrQosSessionAttributes implements Parcelable, QosSessionAttributes {
+    private static final String TAG = NrQosSessionAttributes.class.getSimpleName();
+    private final int m5Qi;
+    private final int mQfi;
+    private final long mMaxUplinkBitRate;
+    private final long mMaxDownlinkBitRate;
+    private final long mGuaranteedUplinkBitRate;
+    private final long mGuaranteedDownlinkBitRate;
+    private final long mAveragingWindow;
+    @NonNull private final List<InetSocketAddress> mRemoteAddresses;
+
+    /**
+     * 5G QOS Identifier (5QI), see 3GPP TS 24.501 and 23.501.
+     * The allowed values are standard values(1-9, 65-68, 69-70, 75, 79-80, 82-85)
+     * defined in the spec and operator specific values in the range 128-254.
+     *
+     * @return the 5QI of the QOS flow
+     */
+    public int get5Qi() {
+        return m5Qi;
+    }
+
+    /**
+     * QOS flow identifier of the QOS flow description in the
+     * range of 1 to 63. see 3GPP TS 24.501 and 23.501.
+     *
+     * @return the QOS flow identifier of the session
+     */
+    public int getQfi() {
+        return mQfi;
+    }
+
+    /**
+     * Minimum bit rate in kbps that is guaranteed to be provided by the network on the uplink.
+     *
+     * see 3GPP TS 24.501 section 6.2.5
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the guaranteed bit rate in kbps
+     */
+    public long getGuaranteedUplinkBitRate() {
+        return mGuaranteedUplinkBitRate;
+    }
+
+    /**
+     * Minimum bit rate in kbps that is guaranteed to be provided by the network on the downlink.
+     *
+     * see 3GPP TS 24.501 section 6.2.5
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the guaranteed bit rate in kbps
+     */
+    public long getGuaranteedDownlinkBitRate() {
+        return mGuaranteedDownlinkBitRate;
+    }
+
+    /**
+     * The maximum uplink kbps that the network will accept.
+     *
+     * see 3GPP TS 24.501 section 6.2.5
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the max uplink bit rate in kbps
+     */
+    public long getMaxUplinkBitRate() {
+        return mMaxUplinkBitRate;
+    }
+
+    /**
+     * The maximum downlink kbps that the network can provide.
+     *
+     * see 3GPP TS 24.501 section 6.2.5
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the max downlink bit rate in kbps
+     */
+    public long getMaxDownlinkBitRate() {
+        return mMaxDownlinkBitRate;
+    }
+
+    /**
+     * The duration in milliseconds over which the maximum bit rates and guaranteed bit rates
+     * are calculated
+     *
+     * see 3GPP TS 24.501 section 6.2.5
+     *
+     * @return the averaging window duration in milliseconds
+     */
+    public long getAveragingWindow() {
+        return mAveragingWindow;
+    }
+
+    /**
+     * List of remote addresses associated with the Qos Session.  The given uplink bit rates apply
+     * to this given list of remote addresses.
+     *
+     * Note: In the event that the list is empty, it is assumed that the uplink bit rates apply to
+     * all remote addresses that are not contained in a different set of attributes.
+     *
+     * @return list of remote socket addresses that the attributes apply to
+     */
+    @NonNull
+    public List<InetSocketAddress> getRemoteAddresses() {
+        return mRemoteAddresses;
+    }
+
+    /**
+     * ..ctor for attributes
+     *
+     * @param fiveQi 5G quality class indicator
+     * @param qfi QOS flow identifier
+     * @param maxDownlinkBitRate the max downlink bit rate in kbps
+     * @param maxUplinkBitRate the max uplink bit rate in kbps
+     * @param guaranteedDownlinkBitRate the guaranteed downlink bit rate in kbps
+     * @param guaranteedUplinkBitRate the guaranteed uplink bit rate in kbps
+     * @param averagingWindow the averaging window duration in milliseconds
+     * @param remoteAddresses the remote addresses that the uplink bit rates apply to
+     *
+     * @hide
+     */
+    public NrQosSessionAttributes(final int fiveQi, final int qfi,
+            final long maxDownlinkBitRate, final long maxUplinkBitRate,
+            final long guaranteedDownlinkBitRate, final long guaranteedUplinkBitRate,
+            final long averagingWindow, @NonNull final List<InetSocketAddress> remoteAddresses) {
+        Objects.requireNonNull(remoteAddresses, "remoteAddress must be non-null");
+        m5Qi = fiveQi;
+        mQfi = qfi;
+        mMaxDownlinkBitRate = maxDownlinkBitRate;
+        mMaxUplinkBitRate = maxUplinkBitRate;
+        mGuaranteedDownlinkBitRate = guaranteedDownlinkBitRate;
+        mGuaranteedUplinkBitRate = guaranteedUplinkBitRate;
+        mAveragingWindow = averagingWindow;
+
+        final List<InetSocketAddress> remoteAddressesTemp = copySocketAddresses(remoteAddresses);
+        mRemoteAddresses = Collections.unmodifiableList(remoteAddressesTemp);
+    }
+
+    private static List<InetSocketAddress> copySocketAddresses(
+            @NonNull final List<InetSocketAddress> remoteAddresses) {
+        final List<InetSocketAddress> remoteAddressesTemp = new ArrayList<>();
+        for (final InetSocketAddress socketAddress : remoteAddresses) {
+            if (socketAddress != null && socketAddress.getAddress() != null) {
+                remoteAddressesTemp.add(socketAddress);
+            }
+        }
+        return remoteAddressesTemp;
+    }
+
+    private NrQosSessionAttributes(@NonNull final Parcel in) {
+        m5Qi = in.readInt();
+        mQfi = in.readInt();
+        mMaxDownlinkBitRate = in.readLong();
+        mMaxUplinkBitRate = in.readLong();
+        mGuaranteedDownlinkBitRate = in.readLong();
+        mGuaranteedUplinkBitRate = in.readLong();
+        mAveragingWindow = in.readLong();
+
+        final int size = in.readInt();
+        final List<InetSocketAddress> remoteAddresses = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
+            final byte[] addressBytes = in.createByteArray();
+            final int port = in.readInt();
+            try {
+                remoteAddresses.add(
+                        new InetSocketAddress(InetAddress.getByAddress(addressBytes), port));
+            } catch (final UnknownHostException e) {
+                // Impossible case since its filtered out the null values in the ..ctor
+                Log.e(TAG, "unable to unparcel remote address at index: " + i, e);
+            }
+        }
+        mRemoteAddresses = Collections.unmodifiableList(remoteAddresses);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull final Parcel dest, final int flags) {
+        dest.writeInt(m5Qi);
+        dest.writeInt(mQfi);
+        dest.writeLong(mMaxDownlinkBitRate);
+        dest.writeLong(mMaxUplinkBitRate);
+        dest.writeLong(mGuaranteedDownlinkBitRate);
+        dest.writeLong(mGuaranteedUplinkBitRate);
+        dest.writeLong(mAveragingWindow);
+
+        final int size = mRemoteAddresses.size();
+        dest.writeInt(size);
+        for (int i = 0; i < size; i++) {
+            final InetSocketAddress address = mRemoteAddresses.get(i);
+            dest.writeByteArray(address.getAddress().getAddress());
+            dest.writeInt(address.getPort());
+        }
+    }
+
+    @NonNull
+    public static final Creator<NrQosSessionAttributes> CREATOR =
+            new Creator<NrQosSessionAttributes>() {
+        @NonNull
+        @Override
+        public NrQosSessionAttributes createFromParcel(@NonNull final Parcel in) {
+            return new NrQosSessionAttributes(in);
+        }
+
+        @NonNull
+        @Override
+        public NrQosSessionAttributes[] newArray(final int size) {
+            return new NrQosSessionAttributes[size];
+        }
+    };
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index b25bc99..dfb229d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -136,7 +136,7 @@
     @Test
     fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
 
-    @Presubmit
+    @FlakyTest
     @Test
     fun visibleLayersShownMoreThanOneConsecutiveEntry() {
         testSpec.assertLayers {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index 95b1d3c..be0357e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -153,7 +153,7 @@
         testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation)
     }
 
-    @Presubmit
+    @FlakyTest
     @Test
     fun visibleLayersShownMoreThanOneConsecutiveEntry() {
         testSpec.assertLayers {
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 a9888b1..a3d1395 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
@@ -16,6 +16,7 @@
 
 package com.android.server.wm.flicker.launch
 
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -24,6 +25,7 @@
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import org.junit.FixMethodOrder
+import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -56,6 +58,12 @@
             }
         }
 
+    @FlakyTest
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
+    }
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index a19a95d..62b9b81 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -80,6 +80,24 @@
         super.navBarLayerRotatesAndScales()
     }
 
+    @FlakyTest
+    @Test
+    override fun statusBarLayerRotatesScales() {
+        super.statusBarLayerRotatesScales()
+    }
+
+    @FlakyTest
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
+    }
+
+    @FlakyTest
+    @Test
+    override fun focusChanges() {
+        super.focusChanges()
+    }
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index dcc64c9..38af8a7d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.server.wm.flicker.launch
 
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -24,6 +25,7 @@
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import org.junit.FixMethodOrder
+import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -61,6 +63,18 @@
             }
         }
 
+    @FlakyTest
+    @Test
+    override fun focusChanges() {
+        super.focusChanges()
+    }
+
+    @FlakyTest
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
+    }
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 6d2a923..f037f08 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -18,6 +18,7 @@
 
 import android.app.Instrumentation
 import android.platform.test.annotations.Presubmit
+import androidx.test.filters.FlakyTest
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -79,7 +80,7 @@
         testSpec.navBarLayerIsAlwaysVisible()
     }
 
-    @Presubmit
+    @FlakyTest
     @Test
     open fun navBarLayerRotatesAndScales() {
         testSpec.navBarLayerRotatesAndScales(
@@ -98,14 +99,14 @@
         testSpec.statusBarLayerIsAlwaysVisible()
     }
 
-    @Presubmit
+    @FlakyTest
     @Test
     open fun statusBarLayerRotatesScales() {
         testSpec.statusBarLayerRotatesScales(
             testSpec.config.startRotation, testSpec.config.endRotation)
     }
 
-    @Presubmit
+    @FlakyTest
     @Test
     open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
         testSpec.assertLayers {
@@ -121,7 +122,7 @@
         }
     }
 
-    @Presubmit
+    @FlakyTest
     @Test
     open fun noUncoveredRegions() {
         testSpec.noUncoveredRegions(testSpec.config.startRotation,
@@ -134,7 +135,7 @@
         testSpec.focusDoesNotChange()
     }
 
-    @Presubmit
+    @FlakyTest
     @Test
     open fun appLayerRotates_StartingPos() {
         testSpec.assertLayersStart {
@@ -142,7 +143,7 @@
         }
     }
 
-    @Presubmit
+    @FlakyTest
     @Test
     open fun appLayerRotates_EndingPos() {
         testSpec.assertLayersEnd {
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index 14dddcbd..e039ef0 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -73,7 +73,7 @@
 import kotlin.test.fail
 
 const val SERVICE_BIND_TIMEOUT_MS = 5_000L
-const val TEST_TIMEOUT_MS = 1_000L
+const val TEST_TIMEOUT_MS = 10_000L
 
 /**
  * Test that exercises an instrumented version of ConnectivityService against an instrumented
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index fd37652..44298d4 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -265,6 +265,7 @@
 import android.system.Os;
 import android.telephony.TelephonyManager;
 import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.telephony.data.NrQosSessionAttributes;
 import android.test.mock.MockContentResolver;
 import android.text.TextUtils;
 import android.util.ArraySet;
@@ -9963,7 +9964,7 @@
                         && session.getSessionType() == QosSession.TYPE_EPS_BEARER), eq(attributes));
 
         mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent()
-                .sendQosSessionLost(qosCallbackId, sessionId);
+                .sendQosSessionLost(qosCallbackId, sessionId, QosSession.TYPE_EPS_BEARER);
         waitForIdle();
         verify(mQosCallbackMockHelper.mCallback).onQosSessionLost(argThat(session ->
                 session.getSessionId() == sessionId
@@ -9971,6 +9972,36 @@
     }
 
     @Test
+    public void testNrQosCallbackAvailableAndLost() throws Exception {
+        mQosCallbackMockHelper = new QosCallbackMockHelper();
+        final int sessionId = 10;
+        final int qosCallbackId = 1;
+
+        when(mQosCallbackMockHelper.mFilter.validate())
+                .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+        mQosCallbackMockHelper.registerQosCallback(
+                mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
+        waitForIdle();
+
+        final NrQosSessionAttributes attributes = new NrQosSessionAttributes(
+                1, 2, 3, 4, 5, 6, 7, new ArrayList<>());
+        mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent()
+                .sendQosSessionAvailable(qosCallbackId, sessionId, attributes);
+        waitForIdle();
+
+        verify(mQosCallbackMockHelper.mCallback).onNrQosSessionAvailable(argThat(session ->
+                session.getSessionId() == sessionId
+                        && session.getSessionType() == QosSession.TYPE_NR_BEARER), eq(attributes));
+
+        mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent()
+                .sendQosSessionLost(qosCallbackId, sessionId, QosSession.TYPE_NR_BEARER);
+        waitForIdle();
+        verify(mQosCallbackMockHelper.mCallback).onQosSessionLost(argThat(session ->
+                session.getSessionId() == sessionId
+                        && session.getSessionType() == QosSession.TYPE_NR_BEARER));
+    }
+
+    @Test
     public void testQosCallbackTooManyRequests() throws Exception {
         mQosCallbackMockHelper = new QosCallbackMockHelper();
 
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 97c65eb..8b072c4 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -83,10 +83,10 @@
 
     IpConnectivityMetrics mService;
     NetdEventListenerService mNetdListener;
-    final NetworkCapabilities mNcWifi = new NetworkCapabilities.Builder()
+    private static final NetworkCapabilities CAPABILITIES_WIFI = new NetworkCapabilities.Builder()
             .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
             .build();
-    final NetworkCapabilities mNcCell = new NetworkCapabilities.Builder()
+    private static final NetworkCapabilities CAPABILITIES_CELL = new NetworkCapabilities.Builder()
             .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
             .build();
 
@@ -590,7 +590,7 @@
                 ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
         verify(mCm).registerNetworkCallback(any(), networkCallback.capture());
         networkCallback.getValue().onCapabilitiesChanged(new Network(netId),
-                netId == 100 ? mNcWifi : mNcCell);
+                netId == 100 ? CAPABILITIES_WIFI : CAPABILITIES_CELL);
     }
 
     void connectEvent(int netId, int error, int latencyMs, String ipAddr) throws Exception {
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index 52975e4..50aaaee 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -63,10 +63,10 @@
 
     NetdEventListenerService mService;
     ConnectivityManager mCm;
-    final NetworkCapabilities mNcWifi = new NetworkCapabilities.Builder()
+    private static final NetworkCapabilities CAPABILITIES_WIFI = new NetworkCapabilities.Builder()
             .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
             .build();
-    final NetworkCapabilities mNcCell = new NetworkCapabilities.Builder()
+    private static final NetworkCapabilities CAPABILITIES_CELL = new NetworkCapabilities.Builder()
             .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
             .build();
 
@@ -475,7 +475,7 @@
                 ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
         verify(mCm).registerNetworkCallback(any(), networkCallback.capture());
         networkCallback.getValue().onCapabilitiesChanged(new Network(netId),
-                netId == 100 ? mNcWifi : mNcCell);
+                netId == 100 ? CAPABILITIES_WIFI : CAPABILITIES_CELL);
     }
 
     Thread connectEventAction(int netId, int error, int latencyMs, String ipAddr) {
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index babea36..f15d420 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -49,7 +49,9 @@
 
 import android.annotation.NonNull;
 import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
 import android.net.ConnectivityManager;
 import android.net.LinkProperties;
 import android.net.NetworkCapabilities;
@@ -336,6 +338,13 @@
         return captor.getValue();
     }
 
+    private BroadcastReceiver getPackageChangeReceiver() {
+        final ArgumentCaptor<BroadcastReceiver> captor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        verify(mMockContext).registerReceiver(captor.capture(), any(), any(), any());
+        return captor.getValue();
+    }
+
     private Vcn startAndGetVcnInstance(ParcelUuid uuid) {
         mVcnMgmtSvc.setVcnConfig(uuid, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
         return mVcnMgmtSvc.getAllVcns().get(uuid);
@@ -412,6 +421,42 @@
     }
 
     @Test
+    public void testPackageChangeListenerRegistered() throws Exception {
+        verify(mMockContext).registerReceiver(any(BroadcastReceiver.class), argThat(filter -> {
+            return filter.hasAction(Intent.ACTION_PACKAGE_ADDED)
+                    && filter.hasAction(Intent.ACTION_PACKAGE_REPLACED)
+                    && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED);
+        }), any(), any());
+    }
+
+    @Test
+    public void testPackageChangeListener_packageAdded() throws Exception {
+        final BroadcastReceiver receiver = getPackageChangeReceiver();
+
+        verify(mMockContext).registerReceiver(any(), argThat(filter -> {
+            return filter.hasAction(Intent.ACTION_PACKAGE_ADDED)
+                    && filter.hasAction(Intent.ACTION_PACKAGE_REPLACED)
+                    && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED);
+        }), any(), any());
+
+        receiver.onReceive(mMockContext, new Intent(Intent.ACTION_PACKAGE_ADDED));
+        verify(mSubscriptionTracker).handleSubscriptionsChanged();
+    }
+
+    @Test
+    public void testPackageChangeListener_packageRemoved() throws Exception {
+        final BroadcastReceiver receiver = getPackageChangeReceiver();
+
+        verify(mMockContext).registerReceiver(any(), argThat(filter -> {
+            return filter.hasAction(Intent.ACTION_PACKAGE_REMOVED)
+                    && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED);
+        }), any(), any());
+
+        receiver.onReceive(mMockContext, new Intent(Intent.ACTION_PACKAGE_REMOVED));
+        verify(mSubscriptionTracker).handleSubscriptionsChanged();
+    }
+
+    @Test
     public void testSetVcnConfigRequiresNonSystemServer() throws Exception {
         doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid();